PixelPort API Reference
The PixelPort REST API gives read access to a studio's galleries, the media inside them, its clients, and its gallery sales. It also publishes the plan catalog and a live service check with no authentication at all.
- Base URL:
https://www.pixelport.co - Specification: OpenAPI 3.1 JSON · YAML
- Tool definitions: tools.json
- Catalog: `/.well-known/api-catalog`
- Discovery:
GET https://www.pixelport.co/api/v1
Authentication
Send an API key as a bearer token on every authenticated request:
curl -H "Authorization: Bearer pk_live_your_key_here" \
https://www.pixelport.co/api/v1/meCreate keys in the dashboard under Settings → API. API access is included with the Studio ($129/month) plan. A key is shown once at creation; PixelPort keeps only its SHA-256 hash.
A missing or invalid key returns 401 with a WWW-Authenticate: Bearer header:
{
"error": {
"code": "unauthorized",
"message": "The API key is invalid, revoked, or expired.",
"docs": "https://www.pixelport.co/docs/api"
}
}Endpoints
GET /api/v1— API discovery document. Operation IDgetApiIndex. No API key required.GET /api/v1/plans— List plans and prices. Operation IDlistPlans. No API key required. Query:planId.GET /api/v1/features— List product features and audiences. Operation IDlistFeatures. No API key required. Query:featureId,audienceId.GET /api/v1/status— Check service health. Operation IDgetServiceStatus. No API key required.GET /api/v1/me— Retrieve the studio behind the API key. Operation IDgetAccount. Requires an API key.GET /api/v1/galleries— List galleries. Operation IDlistGalleries. Requires an API key. Query:limit,offset,status.GET /api/v1/galleries/{galleryId}— Retrieve a gallery. Operation IDgetGallery. Requires an API key.GET /api/v1/galleries/{galleryId}/media— List media in a gallery. Operation IDlistGalleryMedia. Requires an API key. Query:limit,offset,type.GET /api/v1/clients— List clients. Operation IDlistClients. Requires an API key. Query:limit,offset,search.GET /api/v1/orders— List gallery sales. Operation IDlistOrders. Requires an API key. Query:limit,offset,status.
Each operation ID above is unique across the specification, which is what LLM function-calling importers and code generators use to name the generated function.
Function calling
Every operation in the OpenAPI document carries an x-function-schema: its inputs as one JSON Schema object, with path and query parameters merged and no $ref left to resolve. An operation that takes no arguments says so, with an empty properties object, rather than leaving you to guess whether the parameters were merely undocumented.
`https://www.pixelport.co/tools.json` serves those same schemas as finished tool definitions. Each entry carries the schema twice, under parameters and under input_schema, so the file drops into either tool format with no transformation, plus an endpoint object with the method, the URL template, whether a key is needed, and which arguments belong in the path rather than the query string.
curl -s https://www.pixelport.co/tools.json | jq '.tools[] | {name, authRequired: .endpoint.authRequired}'Finding these documents
https://www.pixelport.co/.well-known/api-catalog answers with an RFC 9727 catalog, in the RFC 9264 linkset format, naming the specification, the tool definitions, the reference documentation, and the status endpoint. Every /api/v1 response also points at it:
Link: <https://www.pixelport.co/.well-known/api-catalog>; rel="api-catalog"Conventions
- Money is an integer number of US cents. A
totalCentsof4900is $49.00. There are no floats anywhere in the API. - Timestamps are RFC 3339 strings in UTC, for example
2026-08-21T17:04:05.000Z. A field that has no value isnull, never an empty string. - Pagination uses
limit(1–100, default 25) andoffset. Every list response carries apaginationobject withtotalandhasMore. Advance a page by addinglimittooffsetwhilehasMoreis true. - Unlimited plan limits are expressed as
-1. - Deleted records are never returned.
Example: list galleries
curl -H "Authorization: Bearer pk_live_your_key_here" \
"https://www.pixelport.co/api/v1/galleries?status=active&limit=2"{
"data": [
{
"id": "cl8x1p2q0000a08l4h1f2b3c",
"name": "Johnson Wedding",
"slug": "johnson-wedding",
"status": "active",
"visibility": "password_protected",
"url": "https://sarah-chen.pixelport.co/johnson-wedding",
"allowDownloads": true,
"buyAllEnabled": true,
"buyAllPriceCents": 25000,
"watermarkEnabled": true,
"coverImageUrl": "https://cdn.stack0.dev/.../cover.jpg",
"eventDate": "2026-06-13T00:00:00.000Z",
"createdAt": "2026-06-15T18:22:41.000Z"
}
],
"pagination": { "limit": 2, "offset": 0, "total": 37, "hasMore": true }
}Example: total a studio's paid sales
curl -s -H "Authorization: Bearer pk_live_your_key_here" \
"https://www.pixelport.co/api/v1/orders?status=paid&limit=100" \
| jq '[.data[].totalCents] | add / 100'Example: read the plan catalog without a key
curl -s https://www.pixelport.co/api/v1/plans | jq '.data[] | {id, priceMonthlyCents}'Pass planId when only one plan matters:
curl -s "https://www.pixelport.co/api/v1/plans?planId=studio" | jq '.data[0].priceMonthlyCents'Errors
Every error returns the same envelope. Branch on error.code, which is stable; error.message is written for people and may change.
- 400 `invalid_request` — A query parameter failed validation.
error.detailsnames the offending fields. - 401 `unauthorized` — The bearer token is missing, malformed, revoked, or unknown.
- 404 `not_found` — No record with that id belongs to this studio, or the path is not an endpoint.
- 405 `method_not_allowed` — The API is read-only. Only
GETandOPTIONSare accepted; the response carriesAllow: GET, OPTIONS. - 429 `rate_limited` — Too many requests. Wait for the
Retry-Afterdelay. - 500 `internal_error` — Something failed on our side. Retry, then contact support with the
x-vercel-idheader.
Rate limits
At least 120 requests per 60 seconds are accepted. A request that carries a valid API key is counted against that key; every other request, including the endpoints that need no key, is counted against the client address.
Every response carries the quota, so a client can pace itself rather than discovering the ceiling by hitting it:
RateLimit-Policy: "default";q=120;w=60
RateLimit: "default";r=118;t=57
RateLimit-Limit: 120
RateLimit-Remaining: 118
RateLimit-Reset: 57
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1787500000RateLimit and RateLimit-Policy are the fields from the IETF RateLimit header-fields draft: q and w are the policy, r and t are what is left of it. RateLimit-Limit, -Remaining, and -Reset carry the same numbers under the draft's earlier names, which is what most HTTP clients read. `RateLimit-Reset` is a delay in seconds; `X-RateLimit-Reset` is Unix seconds. The X- fields are the form this API published first and are kept unchanged.
A throttled request returns 429 with Retry-After in seconds. Because the counter lives in the process that serves the request, a caller spread across several instances gets a higher effective ceiling than the numbers above — treat them as a floor that is always accepted, not a cap.
Rate-limit fields are per caller, so no shared cache stores an API response. The catalog endpoints (/api/v1, /api/v1/plans, /api/v1/features) send Cache-Control: private, max-age=300, which your own client may honour.
Versioning
Every response carries an x-api-version header holding the date the current response shapes were fixed. New optional fields may be added without a version change; treat unknown fields as forward compatibility rather than an error. A breaking change ships under a new path prefix, and /api/v1 keeps working.
CORS
All /api/v1 endpoints send Access-Control-Allow-Origin: * and answer OPTIONS preflights. This is safe because the API authenticates only with a bearer key and never with cookies, so a browser cannot make an authenticated call on a signed-in user's behalf.