diff --git a/CHANGELOG b/CHANGELOG
index 2af2056979d6fe3909903d2cddb3e81c4ec61fd2..bf1136afd03ef51f431cfa1de2445383d1e8025b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -92,6 +92,7 @@ v 8.12.0 (unreleased)
   - Refactor the triggers page and documentation !6217
   - Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
   - Use default clone protocol on "check out, review, and merge locally" help page URL
+  - API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
 
 v 8.11.5 (unreleased)
   - Optimize branch lookups and force a repository reload for Repository#find_branch
diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb
index a7af3cb83450b1e6d5fc23559345d7f4accee53f..e06d12cfce16ce5fe0784fb7566045a28341630e 100644
--- a/app/controllers/ci/lints_controller.rb
+++ b/app/controllers/ci/lints_controller.rb
@@ -7,19 +7,14 @@ module Ci
 
     def create
       @content = params[:content]
+      @error = Ci::GitlabCiYamlProcessor.validation_message(@content)
+      @status = @error.blank?
 
-      if @content.blank?
-        @status = false
-        @error = "Please provide content of .gitlab-ci.yml"
-      else
+      if @error.blank?
         @config_processor = Ci::GitlabCiYamlProcessor.new(@content)
         @stages = @config_processor.stages
         @builds = @config_processor.builds
-        @status = true
       end
-    rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
-      @error = e.message
-      @status = false
     rescue
       @error = 'Undefined error'
       @status = false
diff --git a/doc/api/README.md b/doc/api/README.md
index 96d94e08487a35be96d788cde79a5f9ab13e037f..e12070dc1cebb28e96992bff365c42f402c85fe4 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -41,8 +41,9 @@ following locations:
 - [Sidekiq metrics](sidekiq_metrics.md)
 - [System Hooks](system_hooks.md)
 - [Tags](tags.md)
-- [Users](users.md)
 - [Todos](todos.md)
+- [Users](users.md)
+- [Validate CI configuration](ci/lint.md)
 
 ### Internal CI API
 
diff --git a/doc/api/ci/lint.md b/doc/api/ci/lint.md
new file mode 100644
index 0000000000000000000000000000000000000000..0c96b3ee335778cb1d7c11fb14d86098fde4aec4
--- /dev/null
+++ b/doc/api/ci/lint.md
@@ -0,0 +1,49 @@
+# Validate the .gitlab-ci.yml
+
+> [Introduced][ce-5953] in GitLab 8.12.
+
+Checks if your .gitlab-ci.yml file is valid.
+
+```
+POST ci/lint
+```
+
+| Attribute  | Type    | Required | Description |
+| ---------- | ------- | -------- | -------- |
+| `content`  | string    | yes      | the .gitlab-ci.yaml content|
+
+```bash
+curl --header "Content-Type: application/json" https://gitlab.example.com/api/v3/ci/lint --data '{"content": "{ \"image\": \"ruby:2.1\", \"services\": [\"postgres\"], \"before_script\": [\"gem install bundler\", \"bundle install\", \"bundle exec rake db:create\"], \"variables\": {\"DB_NAME\": \"postgres\"}, \"types\": [\"test\", \"deploy\", \"notify\"], \"rspec\": { \"script\": \"rake spec\", \"tags\": [\"ruby\", \"postgres\"], \"only\": [\"branches\"]}}"}'
+```
+
+Be sure to copy paste the exact contents of `.gitlab-ci.yml` as YAML is very picky about indentation and spaces.
+
+Example responses:
+
+* Valid content:
+
+    ```json
+    {
+      "status": "valid",
+      "errors": []
+    }
+    ```
+
+* Invalid content:
+
+    ```json
+    {
+      "status": "invalid",
+      "errors": [
+        "variables config should be a hash of key value pairs"
+      ]
+    }
+    ```
+
+* Without the content attribute:
+
+    ```json
+    {
+      "error": "content is missing"
+    }
+    ```
diff --git a/lib/api/api.rb b/lib/api/api.rb
index e14464c1b0d05847a67c8c22cd1181fb6a9f9e6d..a08fb056049b8fb53b09c4b2101e0f26789caa96 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -45,6 +45,7 @@ module API
     mount ::API::Keys
     mount ::API::Labels
     mount ::API::LicenseTemplates
