Skip to content
javinfo

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

StatusMeaning
400Bad 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.
401Missing or invalid API key.
402Not enough credit. Retry-After is an hour, a hint for automated clients; a top-up unblocks you at once. See billing.
404No 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.
429Too many requests: the per-minute cap for your account, or the hourly budget on live providers. Wait out Retry-After.
503A pinned provider (providers=) is over its hourly budget. Unpin it, or wait out Retry-After.
499The 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.

On this page