diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index bb844f547fb0385600e280a298f54536c58aacba..c45e3f149af5b29014c3eca350b8386c0e78f7e5 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -320,6 +320,16 @@ const options = [
     mergeable: true,
     cli: false,
   },
+  {
+    name: 'registryUrls',
+    description:
+      'List of URLs to try for dependency lookup. Package manager-specific',
+    type: 'list',
+    default: null,
+    stage: 'package',
+    cli: false,
+    env: false,
+  },
   // depType
   {
     name: 'ignoreDeps',
diff --git a/lib/datasource/pypi.js b/lib/datasource/pypi.js
index 69da41203799805cbb929ccb65a3cfe3eed06c31..9bdda93008b2424264f16d3372d173f83623b44b 100644
--- a/lib/datasource/pypi.js
+++ b/lib/datasource/pypi.js
@@ -1,15 +1,22 @@
 const got = require('got');
 const { isVersion, sortVersions } = require('../versioning')('pep440');
+const url = require('url');
+const is = require('@sindresorhus/is');
 
 module.exports = {
   getDependency,
 };
 
-async function getDependency(purl) {
+async function getDependency(purl, config = {}) {
   const { fullname: depName } = purl;
+  let hostUrl = 'https://pypi.org/pypi/';
+  if (!is.empty(config.registryUrls)) {
+    [hostUrl] = config.registryUrls;
+  }
+  const lookupUrl = url.resolve(hostUrl, `${depName}/json`);
   try {
     const dependency = {};
-    const rep = await got(`https://pypi.org/pypi/${depName}/json`, {
+    const rep = await got(lookupUrl, {
       json: true,
     });
     const dep = rep && rep.body;
diff --git a/lib/manager/pip_requirements/extract.js b/lib/manager/pip_requirements/extract.js
index b7283f4529d133f19285f2290255dd90934f75b4..cd3e7a336739c75987ea103741424dc9f71f62f4 100644
--- a/lib/manager/pip_requirements/extract.js
+++ b/lib/manager/pip_requirements/extract.js
@@ -16,6 +16,14 @@ module.exports = {
 function extractDependencies(content) {
   logger.debug('pip_requirements.extractDependencies()');
 
+  let registryUrls;
+  content.split('\n').forEach(line => {
+    if (line.startsWith('--index-url ')) {
+      const registryUrl = line.substring('--index-url '.length);
+      registryUrls = [registryUrl];
+    }
+  });
+
   const regex = new RegExp(`^(${packagePattern})(${specifierPattern})$`, 'g');
   const deps = content
     .split('\n')
@@ -26,13 +34,17 @@ function extractDependencies(content) {
         return null;
       }
       const [, depName, currentValue] = matches;
-      return {
+      const dep = {
         depName,
         currentValue,
         lineNumber,
         purl: 'pkg:pypi/' + depName,
         versionScheme: 'pep440',
       };
+      if (registryUrls) {
+        dep.registryUrls = registryUrls;
+      }
+      return dep;
     })
     .filter(Boolean);
   if (!deps.length) {
diff --git a/test/datasource/__snapshots__/pypi.spec.js.snap b/test/datasource/__snapshots__/pypi.spec.js.snap
index 32eece8d04c0a3a21c8619b7ac5e09b3748b5f69..1ef2c124950fa47565a7c24811f07a37398d4946 100644
--- a/test/datasource/__snapshots__/pypi.spec.js.snap
+++ b/test/datasource/__snapshots__/pypi.spec.js.snap
@@ -102,3 +102,14 @@ Object {
   "releases": Array [],
 }
 `;
+
+exports[`datasource/pypi getDependency supports custom datasource url 1`] = `
+Array [
+  Array [
+    "https://custom.pypi.net/azure-cli-monitor/json",
+    Object {
+      "json": true,
+    },
+  ],
+]
+`;
diff --git a/test/datasource/pypi.spec.js b/test/datasource/pypi.spec.js
index e8bc3da2d585adcbbc59d616192a42e6cf5aef09..bc2d62c51c873ca8a24a3796551d1e4cd4fa764b 100644
--- a/test/datasource/pypi.spec.js
+++ b/test/datasource/pypi.spec.js
@@ -8,6 +8,9 @@ const res1 = fs.readFileSync('test/_fixtures/pypi/azure-cli-monitor.json');
 
 describe('datasource/pypi', () => {
   describe('getDependency', () => {
+    beforeEach(() => {
+      jest.resetAllMocks();
+    });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce({});
       expect(await datasource.getDependency('pkg:pypi/something')).toBeNull();
@@ -26,6 +29,16 @@ describe('datasource/pypi', () => {
         await datasource.getDependency('pkg:pypi/azure-cli-monitor')
       ).toMatchSnapshot();
     });
+    it('supports custom datasource url', async () => {
+      got.mockReturnValueOnce({
+        body: JSON.parse(res1),
+      });
+      const config = {
+        registryUrls: ['https://custom.pypi.net/foo'],
+      };
+      await datasource.getDependency('pkg:pypi/azure-cli-monitor', config);
+      expect(got.mock.calls).toMatchSnapshot();
+    });
     it('returns non-github home_page', async () => {
       got.mockReturnValueOnce({
         body: {
diff --git a/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap
index 76806148973d207b39e3bf34e9552b0d061417f4..c366526d88cc18be53f125f25ec8c2d5f6b39006 100644
--- a/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap
+++ b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap
@@ -7,6 +7,9 @@ Array [
     "depName": "some-package",
     "lineNumber": 2,
     "purl": "pkg:pypi/some-package",
+    "registryUrls": Array [
+      "http://example.com/private-pypi/",
+    ],
     "versionScheme": "pep440",
   },
   Object {
@@ -14,6 +17,9 @@ Array [
     "depName": "some-other-package",
     "lineNumber": 3,
     "purl": "pkg:pypi/some-other-package",
+    "registryUrls": Array [
+      "http://example.com/private-pypi/",
+    ],
     "versionScheme": "pep440",
   },
   Object {
@@ -21,6 +27,9 @@ Array [
     "depName": "not_semver",
     "lineNumber": 4,
     "purl": "pkg:pypi/not_semver",
+    "registryUrls": Array [
+      "http://example.com/private-pypi/",
+    ],
     "versionScheme": "pep440",
   },
 ]
diff --git a/website/docs/configuration-options.md b/website/docs/configuration-options.md
index 87ad02bd5e5bc7c98e7eef59fb3db96bf20c0ce5..30bd58f7c4da993f5ffcc9aaf45544633c0c1615 100644
--- a/website/docs/configuration-options.md
+++ b/website/docs/configuration-options.md
@@ -553,6 +553,10 @@ By default, Renovate will detect if it has proposed an update to a project befor
 
 Typically you shouldn't need to modify this setting.
 
+## registryUrls
+
+This is only necessary in case you need to manually configure a registry URL to use for datasource lookups. Applies to PyPI (pip) only for now. Supports only one URL for now but is defined as a list for forwards compatibility.
+
 ## renovateFork
 
 By default, Renovate will skip over any repositories that are forked, even if they contain a `renovate.json`, because that config may have been from the source repository. To enable Renovate on forked repositories, you need to add `renovateFork: true` to your renovate config.
diff --git a/website/docs/python.md b/website/docs/python.md
index c05a9033b176ba0388b1b80fb0ddb47900f1221c..753f3ad7bc395651459a899b88799fd810c3a769 100644
--- a/website/docs/python.md
+++ b/website/docs/python.md
@@ -28,6 +28,32 @@ The default file matching regex for requirements.txt aims to pick up the most po
   }
 ```
 
+## Alternate registries
+
+Renovate will default to performing all lookups on pypi.org, but it also supports alternative index URLs. There are two ways to achieve this:
+
+#### index-url in `requirements.txt`
+
+The index URL can be specified in the first line of the file, For example:
+
+```
+--index-url http://example.com/private-pypi/
+some-package==0.3.1
+some-other-package==1.0.0
+```
+
+#### Specify URL in configuration
+
+The configuration option `registryUrls` can be used to configure an alternate index URL. Example:
+
+```json
+  "python": {
+    "registryUrls": ["http://example.com/private-pypi/"]
+  }
+```
+
+Note: an index-url found in the `requirements.txt` will take precedent over a registryUrl configured like the above. To override the URL found in `requirements.txt`, you need to configure it in `packageRules`, as they are applied _after_ package file extraction.
+
 ## Disabling Python Support
 
 The most direct way to disable all Python support in Renovate is like this: