diff --git a/lib/error-helper.js b/lib/error-helper.js
index 71aee42843607a351abb22e49cb97dc801084952..9e7fee8a3a76374eb769341d99b67d3343e704ab 100644
--- a/lib/error-helper.js
+++ b/lib/error-helper.js
@@ -5,19 +5,18 @@ const {
   InvalidResponse,
   Inaccessible,
 } = require('../services/errors')
+const defaultErrorMessages = {
+  404: 'not found',
+}
 
-const checkErrorResponse = function(
-  badgeData,
-  err,
-  res,
-  notFoundMessage = 'not found'
-) {
+const checkErrorResponse = function(badgeData, err, res, errorMessages = {}) {
+  errorMessages = { ...defaultErrorMessages, ...errorMessages }
   if (err != null) {
     badgeData.text[1] = 'inaccessible'
     badgeData.colorscheme = 'red'
     return true
-  } else if (res.statusCode === 404) {
-    badgeData.text[1] = notFoundMessage
+  } else if (errorMessages[res.statusCode] !== undefined) {
+    badgeData.text[1] = errorMessages[res.statusCode]
     badgeData.colorscheme = 'lightgrey'
     return true
   } else if (res.statusCode !== 200) {
@@ -29,17 +28,23 @@ const checkErrorResponse = function(
   }
 }
 
-checkErrorResponse.asPromise = function({ notFoundMessage } = {}) {
+checkErrorResponse.asPromise = function(errorMessages = {}) {
   return async function({ buffer, res }) {
-    const underlyingError = Error(
-      `Got status code ${res.statusCode} (expected 200)`
-    )
+    errorMessages = { ...defaultErrorMessages, ...errorMessages }
     if (res.statusCode === 404) {
-      throw new NotFound({ prettyMessage: notFoundMessage })
-    } else if (res.statusCode >= 500) {
-      throw new Inaccessible({ underlyingError })
+      throw new NotFound({ prettyMessage: errorMessages[404] })
     } else if (res.statusCode !== 200) {
-      throw new InvalidResponse({ underlyingError })
+      const underlying = Error(
+        `Got status code ${res.statusCode} (expected 200)`
+      )
+      const props = { underlyingError: underlying }
+      if (errorMessages[res.statusCode] !== undefined) {
+        props.prettyMessage = errorMessages[res.statusCode]
+      }
+      if (res.statusCode >= 500) {
+        throw new Inaccessible(props)
+      }
+      throw new InvalidResponse(props)
     }
     return { buffer, res }
   }
diff --git a/lib/error-helper.spec.js b/lib/error-helper.spec.js
index 92f33cef8306202fe6cc2ff81e76206d74cb2d08..d9b91c4d8c0b6819e38e190b856d0b4984e8aa26 100644
--- a/lib/error-helper.spec.js
+++ b/lib/error-helper.spec.js
@@ -1,7 +1,6 @@
 'use strict'
 
-const chai = require('chai')
-const { assert, expect } = chai
+const { expect } = require('chai')
 const { checkErrorResponse } = require('./error-helper')
 const {
   NotFound,
@@ -9,48 +8,46 @@ const {
   Inaccessible,
 } = require('../services/errors')
 
-chai.use(require('chai-as-promised'))
-
 describe('Standard Error Handler', function() {
   it('makes inaccessible badge', function() {
     const badgeData = { text: [] }
-    assert.equal(
-      true,
-      checkErrorResponse(badgeData, 'something other than null', {})
-    )
-    assert.equal('inaccessible', badgeData.text[1])
-    assert.equal('red', badgeData.colorscheme)
+    expect(checkErrorResponse(badgeData, 'something other than null', {})).to.be
+      .true
+    expect(badgeData.text[1]).to.equal('inaccessible')
+    expect(badgeData.colorscheme).to.equal('red')
   })
 
   it('makes not found badge', function() {
     const badgeData = { text: [] }
-    assert.equal(true, checkErrorResponse(badgeData, null, { statusCode: 404 }))
-    assert.equal('not found', badgeData.text[1])
-    assert.equal('lightgrey', badgeData.colorscheme)
+    expect(checkErrorResponse(badgeData, null, { statusCode: 404 })).to.be.true
+    expect(badgeData.text[1]).to.equal('not found')
+    expect(badgeData.colorscheme).to.equal('lightgrey')
   })
 
   it('makes not found badge with custom error', function() {
     const badgeData = { text: [] }
-    assert.equal(
-      true,
-      checkErrorResponse(badgeData, null, { statusCode: 404 }, 'custom message')
-    )
-    assert.equal('custom message', badgeData.text[1])
-    assert.equal('lightgrey', badgeData.colorscheme)
+    expect(
+      checkErrorResponse(
+        badgeData,
+        null,
+        { statusCode: 404 },
+        { 404: 'custom message' }
+      )
+    ).to.be.true
+    expect(badgeData.text[1]).to.equal('custom message')
+    expect(badgeData.colorscheme).to.equal('lightgrey')
   })
 
   it('makes invalid badge', function() {
     const badgeData = { text: [] }
-    assert.equal(true, checkErrorResponse(badgeData, null, { statusCode: 500 }))
-    assert.equal('invalid', badgeData.text[1])
-    assert.equal('lightgrey', badgeData.colorscheme)
+    expect(checkErrorResponse(badgeData, null, { statusCode: 500 })).to.be.true
+    expect(badgeData.text[1]).to.equal('invalid')
+    expect(badgeData.colorscheme).to.equal('lightgrey')
   })
 
   it('return false on 200 status', function() {
-    assert.equal(
-      false,
-      checkErrorResponse({ text: [] }, null, { statusCode: 200 })
-    )
+    expect(checkErrorResponse({ text: [] }, null, { statusCode: 200 })).to.be
+      .false
   })
 })
 
@@ -78,9 +75,8 @@ describe('async error handler', function() {
 
     it('displays the custom not found message', async function() {
       const notFoundMessage = 'no goblins found'
-      const res = { statusCode: 404 }
       try {
-        await checkErrorResponse.asPromise({ notFoundMessage })({ res })
+        await checkErrorResponse.asPromise({ 404: notFoundMessage })({ res })
         expect.fail('Expected to throw')
       } catch (e) {
         expect(e).to.be.an.instanceof(NotFound)
@@ -91,9 +87,8 @@ describe('async error handler', function() {
   })
 
   context('when status is 4xx', function() {
-    const res = { statusCode: 499 }
-
     it('throws InvalidResponse', async function() {
+      const res = { statusCode: 499 }
       try {
         await checkErrorResponse.asPromise()({ res })
         expect.fail('Expected to throw')
@@ -105,12 +100,27 @@ describe('async error handler', function() {
         expect(e.prettyMessage).to.equal('invalid')
       }
     })
+
+    it('displays the custom error message', async function() {
+      const res = { statusCode: 403 }
+      try {
+        await checkErrorResponse.asPromise({
+          403: 'access denied',
+        })({ res })
+        expect.fail('Expected to throw')
+      } catch (e) {
+        expect(e).to.be.an.instanceof(InvalidResponse)
+        expect(e.message).to.equal(
+          'Invalid Response: Got status code 403 (expected 200)'
+        )
+        expect(e.prettyMessage).to.equal('access denied')
+      }
+    })
   })
 
   context('when status is 5xx', function() {
-    const res = { statusCode: 503 }
-
     it('throws Inaccessible', async function() {
+      const res = { statusCode: 503 }
       try {
         await checkErrorResponse.asPromise()({ res })
         expect.fail('Expected to throw')
@@ -122,5 +132,21 @@ describe('async error handler', function() {
         expect(e.prettyMessage).to.equal('inaccessible')
       }
     })
+
+    it('displays the custom error message', async function() {
+      const res = { statusCode: 500 }
+      try {
+        await checkErrorResponse.asPromise({
+          500: 'server overloaded',
+        })({ res })
+        expect.fail('Expected to throw')
+      } catch (e) {
+        expect(e).to.be.an.instanceof(Inaccessible)
+        expect(e.message).to.equal(
+          'Inaccessible: Got status code 500 (expected 200)'
+        )
+        expect(e.prettyMessage).to.equal('server overloaded')
+      }
+    })
   })
 })
diff --git a/lib/github-helpers.js b/lib/github-helpers.js
index aef5a6cb3a5e8e991d09a630c39f8258f470df59..a698a2d68c2e517e57302d6f0a82a78267451233 100644
--- a/lib/github-helpers.js
+++ b/lib/github-helpers.js
@@ -22,14 +22,14 @@ function checkErrorResponse(
   badgeData,
   err,
   res,
-  notFoundMessage = 'repo not found'
+  notFoundMessage = 'repo not found',
+  errorMessages = {}
 ) {
-  if (res && res.statusCode === 422) {
-    badgeData.text[1] = notFoundMessage
-    badgeData.colorscheme = 'lightgrey'
-    return true
-  }
-  return standardCheckErrorResponse(badgeData, err, res, notFoundMessage)
+  return standardCheckErrorResponse(badgeData, err, res, {
+    ...errorMessages,
+    404: notFoundMessage,
+    422: notFoundMessage,
+  })
 }
 
 const commentsColor = colorScale([1, 3, 10, 25], undefined, true)
diff --git a/lib/github-helpers.spec.js b/lib/github-helpers.spec.js
index 079410ba41d098eb75a95d7816590d295fe4e39d..7e97504461cc0ea29dd54fb459f57111915a845e 100644
--- a/lib/github-helpers.spec.js
+++ b/lib/github-helpers.spec.js
@@ -4,12 +4,34 @@ const { expect } = require('chai')
 const { checkErrorResponse } = require('./github-helpers')
 
 describe('GitHub Error Handler', function() {
-  it('makes not found badge when 422 is returned', function() {
+  it('makes repo not found badge when 422 is returned', function() {
+    const badgeData = { text: [] }
+    expect(checkErrorResponse(badgeData, null, { statusCode: 422 })).to.be.true
+    expect(badgeData.text[1]).to.equal('repo not found')
+    expect(badgeData.colorscheme).to.equal('lightgrey')
+  })
+
+  it('makes user not found badge when 404 is returned', function() {
     const badgeData = { text: [] }
     expect(
-      checkErrorResponse(badgeData, null, { statusCode: 422 }, 'repo not found')
+      checkErrorResponse(badgeData, null, { statusCode: 404 }, 'user not found')
     ).to.be.true
-    expect(badgeData.text[1]).to.equal('repo not found')
+    expect(badgeData.text[1]).to.equal('user not found')
+    expect(badgeData.colorscheme).to.equal('lightgrey')
+  })
+
+  it('makes badge with custom message when specified', function() {
+    const badgeData = { text: [] }
+    expect(
+      checkErrorResponse(
+        badgeData,
+        null,
+        { statusCode: 418 },
+        'repo not found',
+        { 418: "I'm a teapot" }
+      )
+    ).to.be.true
+    expect(badgeData.text[1]).to.equal("I'm a teapot")
     expect(badgeData.colorscheme).to.equal('lightgrey')
   })
 })
diff --git a/server.js b/server.js
index c398385fb60c8c81e37c2bd71ca60d6bdb2e38b3..97bd2ae30e45af23389a2366b5c4664095fa69df 100644
--- a/server.js
+++ b/server.js
@@ -1061,7 +1061,7 @@ cache(function(data, match, sendBadge, request) {
   const url = 'https://lgtm.com/api/v0.1/project/' + projectId + '/details';
   const badgeData = getBadgeData('lgtm', data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'project not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -1107,7 +1107,7 @@ cache(function(data, match, sendBadge, request) {
   })();
   const badgeData = getBadgeData('code quality: ' + languageLabel, data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'project not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -1337,7 +1337,7 @@ cache(function(data, match, sendBadge, request) {
     branch = 'dev-master';
   }
   request(apiUrl, function dealWithData(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'repo not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -2360,7 +2360,7 @@ cache(function(data, match, sendBadge, request) {
   const apiUrl = `https://scrutinizer-ci.com/api/repositories/${repo}`;
   const badgeData = getBadgeData(type, data);
   request(apiUrl, {}, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'project or branch not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'project or branch not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -3596,7 +3596,7 @@ cache(function(data, match, sendBadge, request) {
     badgeData.logo = getLogo('github', data);
   }
   githubApiProvider.request(request, apiUrl, {}, (err, res, buffer) => {
-    if (githubCheckErrorResponse(badgeData, err, res)) {
+    if (githubCheckErrorResponse(badgeData, err, res, 'repo not found', { 403: 'access denied' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -5309,7 +5309,7 @@ cache(function(data, match, sendBadge, request) {
 
   const badgeData = getBadgeData('build', data);
   request(apiUrl, { json:true }, function(err, res, data) {
-    if (checkErrorResponse(badgeData, err, res, 'project not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -5594,7 +5594,7 @@ cache(function(data, match, sendBadge, request) {
   var url = 'https://hub.docker.com/v2/repositories/' + path + '/stars/count/';
   var badgeData = getBadgeData('docker stars', data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'repo not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -5626,7 +5626,7 @@ cache(function(data, match, sendBadge, request) {
   var url = 'https://hub.docker.com/v2/repositories/' + path;
   var badgeData = getBadgeData('docker pulls', data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'repo not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -5695,7 +5695,7 @@ cache(function(data, match, sendBadge, request) {
   var url = 'https://registry.hub.docker.com/v2/repositories/' + path;
   var badgeData = getBadgeData('docker build', data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'repo not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -5730,7 +5730,7 @@ cache(function(data, match, sendBadge, request) {
   var url = 'https://registry.hub.docker.com/v2/repositories/' + path + '/buildhistory';
   var badgeData = getBadgeData('docker build', data);
   request(url, function(err, res, buffer) {
-    if (checkErrorResponse(badgeData, err, res, 'repo not found')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -6540,7 +6540,7 @@ cache(function(data, match, sendBadge, request) {
   const badgeData = getBadgeData('dependencies', data);
 
   request(options, function(err, res, json) {
-    if (checkErrorResponse(badgeData, err, res, 'not available')) {
+    if (checkErrorResponse(badgeData, err, res, { 404: 'not available' })) {
       sendBadge(format, badgeData);
       return;
     }
@@ -6814,7 +6814,7 @@ cache({
 
     request(url, requestOptions, (err, res, data) => {
       try {
-        if (checkErrorResponse(badgeData, err, res, 'resource not found')) {
+        if (checkErrorResponse(badgeData, err, res, { 404: 'resource not found' })) {
           return;
         }
 
diff --git a/services/apm/apm.service.js b/services/apm/apm.service.js
index 277478ac5a448b46853caf6c12e72a53c73c3523..db8afe7b8e1c77eba9d55c399f55d0b739747bc6 100644
--- a/services/apm/apm.service.js
+++ b/services/apm/apm.service.js
@@ -22,7 +22,7 @@ class BaseAPMService extends BaseJsonService {
     return this._requestJson({
       schema: apmSchema,
       url: `https://atom.io/api/packages/${repo}`,
-      notFoundMessage: 'package not found',
+      errorMessages: { 404: 'package not found' },
     })
   }
 
diff --git a/services/appveyor/appveyor.service.js b/services/appveyor/appveyor.service.js
index 26d029ce23994f9019b5cc40a6b20289b0cd2b20..6490d53e9262772c91c81cb694ca115562fb63fd 100644
--- a/services/appveyor/appveyor.service.js
+++ b/services/appveyor/appveyor.service.js
@@ -18,7 +18,7 @@ module.exports = class AppVeyor extends BaseJsonService {
     return this._requestJson({
       schema: appVeyorSchema,
       url,
-      notFoundMessage: 'project not found or access denied',
+      errorMessages: { 404: 'project not found or access denied' },
     })
   }
 
diff --git a/services/base-json.js b/services/base-json.js
index 5584531b50e05463a5db2df445ac6493f6b4d0a6..842b1f2c864a3b326c08f2ecdee21515df400208 100644
--- a/services/base-json.js
+++ b/services/base-json.js
@@ -30,7 +30,7 @@ class BaseJsonService extends BaseService {
     }
   }
 
-  async _requestJson({ schema, url, options = {}, notFoundMessage }) {
+  async _requestJson({ schema, url, options = {}, errorMessages = {} }) {
     const logTrace = (...args) => this.constructor.logTrace('fetch', ...args)
     if (!schema || !schema.isJoi) {
       throw Error('A Joi schema is required')
@@ -45,11 +45,7 @@ class BaseJsonService extends BaseService {
         logTrace(emojic.dart, 'Response status code', res.statusCode)
         return { res, buffer }
       })
-      .then(
-        checkErrorResponse.asPromise(
-          notFoundMessage ? { notFoundMessage: notFoundMessage } : undefined
-        )
-      )
+      .then(checkErrorResponse.asPromise(errorMessages))
       .then(asJson)
       .then(json => {
         logTrace(emojic.dart, 'Response JSON (before validation)', json)
diff --git a/services/github/github.tester.js b/services/github/github.tester.js
index 24db265b50292782b070642542b0182985004465..7a8fb8485d02959f580e3cb0094bd0587bfec870 100644
--- a/services/github/github.tester.js
+++ b/services/github/github.tester.js
@@ -99,7 +99,11 @@ t.create('License - API rate limit exceeded')
         documentation_url: 'https://developer.github.com/v3/#rate-limiting',
       })
   )
-  .expectJSON({ name: 'license', value: 'invalid', colorB: colorsB.lightgrey })
+  .expectJSON({
+    name: 'license',
+    value: 'access denied',
+    colorB: colorsB.lightgrey,
+  })
 
 t.create('Contributors')
   .get('/contributors/cdnjs/cdnjs.json')
diff --git a/services/npm/npm-base.js b/services/npm/npm-base.js
index 5e27fcb84e8f70053636ee59c110ab80dd5907ad..ac0de7d73de71c5b7e6c3306139a1876622e11a4 100644
--- a/services/npm/npm-base.js
+++ b/services/npm/npm-base.js
@@ -82,7 +82,7 @@ module.exports = class NpmBase extends BaseJsonService {
       // Use a custom Accept header because of this bug:
       // <https://github.com/npm/npmjs.org/issues/163>
       options: { Accept: '*/*' },
-      notFoundMessage: 'package not found',
+      errorMessages: { 404: 'package not found' },
     })
 
     let packageData
diff --git a/services/npm/npm-downloads.service.js b/services/npm/npm-downloads.service.js
index 401a693db5dd97406c8764a7b03af6e21681ba92..2855781ea3642bb3af5cd6d57e6ce13f58d74d89 100644
--- a/services/npm/npm-downloads.service.js
+++ b/services/npm/npm-downloads.service.js
@@ -79,7 +79,7 @@ function DownloadsForInterval(interval) {
       let { downloads } = await this._requestJson({
         schema,
         url: `https://api.npmjs.org/downloads/${query}/${packageName}`,
-        notFoundMessage: 'package not found or too new',
+        errorMessages: { 404: 'package not found or too new' },
       })
       if (isRange) {
         downloads = downloads
diff --git a/services/npm/npm-version.service.js b/services/npm/npm-version.service.js
index 97634705b6e9ddd17747a42237e6467e7069c67a..43d3267851fd3392457aead3e502657aa27043d4 100644
--- a/services/npm/npm-version.service.js
+++ b/services/npm/npm-version.service.js
@@ -78,7 +78,7 @@ module.exports = class NpmVersion extends NpmBase {
     const packageData = await this._requestJson({
       schema,
       url: `${registryUrl}/-/package/${slug}/dist-tags`,
-      notFoundMessage: 'package not found',
+      errorMessages: { 404: 'package not found' },
     })
 
     if (tag && !(tag in packageData)) {