+    mount ::API::Lint
     mount ::API::Members
     mount ::API::MergeRequests
     mount ::API::Milestones
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ae43a4a32376bcee210bb07c72ed683352707090
--- /dev/null
+++ b/lib/api/lint.rb
@@ -0,0 +1,21 @@
+module API
+  class Lint < Grape::API
+    namespace :ci do
+      desc 'Validation of .gitlab-ci.yml content'
+      params do
+        requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
+      end
+      post '/lint' do
+        error = Ci::GitlabCiYamlProcessor.validation_message(params[:content])
+
+        status 200
+
+        if error.blank?
+          { status: 'valid', errors: [] }
+        else
+          { status: 'invalid', errors: [error] }
+        end
+      end
+    end
+  end
+end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 47efd5bd9f264eb3685d0f07e252bff49dedb8f5..e8c863493396cf93b69af8f0226f55da4595391f 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -78,6 +78,17 @@ module Ci
       }
     end
 
+    def self.validation_message(content)
+      return 'Please provide content of .gitlab-ci.yml' if content.blank?
+
+      begin
+        Ci::GitlabCiYamlProcessor.new(content)
+        nil
+      rescue ValidationError, Psych::SyntaxError => e
+        e.message
+      end
+    end
+
     private
 
     def initial_parsing
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index be51d942af7c140d00f5e5459e7fece59469b04c..af192664b3305e5745a7dcaefc0d8f77be82266c 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -1250,5 +1250,40 @@ EOT
         end
       end
     end
+
+    describe "#validation_message" do
+      context "when the YAML could not be parsed" do
+        it "returns an error about invalid configutaion" do
+          content = YAML.dump("invalid: yaml: test")
+
+          expect(GitlabCiYamlProcessor.validation_message(content))
+            .to eq "Invalid configuration format"
+        end
+      end
+
+      context "when the tags parameter is invalid" do
+        it "returns an error about invalid tags" do
+          content = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
+
+          expect(GitlabCiYamlProcessor.validation_message(content))
+            .to eq "jobs:rspec tags should be an array of strings"
+        end
+      end
+
+      context "when YAML content is empty" do
+        it "returns an error about missing content" do
+          expect(GitlabCiYamlProcessor.validation_message(''))
+            .to eq "Please provide content of .gitlab-ci.yml"
+        end
+      end
+
+      context "when the YAML is valid" do
+        it "does not return any errors" do
+          content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+
+          expect(GitlabCiYamlProcessor.validation_message(content)).to be_nil
+        end
+      end
+    end
   end
 end
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..391fc13a380f56ff2fd997f221685e1f166d4146
--- /dev/null
+++ b/spec/requests/api/lint_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe API::Lint, api: true do
+  include ApiHelpers
+
+  describe 'POST /ci/lint' do
+    context 'with valid .gitlab-ci.yaml content' do
+      let(:yaml_content) do
+        File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+      end
+
+      it 'passes validation' do
+        post api('/ci/lint'), { content: yaml_content }
+
+        expect(response).to have_http_status(200)
+        expect(json_response).to be_an Hash
+        expect(json_response['status']).to eq('valid')
+        expect(json_response['errors']).to eq([])
+      end
+    end
+
+    context 'with an invalid .gitlab_ci.yml' do
+      it 'responds with errors about invalid syntax' do
+        post api('/ci/lint'), { content: 'invalid content' }
+
+        expect(response).to have_http_status(200)
+        expect(json_response['status']).to eq('invalid')
+        expect(json_response['errors']).to eq(['Invalid configuration format'])
+      end
+
+      it "responds with errors about invalid configuration" do
+        post api('/ci/lint'), { content: '{ image: "ruby:2.1",  services: ["postgres"] }' }
+
+        expect(response).to have_http_status(200)
+        expect(json_response['status']).to eq('invalid')
+        expect(json_response['errors']).to eq(['jobs config should contain at least one visible job'])
+      end
+    end
+
+    context 'without the content parameter' do
+      it 'responds with validation error about missing content' do
+        post api('/ci/lint')
+
+        expect(response).to have_http_status(400)
+        expect(json_response['error']).to eq('content is missing')
+      end
+    end
+  end
+end