Skip to main content
The cloro API uses standard HTTP status codes and one error response format across all endpoints.

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)

A 429 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 a 502 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 on 500 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.
Give sync requests a client timeout of at least 5 minutes (300 s). Closing the connection does not stop an attempt cloro is already running: you lose its result, and the retry you send is a new request, billed if it succeeds.

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. If https://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:
A timeout or a connection reset from the command confirms a network-level block on your side. What to try, in order:
  1. Retry from a different network, for example a mobile hotspot instead of the office network.
  2. Ask your network administrator to allow the Cloudflare IP ranges and the hostnames cloro.dev and dashboard.cloro.dev.
  3. Set your resolver to a public one such as 1.1.1.1 or 8.8.8.8 to rule out DNS interception.
Everything except billing needs the dashboard only for setup. Once you hold an API key, all data operations run against 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 the X-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.

What’s the expected success rate for API requests?

Above 99% on average, varying with upstream provider stability, geographic region, and time of day. Handle the remainder like any 5xx: cloro’s automatic retries cover most, and exponential backoff on your side catches the rest.