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.