diff --git a/CHANGELOG b/CHANGELOG
index 50dc6b8945b149f4aad68267816c3c7aaf0195e9..c19826bc5c121a152514c719ca12a533c31d0c9d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,6 +11,7 @@ v 7.2.0
   - Fix bug when MR download patch return invalid diff
   - Test gitlab-shell integration
   - Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
+  - API for labels (Robert Schilling)
 
 v 7.1.1
   - Fix cpu usage issue in Firefox
diff --git a/app/models/label.rb b/app/models/label.rb
index c32efc7c47fe3377f05b7ae9b1130d7f392fbd88..819d6cefa4171274141e6a0ec984228cbde262a2 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -18,9 +18,7 @@ class Label < ActiveRecord::Base
 
   scope :order_by_name, -> { reorder("labels.title ASC") }
 
-  def name
-    title
-  end
+  alias_attribute :name, :title
 
   def open_issues_count
     issues.opened.count
diff --git a/app/models/project.rb b/app/models/project.rb
index a24eae7d26b59eed531cc087ff21f4cc6570c776..7f6aa6d42493dfd36b2ff9de4c99c72360126cea 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -577,4 +577,8 @@ def update_repository_size
   def forks_count
     ForkedProjectLink.where(forked_from_project_id: self.id).count
   end
+
+  def find_label(name)
+    labels.find_by(name: name)
+  end
 end
diff --git a/doc/api/README.md b/doc/api/README.md
index a0a9ba6f4b68c1ac7c9c9e353833660a672db49a..44e95ed82582101cc65ed55c985f465de0ed723e 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -12,6 +12,7 @@
 - [Branches](branches.md)
 - [Merge Requests](merge_requests.md)
 - [Issues](issues.md)
+- [Labels](labels.md)
 - [Milestones](milestones.md)
 - [Notes](notes.md) (comments)
 - [Deploy Keys](deploy_keys.md)
diff --git a/doc/api/labels.md b/doc/api/labels.md
new file mode 100644
index 0000000000000000000000000000000000000000..95fd4e84119b366452afbd8771d8fdab98818350
--- /dev/null
+++ b/doc/api/labels.md
@@ -0,0 +1,85 @@
+# Labels
+
+## List labels
+
+Get all labels for given project.
+
+```
+GET /projects/:id/labels
+```
+
+```json
+[
+    {
+        "name": "Awesome",
+        "color": "#DD10AA"
+    },
+    {
+        "name": "Documentation",
+        "color": "#1E80DD"
+    },
+    {
+        "name": "Feature",
+        "color": "#11FF22"
+    },
+    {
+        "name": "Bug",
+        "color": "#EE1122"
+    }
+]
+```
+
+## Create a new label
+
+Creates a new label for given repository with given name and color.
+
+```
+POST /projects/:id/labels
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the label
+- `color` (required) -  Color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)
+
+It returns 200 and the newly created label, if the operation succeeds.
+If the label already exists, 409 and an error message is returned.
+If label parameters are invalid, 405 and an explaining error message is returned.
+
+## Delete a label
+
+Deletes a label given by its name.
+
+```
+DELETE /projects/:id/labels
+```
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the label to be deleted
+
+It returns 200 if the label successfully was deleted, 404 for wrong parameters
+and 400 if the label does not exist.
+In case of an error, additionally an error message is returned.
+
+## Edit an existing label
+
+Updates an existing label with new name or now color. At least one parameter
+is required, to update the label.
+
+```
+PUT /projects/:id/labels
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the existing label
+- `new_name` (optional) - The new name of the label
+- `color` (optional) -  New color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)
+
+On success, this method returns 200 with the updated label.
+If required parameters are missing, 400 is returned.
+If the label to be updated is missing, 404 is returned.
+If parameters are invalid, 405 is returned. In case of an error,
+additionally an error message is returned.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b8876e8e104bd3ed707e7b4b2c2de23145f65ab7..894c2fd76a475ac9da3e6982a3a1e37311e4d967 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -632,29 +632,3 @@ Parameters:
 +   query (required) - A string contained in the project name
 +   per_page (optional) - number of projects to return per page
 +   page (optional) - the page to retrieve
