Skip to main content
In pyx, you can create custom “views” on public and private package indexes. A view can be thought of as a filtered combination of one or more indexes, exposed as a single index URL. Views are exposed using the same URL scheme as regular registries. For example, if your team is acme, and you create a dev view, you can install packages from the view with:
uv pip install fastapi --index https://api.pyx.dev/simple/acme/dev
To create a view, navigate to the Views tab in the pyx dashboard.

Filters

A view can represent filtered subsets of upstream indexes. For example, while the public/pypi index includes all packages from PyPI, in pyx, you can create your own index URLs that represent filtered subsets of PyPI, such as:
  • A view of all PyPI packages uploaded prior to a given date, i.e., a snapshot of PyPI at a given point in time (package.upload_time <= "2025-01-01")
  • A view of all PyPI packages that are at least a week old, to guard against malicious packages (package.age_days >= 7)
  • A view of all PyPI packages with at least 1,000 monthly downloads (package.pypi_downloads_30_days >= 1000)
  • A view of all PyPI packages with no critical vulnerabilities (package.cve_max_score < 9.0)
View filters are expressed in a query language based on a subset of Python. For example, to filter PyPI packages to only include those uploaded prior to a given date, you can use the following filter:
package.upload_time <= "2025-01-01"
The filter language exposes the following fields:
  • package.name: Package name (e.g., fastapi)
  • package.upload_time: Package upload time (e.g., 2025-05-01 or 2025-05-01T12:00:00)
  • package.age_days: Package age in days (e.g., 7)
  • release.version: Release version (e.g., 0.100.0)
  • release.upload_time: Release upload time (e.g., 2025-06-01 or 2025-06-01T18:00:00)
  • release.age_days: Release age in days (e.g., 7)
  • file.name: File name (e.g., fastapi-0.100.0-py3-none-any.whl)
  • file.upload_time: File upload time (e.g., 2025-06-01 or 2025-06-01T18:00:00)
  • file.age_days: File age in days (e.g., 7)
  • package.pypi_downloads_30_days: 30-day downloads (e.g., 1000)
  • package.pypi_downloads_7_days: 7-day downloads (e.g., 100)
  • package.cve_max_score: Max CVE score (e.g., 9.0)
  • release.cve_max_score: Release CVE score (e.g., 9.0)
Conditions can be combined using logical operators, as in Python:
package.upload_time <= "2025-01-01" and package.pypi_downloads_30_days >= 1000
For example, to filter out a specific package (like setuptools) from a view, you can use the following filter:
package.name != "setuptools"
Similarly, to filter out a specific version of a package (like setuptools==69.0.0), you can use the following filter:
not (package.name == "setuptools" and release.version == "69.0.0")

Groups

You can also create views that combine multiple indexes. For example, a single view can pull in packages from your own private index if they exist, and falls back to PyPI if they don’t. This is achieved using groups. A view is made up of one or more groups, and each group contains one or more registries (each with an optional filter). Groups control how packages from different registries are combined when resolving dependencies.

Priority across groups

Groups are ordered by priority. When resolving a package, pyx searches each group in order and stops at the first group that contains the package. Later groups are only consulted for packages that don’t exist in any earlier group. This enables an override pattern: a high-priority group with your private registry will serve your internal packages, while a lower-priority group with PyPI provides everything else. If a package exists in your private registry, it takes precedence; PyPI is never consulted for that package. We recommend using this especially for mitigating dependency confusion attacks, where an attacker publishes a malicious package on PyPI with the same name as an existing private package. Putting your private registry in a higher-priority group ensures that packages with the same name are never served from PyPI.

Merging within a group

Registries within the same group are merged together. When a package exists in multiple registries in the same group, the view combines all of their files into a single index response. This is useful for supplementing a public index with additional files, such as custom-built wheels or private distributions. If two registries provide a file with the same filename, the file from the earlier registry in the group is preferred. For example, if a group contains both your private registry and PyPI, and both provide files for numpy, the view will include files from both registries.

Example

Consider a view with two groups:
  1. Group 1: Your private acme/main registry
  2. Group 2: public/pypi
With this configuration:
  • For acme-core (which only exists in your private registry), pyx serves it from Group 1.
  • For flask (which only exists on PyPI), pyx skips Group 1 (no match) and serves it from Group 2.
  • If you publish a custom build of numpy to your private registry, pyx serves your numpy from Group 1 and ignores PyPI’s numpy entirely.
To merge your custom numpy wheels with PyPI’s numpy wheels instead, place both registries in the same group.