taskId, and the request runs in the background — like a tracking number rather than waiting at the counter. Submit tasks one at a time or in batches of up to 500. Use async when:
- Your application has short execution limits, typically in a serverless environment.
- You want to submit many requests quickly without waiting on each one.
- You need resilience that doesn’t depend on a single long-running connection.
Step 1: make an API request
Include awebhook.url and we notify you when the job is done; omit it and you poll for the result.
Identifying your requests with Generate unique keys using UUIDs, timestamps, or a combination of user ID + timestamp to ensure no duplicates.Failed task key retention: When a task returns
idempotencyKeyYou can optionally include an idempotencyKey in your request. This is a unique string you create that allows you to easily identify and reference your requests in your own system.The idempotencyKey must be a unique string across your entire account. If you submit a request with an idempotency key that has already been used, the API will return an error:FAILED, the idempotencyKey is not released — it stays bound for 24 hours, then auto-releases. To retry a failed task within that window, submit the same request with a new key.Serverless crash recovery: If your serverless function (Vercel, Lambda) crashes before you can save the taskId, you can poll GET /v1/async/task/{taskId} using the original idempotencyKey to retrieve the result — no resubmission and no double charge. Store the taskId as early as possible after receiving the submit response.taskId.
taskId and wait for the results.
Understanding task statesEvery async task goes through four possible states:
QUEUED: The request is received and waiting its turn to be processed. Tasks are processed by priority first, then in FIFO (first-in, first-out) order within the same priority level.PROCESSING: The request is actively being processed by our system. The AI provider is generating your response.COMPLETED: The request finished successfully. The final response is included in the same payload.FAILED: The request failed to complete. This can happen because of rate limits, provider errors, or invalid input.
QUEUED. You can track state transitions by polling the task status endpoint or receiving webhook updates.COMPLETED and FAILED tasks are stored in our system for 24 hours after completion. During this time, you can retrieve the full results using the task ID. After 24 hours, the task record and its associated response data are permanently deleted from our system.HTML URLs included in responses expire after 24 hours from generation, regardless of the task’s retention status.Step 2: receive the results
Two ways to retrieve a result. Either way, a finished task carriestask.latencyMs — the milliseconds from first pickup to the final outcome, excluding the initial queue wait. It is null while the task is QUEUED or PROCESSING, and stays null on a task that failed before processing ever started. On a retried task it covers every attempt, including the backoff between them, so it can be far longer than one provider call.
Option A: webhooks (recommended)
If you provided awebhook.url in your request, we will send an HTTP POST to that URL containing the full result as soon as the task reaches a terminal state.
See Webhooks for the payload shape, retry behavior, signature verification, and troubleshooting.
Option B: polling
Without a webhook URL,GET the task status endpoint with your taskId. Once the task completes, the response carries the full result.
Understanding limits
Task submission limits
At submission we check only two things:- Credit Limit: We verify you have enough credits for the task.
- Queue Limit: Your organization can have a maximum of 100,000 tasks waiting in the queue. If you exceed this, you will receive a
429 Too Many Requestserror. Please contact our team if you need this limit increased.
Task processing limits
Once your task is in the queue, our scheduler picks it up for processing. This is where your subscription’s concurrency limit is enforced. For example, if your plan allows 10 concurrent requests, our scheduler will process up to 10 of your tasks in parallel. Tasks are processed by priority first, then in the order they were received within the same priority level.Request prioritization
Omitting it leaves the task at 1, so existing integrations are unaffected. Monitor how your queue is distributed across levels via the async status endpoint.
For practical concurrency patterns and examples, see our
concurrency documentation.
Common questions
How do I cancel pending async tasks?
Individual queued tasks cannot be canceled bytaskId — once a task enters the QUEUED state, it stays there until it is either processed (COMPLETED / FAILED) or removed by a queue-wide clear.
To wipe every pending task in one call, use the DELETE /v1/async/queue endpoint. It removes all QUEUED tasks for your organization and returns the number cleared:
cURL
- Only
QUEUEDtasks are removed.PROCESSINGtasks are already in-flight on a worker and keep running — they cannot be recalled. COMPLETEDandFAILEDtasks are left in place and can still be retrieved viaGET /v1/async/task/{taskId}.- Queued tasks have not been charged yet, so clearing the queue does not refund or debit credits.
- The call is idempotent — re-running it on an empty queue returns
cleared: 0.
- Test with small batches first
- Use unique
idempotencyKeyvalues to prevent duplicate submissions - Implement safeguards in your submission logic
- Monitor your queue depth via the async status endpoint before submitting large batches
What’s the maximum queue depth?
Queue depth is limited to 100,000 tasks per organization. If you exceed this limit, you’ll receive a429 Too Many Requests error when trying to submit additional tasks.
If you need a larger queue for your use case, please contact our team.
How long do async tasks stay in the queue?
Tasks stay in the queue until they are processed, resulting in eitherCOMPLETED or FAILED status.
Check your current queue status using the async status endpoint.
How do I track the credits consumed by each task?
Both the polling response and the webhook payload include acredits object:
creditsToCharge— the estimated cost shown while the task isQUEUEDorPROCESSINGcreditsCharged— the actual amount billed once the task reachesCOMPLETEDorFAILED
creditsCharged from the terminal state to attribute cost per job. Failed tasks may still incur credits depending on how far processing got. Sync requests canceled by the client are also charged for work already done; async tasks cannot be canceled individually once queued, but you can wipe the whole pending queue in one call.
What happens to async tasks when my credits run out?
Credits are checked twice, and the balance is never reserved in advance:- At submission.
POST /v1/async/taskrejects with403 INSUFFICIENT_CREDITSwhen your balance does not cover that task’s cost.POST /v1/async/task/batchinstead returns200with a per-taskINSUFFICIENT_CREDITSerror for each task that doesn’t fit, so a partially affordable batch is partially accepted. - At scheduling. Tasks already sitting in
QUEUEDare re-checked when the scheduler picks them up. A task that no longer fits the balance moves toFAILEDwithcreditsCharged: 0and anINSUFFICIENT_CREDITSerror — it is not held, retried, or resumed when you top up. Re-submit those tasks after topping up.
GET /v1/credits reflects completed charges only — it does not net out creditsToCharge for work still queued or processing. To decide whether a submission will fit, subtract your own outstanding creditsToCharge from remaining rather than trusting remaining alone.
A task came back COMPLETED but the result looks like an upstream error. What happened?
COMPLETED means cloro finished its work and returned what the upstream provider gave us. If the provider returned an error page, a captcha, or a truncated answer, that detail lives inside the response payload. Inspect the response body — FAILED is reserved for cases where cloro could not produce a result at all (network errors, internal exceptions, repeated upstream timeouts).
Does async cost more credits than sync?
No. The credit cost per request is identical whether you use synchronous or async delivery. ThecreditsCharged field in the terminal task state shows the same cost you would see from a sync call for the same endpoint and parameters.
The async endpoint feels slow today. What should I check?
CallGET /v1/async/status to see your account’s queue depth and concurrency usage. If the queue is deep and concurrency is saturated, throughput is plan-bound — see concurrency for how to raise it.