-
-
-## Labels
-
-### List project labels
-
-Get a list of project labels.
-
-```
-GET /projects/:id/labels
-```
-
-Parameters:
-
-+ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
-
-```json
-[
-  {
-    "name": "feature"
-  },
-  {
-    "name": "bug"
-  }
-]
-```
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ce4cc8b34f7e22cc7fdf7ed2942befab4b71edf7..2c7cd9038c3504d33abe0e9a5104a45e5a5b73f7 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -46,5 +46,6 @@ class API < Grape::API
     mount Commits
     mount Namespaces
     mount Branches
+    mount Labels
   end
 end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index fc7d391fd30e94f02c56606ddc86fb0e1909b269..74fdef935433987fd48a8af7448c2476827aa897 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -194,7 +194,7 @@ class ProjectWithAccess < Project
     end
 
     class Label < Grape::Entity
-      expose :name
+      expose :name, :color
     end
 
     class RepoDiff < Grape::Entity
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c73a4dbe916b941588aec127c8b2b0694f10168a
--- /dev/null
+++ b/lib/api/labels.rb
@@ -0,0 +1,100 @@
+module API
+  # Labels API
+  class Labels < Grape::API
+    before { authenticate! }
+
+    resource :projects do
+      # Get all labels of the project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      # Example Request:
+      #   GET /projects/:id/labels
+      get ':id/labels' do
+        present user_project.labels, with: Entities::Label
+      end
+
+      # Creates a new label
+      #
+      # Parameters:
+      #   id    (required) - The ID of a project
+      #   name  (required) - The name of the label to be deleted
+      #   color (required) - Color of the label given in 6-digit hex
+      #                      notation with leading '#' sign (e.g. #FFAABB)
+      # Example Request:
+      #   POST /projects/:id/labels
+      post ':id/labels' do
+        required_attributes! [:name, :color]
+
+        attrs = attributes_for_keys [:name, :color]
+        label = user_project.find_label(attrs[:name])
+
+        if label
+          return render_api_error!('Label already exists', 409)
+        end
+
+        label = user_project.labels.create(attrs)
+
+        if label.valid?
+          present label, with: Entities::Label
+        else
+          render_api_error!(label.errors.full_messages.join(', '), 405)
+        end
+      end
+
+      # Deletes an existing label
+      #
+      # Parameters:
+      #   id    (required) - The ID of a project
+      #   name  (required) - The name of the label to be deleted
+      #
+      # Example Request:
+      #   DELETE /projects/:id/labels
+      delete ':id/labels' do
+        required_attributes! [:name]
+
+        label = user_project.find_label(params[:name])
+        if !label
+          return render_api_error!('Label not found', 404)
+        end
+
+        label.destroy
+      end
+
+      # Updates an existing label. At least one optional parameter is required.
+      #
+      # Parameters:
+      #   id    (required) - The ID of a project
+      #   name  (optional) - The name of the label to be deleted
+      #   color (optional) - Color of the label given in 6-digit hex
+      #                      notation with leading '#' sign (e.g. #FFAABB)
+      # Example Request:
+      #   PUT /projects/:id/labels
+      put ':id/labels' do
+        required_attributes! [:name]
+
+        label = user_project.find_label(params[:name])
+        if !label
+          return render_api_error!('Label not found', 404)
+        end
+
+        attrs = attributes_for_keys [:new_name, :color]
+
+        if attrs.empty?
+          return render_api_error!('Required parameters "name" or "color" ' \
+                                   'missing',
+                                   400)
+        end
+
+        # Rename new name to the actual label attribute name
+        attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name)
+
+        if label.update(attrs)
+          present label, with: Entities::Label
+        else
+          render_api_error!(label.errors.full_messages.join(', '), 405)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 149678e680307a8ea3a54697aa9f263809a31170..55f7975bbf776f80a3dd0280c9ce4a92441729d8 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -224,17 +224,6 @@ def map_public_to_visibility_level(attrs)
         @users = paginate @users
         present @users, with: Entities::UserBasic
       end
