Errors

An error is a JSON body with a machine-readable code and a sentence for people in message. Branch on code, not on the message:

{
  "code": "insufficient_funds",
  "message": "Your balance cannot cover this screen. Add funds at /billing.",
  "required_usd": "12.50",
  "available_usd": "3.00"
}

Some codes add fields, listed below. A 503 is temporary: retry after the Retry-After header's seconds when there is one, or after a minute when not. A 502 or 504 whose body isn't JSON comes from a proxy between you and the API rather than from the API itself. It is temporary too; retry it the same way.

Cloudflare 403

A Cloudflare 403 with error code: 1010 is not the API's JSON forbidden error. Cloudflare blocked the request before it reached Ptarmigan, often because the User-Agent is missing or is an HTTP-library default such as python-httpx or python-requests. Set a descriptive header such as User-Agent: acme-screen/1.0 and retry. If it still fails, send us the Cloudflare Ray ID from the response. Repeating the same request unchanged will not help.

Codes

Code Status Meaning What to do
unauthorized 401 The API key is missing, unknown or revoked. Check the Authorization: Bearer header; create a key at https://ptarmigan.app/api-keys.
insufficient_funds 402 The balance can't cover the screen. Carries required_usd and available_usd. Add funds at https://ptarmigan.app/billing, or screen a smaller library.
not_found 404 No such protein, library, screen or compound on this account. Check the id; ids come from the list routes.
library_not_ready 409 The library is still being prepared. Poll the library until its status is ready.
library_failed 409 The library could not be prepared. Fix the file and upload it again.
library_outdated 409 The library must be prepared again before it can be screened. Update it on the Libraries page, then retry.
model_mismatch 409 The target's predictions can't be used for screening at the moment. Retry later.
library_in_use 409 A screen of this library is queued or running, so it can't be deleted. Wait for the screen to finish.
not_cancellable 409 The screen has started; only a queued screen can be cancelled. Let it finish.
screen_not_completed 409 The screen has no results yet. Poll the screen until it is completed.
idempotency_key_reused 409 This Idempotency-Key already queued a screen of another target or library. Carries screen_id. Use a new key for a new screen.
conflict 409 Another conflict with the current state. Read message.
results_expired 410 The screen's results are past their 6-month retention. Screen again.
gone 410 The resource no longer exists. Read message.
too_large 413 The upload is larger than your plan allows. Split the library, or see the limit on the Libraries page.
invalid_file 422 The upload could not be read as a library. Fix the file; see Library files.
invalid_cursor 422 after is not a cursor this route issued. Pass next_after from the previous page unchanged.
invalid_request 422 A parameter or the body is malformed. Read message for the field.
bad_request 400 The request is malformed. Read message.
forbidden 403 Not allowed. Read message.
method_not_allowed 405 Wrong HTTP method for this route. See the API reference.
too_many_uploads 429 2 of your libraries are already being prepared. Wait for one to finish, or cancel it, then upload again.
rate_limited 429 Too many requests. Slow down; retry after Retry-After.
busy 503 The service is at capacity, or your account already has a compound being scored. Retry after Retry-After.
catalog_updating 503 The target catalog is being updated. Retry in a few minutes.
predictions_not_ready 503 Target predictions aren't ready yet. Retry later.
filters_not_ready 503 Protein filters are still loading; search by text works. Retry after Retry-After, or drop keyword.
auth_unavailable 503 Key checking is unavailable. Retry after Retry-After.
billing_unavailable 503 Billing is unavailable. Retry after Retry-After.
unavailable 503 Another temporary outage. Retry after Retry-After, or in a minute.
backend_failed 502 A prediction failed. Retry; if it keeps failing, contact us.
internal_error 500 Something failed on our side. Contact us with the time of the request.
auth_fault 500 Key checking failed on our side. Carries incident_id. Contact us with the incident_id.
billing_fault 500 Billing failed on our side. Carries incident_id. Contact us with the incident_id.

When online payments are paused, insufficient_funds says so in message. Existing funds can still pay for screens, and results remain available.

Retrying safely

Retrying a GET is always safe. Retrying POST /api/v1/screens is safe only with the same Idempotency-Key as the first attempt: without one, every request is a new, charged screen. See REST with curl.

Errors through MCP

An agent sees the same codes, inside the tool's error text, which ends with the server's tools version: (Ptarmigan-1 tools version 0123456789ab), for example. The server also gives that version to the agent when it connects. If the two differ, the agent's list of tools is out of date, because we changed the tools after its conversation started: start a new conversation to load the current ones. See Using an agent.