HTTP status codes
Error response formats
Every error uses the same envelope:error is always an object. Branch on error.code; message is for a human reading a log, and details appears only when there is extra context. When a request body fails validation, details lists each failing field.
Error codes
Authentication (401)
Permissions and credits (403)
INSUFFICIENT_CREDITS (Insufficient credits) is the only 403 an API key gets; keys have no per-key permissions. Check your balance in the X-Credits-Remaining header of a successful sync response, or at any time — including from async-only workloads — via GET /v1/credits.
Rate limits (429)
A429 carries one of three codes:
QUEUE_LIMIT_EXCEEDED also carries details.limit; RATE_LIMIT_EXCEEDED has no details. No 429 sends Retry-After, so choose the backoff from the code. Sustained concurrency pressure needs a queue on your side, or the async API, rather than retries. See Rate & concurrency limits for both limits, and the response headers that show how close you are to them.
Batch endpoint errorsThe batch task creation endpoint uses a partial success model. Individual tasks can fail with per-task error codes (
VALIDATION_ERROR, RESOURCE_ALREADY_EXISTS, INSUFFICIENT_CREDITS) inside a 200 response. See the batch endpoint documentation for details.External service (502)
Link resolution returns a502 when the link does not resolve:
Retries and cancellation
cloro retries a failed attempt automatically, sync and async alike: up to 5 attempts, fewer on some providers. A sync request starts no new attempt after 5 minutes, but an attempt already running can still finish, so a slow request can take several minutes. Still back off exponentially on500 and 502, and wait the Retry-After seconds on a 503: automatic retries cover transient failures, and a client-side retry adds a second layer for the ones that get through. If they persist, check the status page and contact support.
Empty responses from AI providers
Occasionally a request completes normally —200 on sync, COMPLETED on async — but the
provider returned no usable answer: a short canned message instead of content, and no sources.
Safety refusals, age-restricted topic refusals, provider error messages, truncated answers, and
account notices all arrive this way. It is upstream behavior, and no parameter prevents it.
The automatic retries above do not cover it — an empty response is a successful delivery of what
the provider returned, so detecting and retrying it is a client-side decision.
Detect it structurally: sources is empty AND the answer body is very short (under ~160
characters). Do not match the message text — it is localized across a dozen or more languages,
and providers reword and translate it, including their own product names.
Retry once when you detect one. Most categories are not tied to your prompt, so the same prompt
commonly succeeds on the next attempt. The exception is age-restricted and policy topics such as
alcohol or gambling: those refusals track the subject of the prompt, so retrying converts far
fewer of them and mostly spends credits.
Rates vary by provider and shift as each one updates its models and policies — see the provider’s
endpoint page for measured figures, for example
Gemini.
The dashboard does not load
The dashboard, the docs, and the landing page are served through Cloudflare. Ifhttps://dashboard.cloro.dev times out, hangs on a blank page, or never gets past the sign-in screen, the block is between your network and the Cloudflare edge, not in a cloro service. National filtering, a corporate firewall, a VPN split-tunnel rule, and DNS interception all produce this pattern.
Confirm it before you report it:
- Retry from a different network, for example a mobile hotspot instead of the office network.
- Ask your network administrator to allow the Cloudflare IP ranges and the hostnames
cloro.devanddashboard.cloro.dev. - Set your resolver to a public one such as
1.1.1.1or8.8.8.8to rule out DNS interception.
api.cloro.dev and are unaffected.
Validation rules
Common questions
The dashboard will not load. Is cloro down?
No. The dashboard sits behind Cloudflare, so a network that blocks the Cloudflare edge takes out the dashboard alone. See the dashboard does not load.Why are some of my requests failing with 500 errors?
Usually every automatic attempt failed (Maximum retries exceeded), or the 5-minute retry window ran out (Maximum timeout (5 minutes) exceeded). Handle them with the backoff described in retries and cancellation.
A request succeeded but the answer is empty. Is that a bug?
No — the provider returned a short canned message instead of an answer, and we returned it as received. See empty responses from AI providers for detection and retry guidance.Where do I see logs for successful requests?
Every monitor request — sync and async — is logged to the dashboard with the request id, provider, prompt, country, status code, credits charged, and latency. Successful (2xx) requests show up alongside failures so you can audit usage end-to-end. Entries typically appear within a minute; if a request seems missing, refresh — heavy load occasionally delays ingestion but does not drop entries.
Is there a bulk export of request logs?
No. The dashboard shows logs in a paginated view only — there is no CSV export or bulk download. For log analysis at scale, log theX-Request-ID response header on every API call alongside your own metadata at the point of call. This gives you a permanent audit trail keyed to cloro’s internal request IDs for support lookups.
An async task returned 200 OK but the result looks wrong. What does that mean?
200 OK from GET /v1/async/task/{taskId} only confirms the task record exists. The task’s outcome lives inside task.status — see task states. A COMPLETED task can still carry a provider refusal or error text; see empty responses from AI providers to detect it.