GraphQL APIOne endpoint for everything prefix.dev knows about packages and channels, with an interactive explorer and ready-to-run examples.
Every page on prefix.dev is rendered from the same GraphQL API you can call yourself. It lives at a single endpoint, https://prefix.dev/api/graphql, and handles both queries and mutations.
Use it to:
Search and browse packages across every channel
Read package metadata, dependencies, hashes and attestations
Inspect and configure your own channels
Pull download statistics
Create and revoke API keys
Prefer a terminal?
Everything on this page is also available as a CLI. pixi-pfx wraps these queries in commands with table output and a JSON envelope for scripts.
Endpoint and authentication
Send a POST request with a JSON body containing a query field. A GET request to the same URL serves the interactive explorer instead.
curl -s https://prefix.dev/api/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{ package(channelName: \"conda-forge\", name: \"numpy\") { name summary } }"}'Reads against public channels need no credentials. For private channels and for every mutation, pass an API key as a bearer token:
curl -s https://prefix.dev/api/graphql \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $PREFIX_DEV_API_TOKEN" \
-d '{"query": "{ viewer { login } }"}'See API and API keys for how to create one. The explorer at the bottom of this page uses the session of the browser you are logged in with, so no token is needed there.
Pagination
Every paginated field (packages, variants, versions, channels, attestations and quarantines) caps limit at 50. Ask for more and you get 50 back without an error, so walk the set with page (0-indexed) and use pages and totalCount to know when to stop.
{
package(channelName: "conda-forge", name: "numpy") {
variants(limit: 50, page: 0) {
current
pages
totalCount
page {
version
buildString
platform
filename
}
}
}
}At the time of writing that returns pages: 72 and totalCount: 3574, so pages 0 through 71 are valid.
Filtering and ordering
List fields take a filters argument whose string fields accept eq, ne, isIn, isNotIn, startsWith, endsWith and contains. Conditions nest with and and or. Ordering is either byField or, for names, bySimilarity, which also exposes a similarityScore on each result.
{
packages(
filters: { name: { contains: "torch" } }
orderBy: { bySimilarity: { field: NAME, matches: "torch", direction: DESC } }
limit: 10
) {
totalCount
page {
name
similarityScore
summary
channel { name }
}
}
}On channels, namespace filters on the name a channel is published under, while owner filters on the billing owner. The two differ for channels without a billing owner, such as the public mirrors.
Examples
Expand any of these for a query you can paste straight into the explorer. They are the same examples the explorer itself offers under its Examples tab.
Packages
Get one package with its variants
Everything the package page shows: channel, platforms and the newest builds.
{
package(channelName: "conda-forge", name: "numpy") {
name
summary
description
platforms
channel {
name
owner { name }
}
variants(limit: 10) {
totalCount
page {
version
buildString
platform
filename
size
md5
yankedReason
}
}
}
}Filter variants by platform, version and build
All variant filters combine, and orderBy accepts BUILD_STRING, CREATED_AT or SIZE.
{
package(channelName: "conda-forge", name: "numpy") {
variants(
limit: 20
platform: "linux-64"
version: "2.5.2"
orderBy: { byField: { field: CREATED_AT, direction: DESC } }
) {
totalCount
page {
filename
buildString
buildNumber
size
createdAt
}
}
}
}Look up a single variant by filename
Useful for verifying a download: one record, with both hashes and the yank reason.
{
variant(
channelName: "conda-forge"
packageName: "numpy"
platformName: "linux-64"
fileName: "numpy-2.5.2-py313hf6604e3_0.conda"
) {
filename
version
buildString
size
md5
sha256
license
createdAt
yankedReason
}
}Resolve a MatchSpec
packageByMatchspec takes the same spec syntax as pixi and conda. channels is required.
{
packageByMatchspec(matchSpec: "numpy >=2.0", channels: ["conda-forge"]) {
name
channel { name }
variants(limit: 5) {
totalCount
page {
version
filename
platform
}
}
}
}List the platforms and versions that exist
A cheap way to answer "is this built for osx-arm64?" without paging through variants.
{
package(channelName: "conda-forge", name: "polars") {
platforms
versions(limit: 10) {
totalCount
page { version }
}
}
}Read dependencies, run exports and repodata patches
These come back as raw JSON, matching what ends up in repodata.json. The depends and constrains lists live inside rawIndex.
{
package(channelName: "conda-forge", name: "libcurl") {
variants(limit: 1, platform: "linux-64") {
page {
filename
rawIndex # depends, constrains, build metadata
rawAbout # home, license, doc_url
rawRunExports # weak / strong / noarch run exports
repoDataPatches
}
}
}
}Find yanked or hidden variants
includeHidden: true is needed to see variants that were removed from the index.
{
package(channelName: "conda-forge", name: "numpy") {
variants(limit: 50, includeHidden: true) {
totalCount
page {
filename
hidden
yankedReason
}
}
}
}Fetch attestations for a variant
Sigstore attestations published alongside a package file.
{
variant(
channelName: "conda-forge"
packageName: "numpy"
platformName: "linux-64"
fileName: "numpy-2.5.2-py313hf6604e3_0.conda"
) {
attestationsUrl
attestations(limit: 10) {
totalCount
page {
url
sha256
createdAt
}
}
}
}Channels
Browse and filter channels
The same filter grammar as packages, over the channel list.
{
channels(
filters: { name: { contains: "bio" } }
orderBy: { byField: { field: NAME, direction: ASC } }
limit: 20
) {
totalCount
page {
name
description
isPublic
baseUrl
size
}
}
}Filter packages inside one channel
Scoped to a single channel, so the counts are the channel's own.
{
channel(name: "conda-forge") {
name
description
packages(filters: { name: { startsWith: "pytorch" } }, limit: 20) {
totalCount
page {
name
lastCreatedDate
totalSize
}
}
}
}Download counts
Download counts per version
aggregateBy names the dimension to collapse, not the one to group by. Passing PLATFORM sums across platforms and returns rows still split by version, date and python.
{
package(channelName: "conda-forge", name: "polars") {
downloadCountVersions
downloadCounts(aggregateBy: PLATFORM) {
version
date
python
count
}
}
}Monthly download rollups
A separate field with a bucket date, over a range you choose.
{
package(channelName: "conda-forge", name: "polars") {
monthlyCount(
aggregateBy: PLATFORM
startDate: "2026-01-01"
endDate: "2026-08-01"
) {
bucket
version
totalCount
}
}
}Mutations
These write to the live database
Mutations run against production with your own credentials. There is no sandbox, and deleting a channel really deletes it. Change the placeholder names below before running anything.
Make a channel public
mutation updateChannel {
updateChannel(name: "channel_name", isPublic: true) {
name
isPublic
}
}Create an API key
The key value is only shown once, on creation, so store it right away.
mutation makeApiKey {
createApiKey(name: "new_key", expiresAt: "2026-12-31T00:00:00Z") {
key
}
}Delete an API key
mutation delApiKey {
deleteApiKey(name: "new_key")
}Explorer
The explorer below is the real API. Its sidebar has three tabs: Docs to browse the schema, Examples to load any query from this page into the editor, and History for what you ran before. Press the play button or Ctrl-Enter to run.
For a fullscreen version, open https://prefix.dev/api/graphql directly.
Next steps
pixi-pfx CLI: the same API from a terminal, in CI, or from an agent.
API and API keys: tokens, scopes and the upload API.
Create and configure a channel: the same settings in the web UI.