API overview
One GraphQL endpoint serves your whole catalog. This page covers what applies to every request; the resource pages cover each operation.
Endpoint#
POST https://api.schema.fm/graphqlEvery query and mutation goes to this one URL as an HTTP POST with a JSON
body. There are no per-resource URLs and no versions in the path.
| Method | POST |
| Content type | application/json |
| Authentication | Authorization: Bearer sk_… — see Authentication and scopes |
| Body | {"query": "…", "variables": {…}, "operationName": "…"} — variables and operationName are optional |
| Response | application/json: {"data": …} and, if anything went wrong, "errors": […] |
A browser GET to the same URL opens an interactive explorer (GraphiQL). The
explorer grants nothing by itself: add your key under Headers to run
queries as that key.
Your first request#
query {
viewer {
actorType
catalogPk
scopes
}
}curl https://api.schema.fm/graphql \
-H "Authorization: Bearer $SCHEMA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"query": "query {\n viewer {\n actorType\n catalogPk\n scopes\n }\n}"
}
EOFconst res = await fetch("https://api.schema.fm/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
query {
viewer {
actorType
catalogPk
scopes
}
}
`,
}),
});
const { data, errors } = await res.json();{
"data": {
"viewer": {
"actorType": "api_key",
"catalogPk": 31,
"scopes": ["CatalogRead", "MediaRead"]
}
}
}Responses#
A request that reached GraphQL returns HTTP 200 — even when it contains
errors. Check the errors array, not the status code. A field that fails
comes back null with an entry in errors explaining why, and the rest of
the response is still returned. See Errors.
Field names are camelCase. Identifiers end in Pk (releasePk,
resourcePk) and are 64-bit integers — see Scalars.
Values from fixed sets — states, roles, outcomes — are plain strings rather than GraphQL enums; every possible value is listed in Wire values.
Your catalog is implied#
No operation takes a catalog or organization id to act on. The catalog comes from the credential: an API key is issued for one catalog, and a signed-in user acts on the catalog they selected. There is no way to address another customer's data, and no argument to get wrong. See Catalogs and tenancy.
Pagination#
List queries take a limit (each documents its own cap). releases
also takes an offset, and releasesCount tells you
the total. See Paginate and search.
Limits#
| Limit | Value | What happens |
|---|---|---|
| Query depth | 12 levels | Error Query is nested too deep.; nothing runs |
| Query complexity | 500 fields | Error Query is too complex.; nothing runs |
| List sizes | per query, e.g. releases ≤ 200 | Larger values are clamped, not rejected |
| Single upload | 5 GiB | requestUpload errors; deliver larger files over SFTP |
| Signed URL lifetime | 1 hour | Longer requests are capped |
Complexity counts every field you select, once, however many items a list
returns — so asking for resources on 200 releases costs the same as on one.
Nested loads are batched on the server: requesting releases { resources { … } }
does not make one query per release.
Schema#
The complete schema is published as SDL at
/schema.graphql and on the Schema
page. Point code generators such as graphql-codegen at that file, or
introspect the endpoint directly.
Evolving clients#
The schema grows by addition: new fields, new optional arguments, new values in string fields. Write clients that select only the fields they use, and that tolerate a string value they have not seen before — a new resource state or DDEX role should degrade a display, not crash it.