Error catalogue
Every failure carries a stable code from this
closed set. Branch on the code, never on the message — messages are written for humans and may be reworded.
The shape
Errors are RFC 9457 problem documents
(application/problem+json) with two additions: a machine-readable code, and a
remedy saying what the caller can do.
{
"type": "https://docs.picorank.com/api/errors/slot_limit_exceeded",
"title": "Keyword slot limit reached",
"status": 402,
"code": "slot_limit_exceeded",
"detail": "Adding 40 keywords would need 120 slots; 35 remain.",
"remedy": "Remove keywords or scan combinations, or add slots via the add-on.",
"meta": { "used": 465, "limit": 500, "would_add": 120 },
"request_id": "req_01J8X…",
"documentation_url": "https://docs.picorank.com/api/errors/slot_limit_exceeded"
}
In GraphQL the same information arrives in extensions on the error, with the same
code, remedy and meta — so one error-handling path serves both transports.
Not every empty answer is an error
Four codes describe data, not failure, and arrive with HTTP 200 in a
notice field beside the data. A domain that has never been scanned is not a 404, and returning an
empty list without explanation would be indistinguishable from "every keyword dropped out of the results" — a
very different fact. These are the codes in the informational family below.
Always log request_id. It identifies the exact request in our logs, and quoting it turns a support
thread into a lookup.
Loading the catalogue…