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

HTTP status codes

Error response formats

Most errors use a nested error object:
Validation errors (400) instead use a flat shape with a per-field details array:
Internal errors (500) may arrive in either shape, so handle both — {"success": false, "error": "Maximum retries exceeded"} as well as the nested INTERNAL_SERVER_ERROR object.

Error codes

Authentication (401)

Permissions and credits (403)

Check your balance on sync requests via the X-Credits-Remaining header, or at any time — including from async-only workloads — via GET /v1/credits.

Rate limits (429)

Monitor endpoints (/v1/monitor/*) enforce your plan’s concurrency limit:
All endpoints (/v1/*) additionally enforce a 1,000/sec per-endpoint rate limit:
Watch X-RateLimit-Remaining and the concurrency headers to stay inside both. The rate-limit counter resets every second, so brief throttling clears it; sustained concurrency pressure needs a queue on your side.
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)

Retries and cancellation

cloro retries transient failures automatically, up to 10 attempts, stopping when the request succeeds or the attempts are exhausted. You do not need your own timeout logic. Still add exponential backoff on 500 and 502 — 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.
Canceled requests are charged. A 499 means you closed the connection, but the processing done before that point is billed — cloro charges for resources consumed, not just delivered results. Retries you issue are billed as new requests too.

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-side errors, 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.

Validation rules

Common questions

Why are some of my requests failing with 500 errors?

Upstream provider problems, a request that doesn’t match the API specification, or brief infrastructure disruptions. 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.

Can I read the request latency from the API response?

Yes. Every response carries the API’s processing time for that HTTP call in the X-Latency-Ms header. Async task processing time is a separate figure, reported as task.latencyMs. Neither includes network transit, so a wall-clock measurement around your call is always higher.

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 for the full lifecycle and the COMPLETED vs. upstream-error FAQ for how to detect degraded provider responses.

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.