Telemetry read API
Errors
One error envelope, and what each code means.
Every error has the same three fields, plus details on validation failures.
{
"error": "This API key cannot read telemetry",
"code": "FORBIDDEN",
"timestamp": 1788566031707
}| Field | Type | Description |
|---|---|---|
error | string | A human-readable sentence. Do not match on it; it may be reworded |
code | string | The stable machine-readable code. Switch on this |
timestamp | integer | Milliseconds since the Unix epoch, at the moment of the failure |
Codes
| Status | code | Cause | Fix |
|---|---|---|---|
| 400 | VALIDATION_ERROR | A query parameter is malformed — a non-integer limit, a from that is not ISO 8601 | Fix the parameter |
| 400 | INVALID_CURSOR | The cursor is malformed, belongs to a different query, or was edited | Restart the listing without a cursor |
| 401 | AUTHENTICATION_ERROR | No Authorization header, or an invalid or expired key | See authentication |
| 403 | FORBIDDEN | The key lacks permissions.telemetry, or asked for a core outside permissions.cores | Use a key with the permission |
| 404 | NOT_FOUND | No such session or run in this organization, or none this key may read | Check the id, and the key's core scope |
| 413 | PAYLOAD_TOO_LARGE | An ingestion body over 10 MB. Reads cannot produce this | — |
| 429 | RATE_LIMITED | Over 120 reads per minute | Wait Retry-After seconds |
| 500 | DATABASE_ERROR | The query failed inside Subcore | Retry; if it persists, report it |
| 500 | INTERNAL_ERROR | An unexpected failure | Retry; if it persists, report it |
Handling them
Retry 429 and 5xx; do not retry 4xx. A 400, 401, 403 or 404
means the request itself needs to change, and sending it again unchanged will
fail identically. On a 429, wait the number of seconds in Retry-After.
Switch on code, not on error. The sentences are written for a human
reading a log and are not part of the contract.
INVALID_CURSOR means start over. There is no way to repair a rejected
cursor. Drop it and re-page from the beginning, narrowing the time window if you
need to avoid re-reading.
if (!res.ok) {
const { code } = await res.json();
if (code === "RATE_LIMITED") {
await sleep(Number(res.headers.get("Retry-After") ?? 1) * 1000);
return retry();
}
if (code === "INVALID_CURSOR") return restartWithoutCursor();
throw new Error(code);
}