diff --git a/CHANGELOG b/CHANGELOG
index c1531cb2ff2757995fc4bb967fa5946ee75ef06b..0f2b97e7c2b516093732e6f38b7dd3b4e6559eb3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,6 +12,7 @@ v 7.3.0
   - Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
   - Keyboard shortcuts for productivity (Robert Schilling)
   - API: filter issues by state (Julien Bianchi)
+  - API: filter issues by labels (Julien Bianchi)
   - Add system hook for ssh key changes
 
 v 7.2.0
diff --git a/app/models/project.rb b/app/models/project.rb
index 5cc35f20cadf4de65251e3453f22b60f712ad192..2bd6a57ee287c8d2c0881b450e805663b0d762e3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -70,7 +70,7 @@ class Project < ActiveRecord::Base
   has_many :merge_requests,     dependent: :destroy, foreign_key: "target_project_id"
   # Merge requests from source project should be kept when source project was removed
   has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
-  has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
+  has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
   has_many :labels,             dependent: :destroy
   has_many :services,           dependent: :destroy
   has_many :events,             dependent: :destroy
diff --git a/doc/api/issues.md b/doc/api/issues.md
index c12d45285469419186a983c27c7110bfb8faa620..a935b146d37e5b5d08bbda94a021730be4c81230 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -9,11 +9,15 @@ Get all issues created by authenticated user. This function takes pagination par
 GET /issues
 GET /issues?state=opened
 GET /issues?state=closed
+GET /issues?labels=foo
+GET /issues?labels=foo,bar
+GET /issues?labels=foo,bar&state=opened
 ```
 
 Parameters:
 
 - `state` (optional) - Return `all` issues or just those that are `opened` or `closed`
+- `labels` (optional) - Comma-separated list of label names
 
 ```json
 [
@@ -88,12 +92,16 @@ to return the list of project issues.
 GET /projects/:id/issues
 GET /projects/:id/issues?state=opened
 GET /projects/:id/issues?state=closed
+GET /projects/:id/issues?labels=foo
+GET /projects/:id/issues?labels=foo,bar
+GET /projects/:id/issues?labels=foo,bar&state=opened
 ```
 
 Parameters:
 
 - `id` (required) - The ID of a project
 - `state` (optional) - Return `all` issues or just those that are `opened` or `closed`
+- `labels` (optional) - Comma-separated list of label names
 
 ## Single issue
 
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 15a49b452bd7689ac3a7ef4f074e3e94fab19e71..e4a66eceadde88b997e1011c402c653a2966f570 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -11,6 +11,10 @@ module API
         else issues.order('id DESC')
         end
       end
+
+      def filter_issues_labels(issues, labels)
+        issues.includes(:labels).where("labels.title" => labels.split(','))
+      end
     end
 
     resource :issues do
@@ -18,13 +22,21 @@ module API
       #
       # Parameters:
       #   state (optional) - Return "opened" or "closed" issues
-      #
+      #   labels (optional) - Comma-separated list of label names
+
       # Example Requests:
       #   GET /issues
       #   GET /issues?state=opened
       #   GET /issues?state=closed
+      #   GET /issues?labels=foo
+      #   GET /issues?labels=foo,bar
+      #   GET /issues?labels=foo,bar&state=opened
       get do
-        present paginate(filter_issues_state(current_user.issues, params['state'])), with: Entities::Issue
+        issues = current_user.issues
+        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
+        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
+
+        present paginate(issues), with: Entities::Issue
       end
     end
 
@@ -34,13 +46,22 @@ module API
       # Parameters:
       #   id (required) - The ID of a project
       #   state (optional) - Return "opened" or "closed" issues
+      #   labels (optional) - Comma-separated list of label names
       #
       # Example Requests:
       #   GET /projects/:id/issues
       #   GET /projects/:id/issues?state=opened
       #   GET /projects/:id/issues?state=closed
+      #   GET /projects/:id/issues
+      #   GET /projects/:id/issues?labels=foo
+      #   GET /projects/:id/issues?labels=foo,bar
+      #   GET /projects/:id/issues?labels=foo,bar&state=opened
       get ":id/issues" do
-        present paginate(filter_issues_state(user_project.issues, params['state'])), with: Entities::Issue
+        issues = user_project.issues
+        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
+        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
+
+        present paginate(issues), with: Entities::Issue
       end
 
       # Get a single project issue
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index f70b56b194f13fec954c301bedb4afbffbf70dc3..e8eebda95b40943b24fcd94e6821081561b128f3 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -9,6 +9,7 @@ describe API::API, api: true  do
   let!(:label) do
     create(:label, title: 'label', color: '#FFAABB', project: project)
   end
+  let!(:label_link) { create(:label_link, label: label, target: issue) }
 
   before { project.team << [user, :reporter] }
 
@@ -58,6 +59,45 @@ describe API::API, api: true  do
         json_response.first['id'].should == issue.id
         json_response.second['id'].should == closed_issue.id
       end
+
+      it 'should return an array of labeled issues' do
+        get api("/issues?labels=#{label.title}", user)
+        response.status.should == 200
+        json_response.should be_an Array
+        json_response.length.should == 1
+        json_response.first['labels'].should == [label.title]
+      end
+
+      it 'should return an array of labeled issues when at least one label matches' do
+        get api("/issues?labels=#{label.title},foo,bar", user)
+        response.status.should == 200
+        json_response.should be_an Array
+        json_response.length.should == 1
+        json_response.first['labels'].should == [label.title]
+      end
+
+      it 'should return an empty array if no issue matches labels' do
+        get api('/issues?labels=foo,bar', user)
+        response.status.should == 200
+        json_response.should be_an Array
+        json_response.length.should == 0
+      end
+
+      it 'should return an array of labeled issues matching given state' do
+        get api("/issues?labels=#{label.title}&state=opened", user)
+        response.status.should == 200
+        json_response.should be_an Array
+        json_response.length.should == 1
+        json_response.first['labels'].should == [label.title]
+        json_response.first['state'].should == 'opened'
+      end
+
+      it 'should return an empty array if no issue matches labels and state filters' do
+        get api("/issues?labels=#{label.title}&state=closed", user)
+        response.status.should == 200
+        json_response.should be_an Array
+        json_response.length.should == 0
+      end
     end
   end
 
@@ -68,6 +108,29 @@ describe API::API, api: true  do
       json_response.should be_an Array
       json_response.first['title'].should == issue.title
     end
+
+    it 'should return an array of labeled project issues' do
+      get api("/projects/#{project.id}/issues?labels=#{label.title}", user)
+      response.status.should == 200
+      json_response.should be_an Array
+      json_response.length.should == 1
+      json_response.first['labels'].should == [label.title]
+    end
+
+    it 'should return an array of labeled project issues when at least one label matches' do
+      get api("/projects/#{project.id}/issues?labels=#{label.title},foo,bar", user)
+      response.status.should == 200
+      json_response.should be_an Array
+      json_response.length.should == 1
+      json_response.first['labels'].should == [label.title]
+    end
+
+    it 'should return an empty array if no project issue matches labels' do
+      get api("/projects/#{project.id}/issues?labels=foo,bar", user)
+      response.status.should == 200
+      json_response.should be_an Array
+      json_response.length.should == 0
+    end
   end
 
   describe "GET /projects/:id/issues/:issue_id" do
@@ -182,7 +245,7 @@ describe API::API, api: true  do
         labels: 'label2', state_event: "close"
       response.status.should == 200
 
-      json_response['labels'].should == ['label2']
+      json_response['labels'].should include 'label2'
       json_response['state'].should eq "closed"
     end
   end