-
-      # Get a project labels
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   GET /projects/:id/labels
-      get ':id/labels' do
-        @labels = user_project.labels
-        present @labels, with: Entities::Label
-      end
     end
   end
 end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index d40c2c21cec339c9492547c06c313baa418ae939..b06b353333de065610f5b872a2167898546070de 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -21,4 +21,131 @@
       json_response.first['name'].should == label1.name
     end
   end
+
+  describe 'POST /projects/:id/labels' do
+    it 'should return created label' do
+      post api("/projects/#{project.id}/labels", user),
+           name: 'Foo',
+           color: '#FFAABB'
+      response.status.should == 201
+      json_response['name'].should == 'Foo'
+      json_response['color'].should == '#FFAABB'
+    end
+
+    it 'should return a 400 bad request if name not given' do
+      post api("/projects/#{project.id}/labels", user), color: '#FFAABB'
+      response.status.should == 400
+    end
+
+    it 'should return a 400 bad request if color not given' do
+      post api("/projects/#{project.id}/labels", user), name: 'Foobar'
+      response.status.should == 400
+    end
+
+    it 'should return 405 for invalid color' do
+      post api("/projects/#{project.id}/labels", user),
+           name: 'Foo',
+           color: '#FFAA'
+      response.status.should == 405
+      json_response['message'].should == 'Color is invalid'
+    end
+
+    it 'should return 405 for invalid name' do
+      post api("/projects/#{project.id}/labels", user),
+           name: '?',
+           color: '#FFAABB'
+      response.status.should == 405
+      json_response['message'].should == 'Title is invalid'
+    end
+
+    it 'should return 409 if label already exists' do
+      post api("/projects/#{project.id}/labels", user),
+           name: 'label1',
+           color: '#FFAABB'
+      response.status.should == 409
+      json_response['message'].should == 'Label already exists'
+    end
+  end
+
+  describe 'DELETE /projects/:id/labels' do
+    it 'should return 200 for existing label' do
+      delete api("/projects/#{project.id}/labels", user), name: 'label1'
+      response.status.should == 200
+    end
+
+    it 'should return 404 for non existing label' do
+      delete api("/projects/#{project.id}/labels", user), name: 'label2'
+      response.status.should == 404
+      json_response['message'].should == 'Label not found'
+    end
+
+    it 'should return 400 for wrong parameters' do
+      delete api("/projects/#{project.id}/labels", user)
+      response.status.should == 400
+    end
+  end
+
+  describe 'PUT /projects/:id/labels' do
+    it 'should return 200 if name and colors are changed' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label1',
+          new_name: 'New Label',
+          color: '#FFFFFF'
+      response.status.should == 200
+      json_response['name'].should == 'New Label'
+      json_response['color'].should == '#FFFFFF'
+    end
+
+    it 'should return 200 if name is changed' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label1',
+          new_name: 'New Label'
+      response.status.should == 200
+      json_response['name'].should == 'New Label'
+      json_response['color'].should == label1.color
+    end
+
+    it 'should return 200 if colors is changed' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label1',
+          color: '#FFFFFF'
+      response.status.should == 200
+      json_response['name'].should == label1.name
+      json_response['color'].should == '#FFFFFF'
+    end
+
+    it 'should return 404 if label does not exist' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label2',
+          new_name: 'label3'
+      response.status.should == 404
+    end
+
+    it 'should return 400 if no label name given' do
+      put api("/projects/#{project.id}/labels", user), new_name: 'label2'
+      response.status.should == 400
+    end
+
+    it 'should return 400 if no new parameters given' do
+      put api("/projects/#{project.id}/labels", user), name: 'label1'
+      response.status.should == 400
+    end
+
+    it 'should return 405 for invalid name' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label1',
+          new_name: '?',
+          color: '#FFFFFF'
+      response.status.should == 405
+      json_response['message'].should == 'Title is invalid'
+    end
+
+    it 'should return 405 for invalid name' do
+      put api("/projects/#{project.id}/labels", user),
+          name: 'label1',
+          color: '#FF'
+      response.status.should == 405
+      json_response['message'].should == 'Color is invalid'
+    end
+  end
 end