Simple repository API#
The simple repository API is an HTTP-based protocol for tools to list and download Python packages. It is the API which package indexes implement to provide package managers (for example, pip) enough information to determine what to install for a given set of requirements, then go on to install those packages.
There is one version series for the API: version 1. Minor versions add optional features, and are described below.
The API consists of two endpoints: listing projects available in the index, and listing package files for (and some other details of) a given project. These endpoints are provided as responses from an HTTP server.
There are two representations of responses from the API: HTML and JSON. Apart from optional features, these representations provide the same information.
Specification#
OpenAPI
An OpenAPI document which specifies the full API. It declares both representations, but only details the JSON format’s schema.
openapi: '3.1.0'
info:
title: Simple Repository API
version: '1.1'
summary: A protocol for tools to list and and download Python packages
paths:
/:
get:
summary: Projects list
description: List all projects provided by this package index
responses:
200:
description: Success
content:
text/html:
schema:
title: HTML response
type: string
application/vnd.pypi.simple.v1+html:
schema:
title: HTML response
type: string
application/vnd.pypi.simple.v1+json:
schema:
title: JSON response
type: object
required:
- meta
- projects
properties:
meta:
$ref: '#/components/schemas/meta'
projects:
title: Projects list
type: array
items:
title: Project reference
type: object
required:
- name
properties:
name:
title: Project name
type: string
example:
meta:
api-version: '1.0'
projects:
- name: Frob
- name: spamspamspam
/{project}/:
parameters:
- name: project
in: path
description: Project normalised name
required: true
example: numpy
schema:
type: string
pattern: ^[a-z0-9](-?[a-z0-9])*[a-z0-9]?$
get:
summary: Project details
description: Some project details and all package files in the index for
the project
responses:
200:
description: Success
content:
text/html:
schema:
title: HTML response
type: string
application/vnd.pypi.simple.v1+html:
schema:
title: HTML response
type: string
application/vnd.pypi.simple.v1+json:
schema:
title: JSON response
type: object
required:
- meta
- name
- files
properties:
meta:
$ref: '#/components/schemas/meta'
name:
title: Project name (normalised)
type: string
pattern: ^[a-z0-9](-?[a-z0-9])*[a-z0-9]?$
files:
title: Project package files
type: array
items:
title: Package file details
type: object
required:
- filename
- url
- hashes
properties:
filename:
title: Package filename
type: string
url:
title: File download URL
type: string
format: uri-reference
hashes:
$ref: '#/components/schemas/hashes'
requires-python:
title: Python requirement
description: Value of distribution's
`Requires-Python` metadata field
type: string
dist-info-metadata:
title: Distribution metadata file details
description: Indicates whether the distribution
metadata file exists. This file is located at the
URL equal to the package file's URL appended with
`.metadata`
oneOf:
- title: Metadata file existence
type: boolean
- $ref: '#/components/schemas/hashes'
gpg-sig:
title: File GPG signature file existence
description: Indicates whether the GPG signature
file exists. This file is located at the URL equal
to the package file's URL appended with `.asc`
type: boolean
yanked:
title: Distribution yanked details
description: Indicates whether the distribution has
been yanked
oneOf:
- title: Yanked status
type: boolean
- title: Yanked reason
type: string
example:
meta:
api-version: '1.0'
name: holygrail
files:
- filename: holygrail-1.0.tar.gz
url: https://example.com/files/holygrail-1.0.tar.gz
hashes:
sha256: ...
blake2b: ...
requires-python: '>=3.7'
yanked: Had a vulnerability
- filename: holygrail-1.0-py3-none-any.whl
url: https://example.com/files/holygrail-1.0-py3-none-any.whl
hashes:
sha256: ...
blake2b: ...
requires-python: '>=3.7'
dist-info-metadata: true
404:
description: Project not known by the index
components:
schemas:
meta:
title: Response meta-details
type: object
properties:
api-version:
type: string
enum: ['1.0', '1.1', '1.2']
hashes:
title: File hashes
type: object
additionalProperties:
title: Hash value
description: Hexadecimal encoding of hash value
type: string
pattern: ^[0-9a-f]+$
propertyNames:
title: Hash name
description: Name of hash algorithm in `hashlib`, lower-case,
preferably `sha256`
pattern: ^[a-z0-9_]+$
examples:
- sha256: 4e388ab32b10dc8dbc7e28144f552830adc74787c1e2c0824032078a79f227fb
servers:
- url: https://pypi.org/simple/
description: PyPI, the default package index used by most Python packaging
tools. It runs [Warehouse](https://warehouse.pypa.io/) as the index
server.
API version scheme#
The version of the API is a version number which follows the version
specifier specification as only Major.Minor.
Incrementing the major version is used to signal a backwards incompatible change such that existing clients would no longer be expected to be able to meaningfully use the API.
Incrementing the minor version is used to signal a backwards compatible change such that existing clients would still be expected to be able to meaningfully use the API.
Clients should introspect each response for the repository version, and must assume version 1.0 if missing. If the major version is greater than expected, clients must fail with an appropriate error message for the user. If the minor version is greater than expected, clients should warn the user with an appropriate message. Clients may continue to use feature detection.
Representations#
The index server can respond with one of two different representation formats for each endpoint [1]:
HTML5: content-type
application/vnd.pypi.simple.v1+htmlClients can also request
application/vnd.pypi.simple.latest+html
JSON: content-type
application/vnd.pypi.simple.v1+jsonClients can also request
application/vnd.pypi.simple.latest+jsontext/htmlis an alias for this content-type
Content negotiation#
The representation can be selected by the client through the use of HTTP content negotiation.
To request one of these representations, the Accept header should be
set in requests to one of the above content-type values. If this header is
missing, the index server assumes it to be */*.
Responses from the index server should have the corresponding Content-Type header for the
representation provided. If latest was requested by the client, then v1
must be returned by the index server.
If a representation can’t be selected from the client’s request, then the index server can do one of:
Select a default content type other than what the client has requested and return a response with that.
Return a HTTP 406 Not Acceptable response to indicate that none of the requested content types were available, and the server was unable or unwilling to select a default content type to respond with.
Return a HTTP 300 Multiple Choices response that contains a list of all of the possible responses that could have been chosen. This option is not encouraged as there is no standard format for this list.
Clients should be prepared to handle all possible responses.
URL parameter#
This method takes precedence over content negotiation. Index servers may optionally support this method, or respond with an error if present, and clients should not rely on it.
A format URL parameter can be specified, with value equal to one of the
above content-type values. If the index server does not support the value, it
may fall back to content negotiation.
Endpoint configuration#
This method is simply a suggestion, and is not standardised. Servers could configure different base URLs to serve the different representations.
Endpoints#
The API consists of two metadata endpoints:
The root URL / represents the base URL, where it would be prefixed with
the index’s URL to construct the full URL which tools make the request for.
If a client makes a request to a URL without a trailing forward-slash /,
then the index server should redirect the client to the same URL with the /
appended.
Projects list#
URL: /, the root URL
This endpoint returns a list of all of the projects provided by the index, with each list item containing the project’s name. This list is not necessarily ordered.
HTML representation#
The response from the index is a valid HTML5 page.
A metadata element <meta> may exist anywhere in the HTML document, with
name attribute value equal to the string pypi:repository-version, and
content attribute value equal the API version which the response
implements.
Each project provided by the index has a corresponding anchor element
<a>:
Its body text must exist and is the name of the project (not necessarily normalized).
Its
hrefattribute must exist and is a URL to the project details page for the project. This URL must end with a forward-slash/, but may be absolute or relative.
An example response page:
<!DOCTYPE html>
<html>
<head>
<meta name="pypi:repository-version" content="1.0">
<title>Projects</title>
</head>
<body>
<a href="/frob/">frob</a>
<a href="/spamspamspam/">spamspamspam</a>
</body>
</html>
JSON representation#
The response from the index is a valid JSON document. This document represents an object with properties:
meta(object, required) - response metadata; has properties:api-version(string, required) - the API version the response implements.
projects(array of objects, required) - projects list. Each project provided by the index corresponds to an element in this array, and vice versa. Objects have properties:name(required) - the project’s name (not necessarily normalized), as a string.
Unknown JSON object keys must be ignored.
An example response document:
{
"meta": {
"api-version": "1.0"
},
"projects": [
{"name": "Frob"},
{"name": "spamspamspam"}
]
}
Project details#
URL: /<project>/, where <project> is replaced with the normalized
name of the project.
This endpoint returns some metadata of the project, along with a list of all package files provided by the index for the project. This list of files is not necessarily ordered.
If a client uses an unnormalized name for <project>, the index server may
redirect to the URL with the normalized name. Conformant client must always
make requests with normalized names.
API file-related features:
The file can be hosted anywhere, not necessarily by the index server.
The file’s URL in the list-item is a URL to fetch the file. It may be absolute or relative. Its last path segment must be the file’s filename.
Hashes of the file’s contents are optional but recommended. The hash name is the name of the hash algorithm’s function, and the value is the hex-encoded digest hash. The function should be one in the standard-library
hashlibmodule, andhashlib.sha256()is preferred.A GPG signature for the file can be accessed at the same URL as the file but with
.ascappended, if it is provided. For example, the file at/packages/HolyGrail-1.0.tar.gzmay have a signature at/packages/HolyGrail-1.0.tar.gz.asc.The file’s Requires-Python metadata field may be provided. Clients should ignore the file when installing to an environment for a version of Python which doesn’t satisfy the requirement.
Files may be marked as yanked.
The file’s Core Metadata must be provided if its existence is indicated (by one of the representation-specific mechanisms described below). In addition, the file must contain this metadata which will not be modified when the distribution is processed and/or installed.
The metadata must be accessed at the same URL as the file but with
.metadataappended. For example, the file at/files/distribution-1.0-py3.none.any.whlmay have its metadata at/files/distribution-1.0-py3.none.any.whl.metadata.The index should also provide a hash of the metadata.
HTML representation#
The response from the index is a valid HTML5 page.
A metadata element <meta> may exist anywhere in the HTML document, with
name attribute value equal to the string pypi:repository-version, and
content attribute value equal the API version which the response
implements.
Each distribution package file provided by the index for the project has a
corresponding anchor element <a>:
Its body text must exist and is the file’s filename.
Its
hrefattribute must exist and is the file’s URL.This URL should also include a URL fragment of the form
#<hash>=<value>, where<hash>is the hash name and<value>is hash value.
A
data-gpg-sigdata attribute may exist, and have valuetrueto indicate a file has a GPG signature (at the location described above), orfalseto indicate no signature. Indexes should do this for none or all files (not some).A
data-requires-pythondata attribute may exist, and have value equal to the Requires-Python metadata field for the file’s release, with HTML-encoding (less-than<becomes the string<, and greater-than>becomes the string>).A
data-yankeddata attribute may exist to indicate the file was yanked. The attribute may have a value which specifies the reason the file is yanked.A
data-core-metadatadata attribute may exist to indicate the index provides the file’s core-metadata. The attribute’s value should be of the form<hash>=<value>, where<hash>is the hash name and<value>is hash value; otherwise, the value may the stringtrue, or not provided, if the metadata’s hash is not available.This attribute may be duplicated as the data attribute
data-dist-info-metadata.
An example response page:
<!DOCTYPE html>
<html>
<head>
<meta name="pypi:repository-version" content="1.0">
<title>Foo</title>
</head>
<body>
<a href="/foo/foo-1.0.0.tar.gz">foo-1.0.0.tar.gz</a>
<a
href="/foo/foo-1.0.1.tar.gz#sha256=abcd1234"
data-gpg-sig="true"
data-requires-python=">=3.12"
data-yanked="Too much bar"
data-core-metadata="sha256=abcd1234"
>foo-1.0.1.tar.gz</a>
</body>
</html>
JSON representation#
The response from the index is a valid JSON document. This document represents an object with properties:
meta(object, required) - response metadata; has properties:api-version(string, required) - the API version the response implements.
name(string, required) - the normalized name of the project.versions(array of strings, required) - all of the project versions uploaded for this project. It must not contain duplicates, and the order is not significant. All files must be associated with a version in this array, but not all versions need files associated. These versions should be normalized.New in version 1.1.
files(array of objects, required) - files list. Each file provided by the index for the project corresponds to an element in this array, and vice versa. Objects have properties:filename(string, required) - the file’s filenameurl(string, required) - the file’s URLhashes(object, required) - the file’s hashes. Its keys are the hash names, and the values are the corresponding hash values. Should contain at least one hash.gpg-sig(boolean, optional) - indicates whether the index provides the file’s GPG signature.If this key is missing, the signature may or may not be available.
requires-python(string, optional) - the Requires-Python metadata field for the file’s release.yanked(boolean or string, optional) - indicates whether the file should be considered yanked (if truthy, using Python truthiness) or not (if falsy).If this is a string, then it specifies the reason for being yanked.
core-metadata(boolean or object, optional) - indicates whether the index provide’s the file’s Core Metadata (if truthy, using Python truthiness) or not (if falsy).If this is an object, then it contains hashes of the metadata, in the same form as the
hashesfile-object key.If this key is missing, the metadata may or may not be available.
size(number, required) - file size in integer bytes.New in version 1.1.
upload-time(string, optional) - file upload time, as an ISO 8601 date/time string in the UTC timezone using aZsuffix with precision between seconds and microseconds: in the formatYYYY-mm-ddTHH:MM:SS.ffffffZ(number off’s variable).New in version 1.1.
Unknown JSON object keys must be ignored.
An example response document:
{
"meta": {
"api-version": "1.0"
},
"name": "foo",
"files": [
{"filename": "foo-1.0.0.tar.gz", "url": "/foo/foo-1.0.0.tar.gz"},
{
"filename": "foo-1.0.1.tar.gz",
"url": "/foo/foo-1.0.1.tar.gz",
"gpg-sig": true,
"requires-python": ">=3.12",
"yanked": "Too much bar",
"core-metadata": {"sha256": "abcd1234"}
}
]
}
Yanked files#
A yanked package file is one intended to be now-unavailable for installation from the index. The file’s yank status can be changed at anypoint (to be unyanked, or even yanked again).
Indexes may provide a textual reason for why the file has been yanked, and clients may display that reason to end-users.
Installers must ignore yanked releases if a non-yanked release satisfies the requirement. Installers may refuse to install a yanked release and not install anything. Installers should follow the spirit of the intention of yanked files [2] and prevent new dependencies on yanked releases and files.
Installers should emit a warning if they decide to install a yanked file. That warning may utilize the reason for the yanking.
What this means is left up to the specific installer, to decide how to best fit into the overall usage of their installer. However, there are two suggested approaches to take:
Yanked files are always ignored, unless they are the only file that matches a version specifier that “pins” to an exact version using either
==(without any modifiers that make it a range, such as.*) or===. Matching this version specifier should otherwise be done as per the] version specifier specification for things like local versions, zero padding, etc.Yanked files are always ignored, unless they are the only file that matches what a lock file (such as Pipfile.lock or poetry.lock) specifies to be installed. In this case, a yanked file SHOULD not be used when creating or updating a lock file from some input file or command.
Mirror indexes may omit list items for yanked files in their responses to clients, or may include list items for yanked files along with their yank status (this status must be present for yanked files).
History#
September 2015: initial form of the HTML format, in PEP 503
July 2016: Requires-Python metadata, in an update to PEP 503
May 2019: “yank” support, in PEP 592
July 2020: API versioning convention and metadata, and declaring the HTML format as API v1, in PEP 629
May 2021: providing package metadata independently from a package, in PEP 658
May 2022: initial form of the JSON format, with a mechanism for clients to choose between them, and declaring both formats as API v1, in PEP 691
October 2022: project versions and file size and upload-time in the JSON format, in PEP 700
June 2023: renaming the field which provides package metadata independently from a package, in PEP 714
Footnotes