
PyPI has adopted PEP 833, which “freezes” the HTML representation of the index API, which is also sometimes called the “simple API” or the “simple repository API.”
New packages and releases will continue to appear in the HTML representation, meaning this has no breaking implications for downstream index consumers.
However, future standardization and development efforts will focus on the JSON representation, and downstreams that consume only the HTML representation are strongly encouraged to transition to the JSON representation to ensure access to the latest and greatest index features.
Background
As the name suggests, PyPI’s primary responsibility is serving a package index. Installers (like pip and uv) consume this index as part of resolving and installing dependencies.
For historical reasons, there are two different standard representations of a Python packaging index:
-
The HTML representation, which predates standardization, and was retroactively standardized with PEP 503.
The HTML index is essentially a bare-bones version of the human-readable index that PyPI once served, back before PyPI was a resource that installers automatically retrieved packages from. It’s that old!
-
The JSON representation, which was standardized with PEP 691.
Unlike the HTML representation, the JSON representation was designed from the ground-up to be useful to machine clients, rather than humans.
The two representations are more or less1 coextensive in terms of serving the information that installers care about. This is intentional: efforts to extend the simple index over the years have worked hard to put that state into both representations.
Doing so with the HTML representation is often unwieldy, as metadata that naturally fits JSON’s object model needs to be shoehorned into nooks and crannies (mainly HTML attributes and new <meta> tags) that won’t interfere with pre-existing assumptions about the HTML’s shape.
Unfortunately, that shoehorning process is has become harder over time:
-
Many consumers have made suboptimal, unsound assumptions about the shape of shape of PyPI’s HTML representation. More precisely: they go beyond assuming the basic shape of the HTML, and actually make assumptions about semantically irrelevant aspects like whitespace, attribute order, &c.
This makes even “shape preserving” changes to the HTML representation fraught.
By contrast, consumers of the JSON representation don’t generally encounter these issues. Some of that is probably a function of the JSON representation being newer, but a large part is likely because, unlike HTML, users don’t find themselves open-coding a JSON parser in high-level languages.
-
Because the fundamental shape of the HTML representation can’t be changed, adding new pieces of metadata to it becomes an exercise in creative munging: things that are natural to express as object members in JSON need to be shoehorned into new, flat
<meta>tags or HTML attributes in the HTML representation.PEP 792 is a good example of this. Compare the JSON representation:
{ "meta": { "api-version": "1.4" }, "project-status": { "status": "quarantined", "reason": "the project is haunted" } }…to the HTML one:
<meta name="pypi:repository-version" content="1.4"> <meta name="pypi:project-status" content="quarantined"> <meta name="pypi:project-status-reason" content="the project is haunted">This may not seem so bad in isolation, but keep in mind that this flattening and munging needs to happen for every single new addition to the index.
-
Even when we do successfully shoehorn features into the HTML representation (on paper, and on PyPI), it often doesn’t matter to users: users are bifurcated between adoption of the JSON representation (which doesn’t have these issues) and third-party (often private) indices that only implement the absolute bare minimum required in PEP 503.
As a result, even when we do update the HTML representation, users rarely benefit from it.
“Freezing” the HTML representation is our way of acknowledging this situation. It’s our way of saying that we can’t2 deprecate the HTML representation, but that we don’t want to keep extending it given how lopsided the tradeoffs are.
Do I have to do anything?
No!
The HTML representation is not going away, and will not go “stale” in terms of content updates. “Freezing” means that Python packaging as a whole has made a policy decision to not continue to munge features into its structure.
In other words: the HTML representation will continue to be served by PyPI for the foreseeable future, but Python packaging as a whole will no longer prioritize attempting to shoe-horn new pieces of metadata into its relatively inflexible HTML structure.
The longer answers:
-
If you’re an “individual” downstream package consumer (e.g., a user who installs packages from PyPI through a tool like pip or uv): this will have no impact on you.
This is for two reasons: first, nothing is actually changing about the HTML representation from your (your tool’s) perspective. Second, your tool is probably already preferring the JSON representation: pip has supported the JSON representation since 22.2, and uv has always supported it.
-
If you’re a “bulk” index consumer (e.g., someone who mirrors all of PyPI into a public or private) copy: you also won’t be affected by this.
However, we strongly recommend that you consider switching to the JSON representation as your primary consumption format: you can still serve the HTML representation in your mirror by reconstructing it from the JSON representation, plus you’ll get access to fields and better structured metadata that the HTML can’t offer.

