Errors
HTTP status codes and the error body returned by the javinfo API.
Failed requests are never billed. You pay for 200 only.
Status codes
| Status | Meaning |
|---|---|
400 | Bad request. Input validation failed (missing parameter, value out of range, bad enum), a /query or /idols request has neither q nor a filter, or the provider id is unknown. |
401 | Missing or invalid API key. |
402 | Not enough credit. Retry-After is an hour, a hint for automated clients; a top-up unblocks you at once. See billing. |
404 | No result. Returned by /movie, /random, /idols/{id}, /discover/similar, the /analytics/idols/* lookups, and /resolve, /resolve/provenance and /resolve/streams. |
422 | /query only: no provider supports that filter/sort pair, the pinned provider doesn't, or you paged past 50 without a q. See Filtering. |
429 | Too many requests: the per-minute cap for your account, or the hourly budget on live providers. Wait out Retry-After. |
503 | A pinned provider (providers=) is over its hourly budget. Unpin it, or wait out Retry-After. |
499 | The client closed the request before a provider responded. |
/query never returns 404. No match is a 200 with count: 0 and an empty results
array, and costs nothing — check count. Named lookups (/idols/{id}, /discover/similar,
resolve) return 404 when the code or performer is missing.
Error body
Handler errors use oRPC's envelope, carrying defined, code, status and message:
{
"defined": false,
"code": "NOT_FOUND",
"status": 404,
"message": "no title found for \"ZZZZ-99999\""
}A 400 adds data.issues, one per failed rule:
{
"defined": false,
"code": "BAD_REQUEST",
"status": 400,
"message": "Input validation failed",
"data": {
"issues": [
{
"expected": "string",
"code": "invalid_type",
"path": ["q"],
"message": "Invalid input: expected string, received undefined"
}
]
}
}422: same envelope, status: 422, code: "BAD_REQUEST":
{
"defined": false,
"code": "BAD_REQUEST",
"status": 422,
"message": "does not support sort \"rating\""
}Gate rejections, before a handler runs, are shorter:
{ "error": "invalid api key" }No key at all passes the gate and is refused by the handler, with the oRPC UNAUTHORIZED body:
{
"defined": false,
"code": "UNAUTHORIZED",
"status": 401,
"message": "Unauthorized"
}The per-minute limiter uses the short shape too: { "error": "rate limit exceeded" }.
Don't assume code exists — branch on the status and read the body.
Backing off
429, 402 and 503 carry Retry-After in seconds. Respect it:
if ([429, 402, 503].includes(res.status)) {
const wait = Number(res.headers.get('Retry-After') ?? 1);
await new Promise((r) => setTimeout(r, wait * 1000));
}For 402, the hour is advisory. If a human is watching, top up and retry now.