Package Indexes & Finding Packages

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the Python Reference bibliography.

pip finds distributions on package indexes and in find-links locations. This page covers the options that control where it looks and the two commands that only look (pip index, pip download). It was written against pip 26.2.2 and run on CPython 3.14.6 on 2026-10-22. The private index in the examples is birdwatch: pypiserver 2.4.2 serving the birdtally files on http://localhost:8080; for names it does not host it answers with a redirect to PyPI. Running your own index is covered on Private Indexes & Alternative Distribution.

The index options

Option Meaning

-i, --index-url <url> (PIP_INDEX_URL)

The base URL of the index, https://pypi.org/simple by default. It must speak the simple repository API, or be a local directory laid out the same way.

--extra-index-url <url> (PIP_EXTRA_INDEX_URL)

Additional indexes, searched in addition to --index-url, with the same priority.

--no-index (PIP_NO_INDEX)

Ignore every index and use only --find-links. Also disables the pip version check.

-f, --find-links <url-or-path> (PIP_FIND_LINKS)

A directory of archives, or an HTML page whose links point to archives. VCS links are not supported.

pip has no source priority. Every index and find-links location is consulted, and the best matching version from any of them is chosen. That is what makes --extra-index-url dangerous for private names: the pip documentation’s example of it carries the warning that using it "to search for packages which are not in the main repository (for example, private packages) is unsafe", because an attacker who uploads the same name with a higher version to PyPI wins. Use one index that serves everything (an index server that proxies PyPI), or --no-index with a curated directory. See Supply-Chain Security.

What an index serves

pip reads the simple API: /simple/ lists projects, /simple/<normalized-name>/ lists files with their hashes, and pip asks for the PEP 691 JSON form first, falling back to PEP 503 HTML. The birdwatch server answers with HTML (pypiserver does not implement the JSON form):

curl -s http://localhost:8080/simple/
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Simple Index</title>
    </head>
    <body>
        <h1>Simple Index</h1>
                <a href="birdtally/">birdtally</a><br>
    </body>
</html>

The JSON form served by PyPI, with core-metadata, upload-time and provenance, is shown on Packaging Concepts & Standards.

Installing from another index

python -m pip install --index-url http://localhost:8080/simple "birdtally[pretty]"

works for the whole tree, because the server redirects rich, pygments and the rest to PyPI. With an index that does not fall back, you would need PyPI as well, and the only way pip offers is --extra-index-url, with the risk above. An index on plain HTTP is accepted only for localhost; for any other host pip logs The repository located at <host> is not a trusted or secure host and is being ignored unless you pass --trusted-host. For a real private index use HTTPS instead (see Authentication, Proxies & Certificates).

The TestPyPI pattern

The Packaging User Guide shows how to test a package uploaded to TestPyPI (not run here):

python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ your-package

The --extra-index-url is there so that dependencies, which usually do not exist on TestPyPI, come from PyPI. It also means that any project on TestPyPI with the same name as one of your dependencies can be installed instead, and anyone can upload anything to TestPyPI. Run it only in a throw-away venv, pin your dependencies, and never reuse that environment.

pip index versions

pip index versions <name> lists the versions an index offers, newest first. It honours the index options and --pre; since pip 25.1 --json prints machine-readable output:

python -m pip index versions rich
python -m pip index versions --json rich
python -m pip index versions --index-url http://localhost:8080/simple birdtally
python -m pip index versions --pre celery
rich (15.0.0)
Available versions: 15.0.0, 14.3.4, 14.3.3, 14.3.2, 14.3.1, 14.3.0, 14.2.0, 14.1.0, 14.0.0, 13.9.4, 13.9.3, …
{"name": "rich", "versions": ["15.0.0", "14.3.4", "14.3.3", …], "latest": "15.0.0"}
birdtally (0.1.0)
Available versions: 0.1.0
celery (5.7.0rc1)
…

(The --json line is shortened here; the real output lists every version.) When the package is installed, the text output also shows INSTALLED: and LATEST: lines. The command was added in pip 21.2; like pip download it also accepts --platform, --python-version, --implementation and --abi.

pip download and offline installs

pip download resolves exactly like pip install but saves the files into a directory (-d/--dest, the current directory by default) instead of installing them. Together with --no-index --find-links it gives an offline install:

python -m pip download -d wheelhouse --index-url http://localhost:8080/simple "birdtally[pretty]"
ls wheelhouse
Saved ./wheelhouse/birdtally-0.1.0-py3-none-any.whl
Saved ./wheelhouse/rich-15.0.0-py3-none-any.whl
Saved ./wheelhouse/pygments-2.21.0-py3-none-any.whl
Saved ./wheelhouse/markdown_it_py-4.2.0-py3-none-any.whl
Saved ./wheelhouse/mdurl-0.1.2-py3-none-any.whl
Successfully downloaded birdtally rich pygments markdown-it-py mdurl
birdtally-0.1.0-py3-none-any.whl
markdown_it_py-4.2.0-py3-none-any.whl
mdurl-0.1.2-py3-none-any.whl
pygments-2.21.0-py3-none-any.whl
rich-15.0.0-py3-none-any.whl

On the offline machine, in a fresh venv:

python -m pip install --no-index --find-links wheelhouse "birdtally[pretty]"
Looking in links: wheelhouse
Processing ./wheelhouse/birdtally-0.1.0-py3-none-any.whl
Processing ./wheelhouse/rich-15.0.0-py3-none-any.whl (from birdtally[pretty])
Processing ./wheelhouse/markdown_it_py-4.2.0-py3-none-any.whl (from rich>=13->birdtally[pretty])
Processing ./wheelhouse/pygments-2.21.0-py3-none-any.whl (from rich>=13->birdtally[pretty])
Processing ./wheelhouse/mdurl-0.1.2-py3-none-any.whl (from markdown-it-py>=2.2.0->rich>=13->birdtally[pretty])
Installing collected packages: pygments, mdurl, birdtally, markdown-it-py, rich

Successfully installed birdtally-0.1.0 markdown-it-py-4.2.0 mdurl-0.1.2 pygments-2.21.0 rich-15.0.0

A directory of wheels like this is a wheelhouse; the pip documentation calls it an installation bundle and pairs it with hash checking (Repeatable & Secure Installs). pip download downloads sdists when no wheel fits; pip wheel builds wheels from them instead (see Wheels & Caching).

To download for another platform, --platform, --python-version, --implementation and --abi require --only-binary=:all: or --no-deps, and the documentation recommends setting all four once you set one, because each defaults to the current interpreter rather than to "any". The example is on Installing Packages.

pip search no longer works

pip search used PyPI’s XML-RPC search API, which PyPI disabled:

python -m pip search birdtally
ERROR: XMLRPC request failed [code: -32500]
RuntimeError: PyPI no longer supports 'pip search' (or XML-RPC search). Please use https://pypi.org/search (via a browser) instead. See https://warehouse.pypa.io/api-reference/xml-rpc.html#deprecated-methods for more information.

The command still exists for other indexes that implement the XML-RPC search method (pip search --index <url>). For PyPI use the website, or pip index versions when you already know the name.

Source: pip search.

pip and Poetry

pip Poetry

--index-url replaces PyPI

A primary source; adding any primary source disables the implicit PyPI

--extra-index-url

Another primary source, or supplemental (searched only if nothing else matches)

(no equivalent)

explicit sources and per-package source constraints

--find-links

No equivalent; use a path or URL dependency on the file itself

pip index versions

poetry search <name> (versions of an exact name); poetry show --latest for the locked packages

pip search

poetry search <name> still lists the versions of an exact name (it did for tomli-w), but its docs note that PyPI no longer allows full-text search without a browser