diff --git a/services/github/github-common-fetch.js b/services/github/github-common-fetch.js
index 6eb60e43af4cbfe9f6e78c0f72dd7de72b742e03..aff6d6ec1dc46a2c8f00e04c19fe921e6630562c 100644
--- a/services/github/github-common-fetch.js
+++ b/services/github/github-common-fetch.js
@@ -40,7 +40,9 @@ async function fetchJsonFromRepo(
     schema: contentSchema,
     url,
     options,
-    errorMessages: errorMessagesFor(`${filename} missing or repo not found`),
+    errorMessages: errorMessagesFor(
+      `repo not found, branch not found, or ${filename} missing`
+    ),
   })
 
   let decoded
diff --git a/services/github/github-manifest.tester.js b/services/github/github-manifest.tester.js
index 75cebbe57d6dcbe1195211de1e3139d925add1cf..55a77a5043b01eb6586f5100062ba7e3fd0f81ca 100644
--- a/services/github/github-manifest.tester.js
+++ b/services/github/github-manifest.tester.js
@@ -40,5 +40,5 @@ t.create('Manifest invalid json response')
   .get('/v/RedSparr0w/not-a-real-project.json')
   .expectJSON({
     name: 'version',
-    value: 'manifest.json missing or repo not found',
+    value: 'repo not found, branch not found, or manifest.json missing',
   })
diff --git a/services/github/github-package-json.service.js b/services/github/github-package-json.service.js
index 87ad2276c5f752cf77c13e12ffe60827b2fd4e85..870289905d4f685186273fb58aef5f8f7f2cdbe6 100644
--- a/services/github/github-package-json.service.js
+++ b/services/github/github-package-json.service.js
@@ -6,11 +6,17 @@ const {
   transformAndValidate,
   renderDynamicBadge,
 } = require('../dynamic-common')
+const {
+  isPackageJsonWithDependencies,
+  getDependencyVersion,
+} = require('../package-json-helpers')
 const { semver } = require('../validators')
 const { ConditionalGithubAuthService } = require('./github-auth-service')
 const { fetchJsonFromRepo } = require('./github-common-fetch')
 const { documentation } = require('./github-helpers')
 
+const keywords = ['npm', 'node']
+
 const versionSchema = Joi.object({
   version: semver,
 }).required()
@@ -35,6 +41,7 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthService {
         namedParams: { user: 'IcedFrisby', repo: 'IcedFrisby' },
         staticPreview: this.render({ version: '2.0.0-alpha.2' }),
         documentation,
+        keywords,
       },
       {
         title: 'GitHub package.json version (branch)',
@@ -46,6 +53,7 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthService {
         },
         staticPreview: this.render({ version: '2.0.0-alpha.2' }),
         documentation,
+        keywords,
       },
     ]
   }
@@ -70,6 +78,99 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthService {
   }
 }
 
+class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthService {
+  static get category() {
+    return 'platform-support'
+  }
+
+  static get route() {
+    return {
+      base: 'github/package-json/dependency-version',
+      pattern:
+        ':user/:repo/:kind(dev|peer)?/:scope(@[^/]+)?/:packageName/:branch*',
+    }
+  }
+
+  static get examples() {
+    return [
+      {
+        title: 'GitHub package.json dependency version (prod)',
+        pattern: ':user/:repo/:packageName',
+        namedParams: {
+          user: 'developit',
+          repo: 'microbundle',
+          packageName: 'rollup',
+        },
+        staticPreview: this.render({
+          dependency: 'rollup',
+          range: '^0.67.3',
+        }),
+        documentation,
+        keywords,
+      },
+      {
+        title: 'GitHub package.json dependency version (dev dep on branch)',
+        pattern: ':user/:repo/dev/:scope?/:packageName/:branch*',
+        namedParams: {
+          user: 'zeit',
+          repo: 'next.js',
+          branch: 'canary',
+          scope: '@babel',
+          packageName: 'preset-react',
+        },
+        staticPreview: this.render({
+          dependency: '@babel/preset-react',
+          range: '7.0.0',
+        }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'dependency',
+    }
+  }
+
+  static render({ dependency, range }) {
+    return {
+      label: dependency,
+      message: range,
+      color: 'blue',
+    }
+  }
+
+  async handle({ user, repo, kind, branch = 'master', scope, packageName }) {
+    const {
+      dependencies,
+      devDependencies,
+      peerDependencies,
+    } = await fetchJsonFromRepo(this, {
+      schema: isPackageJsonWithDependencies,
+      user,
+      repo,
+      branch,
+      filename: 'package.json',
+    })
+
+    const wantedDependency = scope ? `${scope}/${packageName}` : packageName
+    const { range } = getDependencyVersion({
+      kind,
+      wantedDependency,
+      dependencies,
+      devDependencies,
+      peerDependencies,
+    })
+
+    return this.constructor.render({
+      dependency: wantedDependency,
+      range,
+    })
+  }
+}
+
 class DynamicGithubPackageJson extends ConditionalGithubAuthService {
   static get category() {
     return 'other'
@@ -98,6 +199,7 @@ class DynamicGithubPackageJson extends ConditionalGithubAuthService {
           value: ['bundle', 'rollup', 'micro library'],
         }),
         documentation,
+        keywords,
       },
       {
         title: 'GitHub package.json dynamic',
@@ -114,6 +216,7 @@ class DynamicGithubPackageJson extends ConditionalGithubAuthService {
           branch: 'master',
         }),
         documentation,
+        keywords,
       },
     ]
   }
@@ -151,5 +254,6 @@ class DynamicGithubPackageJson extends ConditionalGithubAuthService {
 
 module.exports = {
   GithubPackageJsonVersion,
+  GithubPackageJsonDependencyVersion,
   DynamicGithubPackageJson,
 }
diff --git a/services/github/github-package-json.tester.js b/services/github/github-package-json.tester.js
index 83f69266e76a4ae2481048fe76079d75fd519e4a..d376bd1b02a035269d7c9a8b8cc674b5e6334bbc 100644
--- a/services/github/github-package-json.tester.js
+++ b/services/github/github-package-json.tester.js
@@ -3,6 +3,7 @@
 const Joi = require('joi')
 const ServiceTester = require('../service-tester')
 const { isSemver } = require('../test-validators')
+const { semverRange } = require('../validators')
 
 const t = (module.exports = new ServiceTester({
   id: 'GithubPackageJson',
@@ -23,7 +24,7 @@ t.create('Package version (repo not found)')
   .get('/v/badges/helmets.json')
   .expectJSON({
     name: 'version',
-    value: 'package.json missing or repo not found',
+    value: 'repo not found, branch not found, or package.json missing',
   })
 
 t.create('Package name')
@@ -46,3 +47,57 @@ t.create('Package array')
 t.create('Package object')
   .get('/dependencies/badges/shields.json')
   .expectJSON({ name: 'package.json', value: 'invalid key value' })
+
+t.create('Peer dependency version')
+  .get('/dependency-version/paulmelnikow/react-boxplot/peer/react.json')
+  .expectJSONTypes(
+    Joi.object({
+      name: 'react',
+      value: semverRange,
+    })
+  )
+
+t.create('Dev dependency version')
+  .get(
+    '/dependency-version/paulmelnikow/react-boxplot/dev/react.json?label=react%20tested'
+  )
+  .expectJSONTypes(
+    Joi.object({
+      name: 'react tested',
+      value: semverRange,
+    })
+  )
+
+t.create('Prod prod dependency version')
+  .get('/dependency-version/paulmelnikow/react-boxplot/simple-statistics.json')
+  .expectJSONTypes(
+    Joi.object({
+      name: 'simple-statistics',
+      value: semverRange,
+    })
+  )
+
+t.create('Scoped dependency')
+  .get('/dependency-version/badges/shields/dev/@babel/core.json')
+  .expectJSONTypes(
+    Joi.object({
+      name: '@babel/core',
+      value: semverRange,
+    })
+  )
+
+t.create('Scoped dependency on branch')
+  .get('/dependency-version/zeit/next.js/dev/babel-eslint/alpha.json')
+  .expectJSONTypes(
+    Joi.object({
+      name: 'babel-eslint',
+      value: semverRange,
+    })
+  )
+
+t.create('Unknown dependency')
+  .get('/dependency-version/paulmelnikow/react-boxplot/dev/i-made-this-up.json')
+  .expectJSON({
+    name: 'dependency',
+    value: 'dev dependency not found',
+  })
diff --git a/services/npm/npm-base.js b/services/npm/npm-base.js
index af016a278264f3a632b1e4ff1916835ef552e4de..2d7d239fe9f3d7de8d1eb9fb5eb9a21d1e5fbaf9 100644
--- a/services/npm/npm-base.js
+++ b/services/npm/npm-base.js
@@ -4,26 +4,15 @@ const Joi = require('joi')
 const serverSecrets = require('../../lib/server-secrets')
 const BaseJsonService = require('../base-json')
 const { InvalidResponse, NotFound } = require('../errors')
-const { semverRange } = require('../validators')
+const { isDependencyMap } = require('../package-json-helpers')
 
 const deprecatedLicenseObjectSchema = Joi.object({
   type: Joi.string().required(),
 })
-const dependencyMap = Joi.object()
-  .pattern(
-    /./,
-    Joi.alternatives().try(
-      semverRange,
-      Joi.string()
-        .uri()
-        .required()
-    )
-  )
-  .default({})
-const schema = Joi.object({
-  dependencies: dependencyMap,
-  devDependencies: dependencyMap,
-  peerDependencies: dependencyMap,
+const packageDataSchema = Joi.object({
+  dependencies: isDependencyMap,
+  devDependencies: isDependencyMap,
+  peerDependencies: isDependencyMap,
   engines: Joi.object().pattern(/./, Joi.string()),
   license: Joi.alternatives().try(
     Joi.string(),
@@ -135,6 +124,6 @@ module.exports = class NpmBase extends BaseJsonService {
       }
     }
 
-    return this.constructor._validate(packageData, schema)
+    return this.constructor._validate(packageData, packageDataSchema)
   }
 }
diff --git a/services/npm/npm-dependency-version.service.js b/services/npm/npm-dependency-version.service.js
index a741aa7af814f1125f351d7a4ec2786c7b6e1739..82c3fa4a488e6ed0fc57177ecb8439b32a1b8a5b 100644
--- a/services/npm/npm-dependency-version.service.js
+++ b/services/npm/npm-dependency-version.service.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const { InvalidParameter } = require('../errors')
+const { getDependencyVersion } = require('../package-json-helpers')
 const NpmBase = require('./npm-base')
 
 const keywords = ['node']
@@ -78,30 +78,6 @@ module.exports = class NpmDependencyVersion extends NpmBase {
     }
   }
 
-  transform({
-    kind,
-    wantedDependency,
-    dependencies,
-    devDependencies,
-    peerDependencies,
-  }) {
-    let dependenciesOfKind
-    if (kind === 'peer') {
-      dependenciesOfKind = peerDependencies
-    } else if (kind === 'dev') {
-      dependenciesOfKind = devDependencies
-    } else {
-      dependenciesOfKind = dependencies
-    }
-
-    const range = dependenciesOfKind[wantedDependency]
-    if (range === undefined) {
-      throw new InvalidParameter({ prettyMessage: 'not found' })
-    }
-
-    return { range }
-  }
-
   async handle(namedParams, queryParams) {
     const { scope, packageName, registryUrl } = this.constructor.unpackParams(
       namedParams,
@@ -119,7 +95,7 @@ module.exports = class NpmDependencyVersion extends NpmBase {
       registryUrl,
     })
 
-    const { range } = this.transform({
+    const { range } = getDependencyVersion({
       kind,
       wantedDependency,
       dependencies,
diff --git a/services/npm/npm-dependency-version.tester.js b/services/npm/npm-dependency-version.tester.js
index 5232feda50312280e338bca11b91184c5e88baf3..5a998e8a25bb43af2b9d6c8df64c4bd9fb785ae6 100644
--- a/services/npm/npm-dependency-version.tester.js
+++ b/services/npm/npm-dependency-version.tester.js
@@ -44,5 +44,5 @@ t.create('unknown dependency')
   .get('/react-boxplot/dev/i-made-this-up.json')
   .expectJSON({
     name: 'dependency',
-    value: 'not found',
+    value: 'dev dependency not found',
   })
diff --git a/services/package-json-helpers.js b/services/package-json-helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ad97dc03cee3e1cc26e8fec2869fdee56e3f6cc
--- /dev/null
+++ b/services/package-json-helpers.js
@@ -0,0 +1,54 @@
+'use strict'
+
+const Joi = require('joi')
+const { InvalidParameter } = require('./errors')
+
+const isDependencyMap = Joi.object()
+  .pattern(
+    /./,
+    // This accepts a semver range, a URL, and many other possible values.
+    Joi.string()
+      .min(1)
+      .required()
+  )
+  .default({})
+
+const isPackageJsonWithDependencies = Joi.object({
+  dependencies: isDependencyMap,
+  devDependencies: isDependencyMap,
+  peerDependencies: isDependencyMap,
+}).required()
+
+function getDependencyVersion({
+  kind = 'prod',
+  wantedDependency,
+  dependencies,
+  devDependencies,
+  peerDependencies,
+}) {
+  let dependenciesOfKind
+  if (kind === 'peer') {
+    dependenciesOfKind = peerDependencies
+  } else if (kind === 'dev') {
+    dependenciesOfKind = devDependencies
+  } else if (kind === 'prod') {
+    dependenciesOfKind = dependencies
+  } else {
+    throw Error(`Not very kind: ${kind}`)
+  }
+
+  const range = dependenciesOfKind[wantedDependency]
+  if (range === undefined) {
+    throw new InvalidParameter({
+      prettyMessage: `${kind} dependency not found`,
+    })
+  }
+
+  return { range }
+}
+
+module.exports = {
+  isDependencyMap,
+  isPackageJsonWithDependencies,
+  getDependencyVersion,
+}