Docs /
Async tasks and batches
Task lifecycle
| Status | Meaning |
|---|---|
QUEUED | Waiting to start, with no time limit. Not charged; can be cleared. |
PROCESSING | Running. Has ten minutes from its first start to finish. |
COMPLETED | response holds the engine's result. Charged. |
FAILED | response holds the error. Not charged. |
task.latencyMs runs from the first start to the final outcome, excluding the time queued.
A queued task stays QUEUED until it can start; clear the queue if you no longer need it. A task that started but did not finish within ten minutes
fails with Maximum retries exceeded, uncharged.
A task can go from PROCESSING back to QUEUED and start again. Treat only
COMPLETED and FAILED as final.
Creating a task
POST /v1/async/task takes the engine's request as payload and answers at once with the task. Invalid bodies get 422.
priority orders your own queue: higher runs first, equal priorities in submission order, and other accounts are unaffected.
-
taskType"AIMODE" | "GOOGLE" | "GOOGLE_NEWS" | "GEMINI" | "CHATGPT" | "COPILOT" | "PERPLEXITY" | "GROK" required -
Engine that should handle this task.
-
payloadobject required -
Request body specific to the engine. Needs at least
prompt(queryfor Google Search). -
priorityinteger -
Priority of the task, from 1 to 10; tasks with higher numbers run first. Default: 1.
-
idempotencyKeystring -
Distinct string that stops the same task from being created twice. No two tasks in your account may share it: the key stays bound while its task is kept (4 to 5 days after it finishes), and a failed task frees it 24 hours after failing.
-
webhookobject -
Settings for the webhook that reports a finished task.
1 property
-
webhook.urlstring required -
Endpoint that gets the webhook POST request once the task finishes.
-
Batches
POST /v1/async/task/batch takes 1 to 500 tasks. Each is validated and admitted on its own, and results answers each by its
index in input order. Only an overflowing queue rejects the whole batch (429 QUEUE_LIMIT_EXCEEDED). The SDKs refuse a batch outside that
size before sending it.
curl -X POST "https://api.answerline.dev/v1/async/task/batch" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '[{"taskType":"CHATGPT","payload":{"prompt":"iPhone 17 or Galaxy S26?","country":"US"},"idempotencyKey":"run-0-US-2026-09-15"},{"taskType":"CHATGPT","payload":{"prompt":"iPhone 17 or Galaxy S26?","country":"GB"},"priority":5,"idempotencyKey":"run-0-GB-2026-09-15"}]'
const prompts = ["iPhone 17 or Galaxy S26?", "Best wireless earbuds under $150"];
const tasks = prompts.flatMap((prompt, i) =>
["US", "GB"].map((country) => ({
taskType: "CHATGPT" as const,
payload: { prompt, country },
idempotencyKey: `run-${i}-${country}-2026-09-15`,
})),
);
const batch = await client.createBatch(tasks);
for (const item of batch.results) {
if (!item.success) console.warn(tasks[item.index].idempotencyKey, item.error.code);
}
prompts = ["iPhone 17 or Galaxy S26?", "Best wireless earbuds under $150"]
tasks = [
{"taskType": "CHATGPT", "payload": {"prompt": p, "country": c}, "idempotencyKey": f"run-{i}-{c}-2026-09-15"}
for i, p in enumerate(prompts)
for c in ("US", "GB")
]
batch = client.create_batch(tasks)
failed = [r for r in batch["results"] if not r["success"]]
{
"success": true,
"summary": {
"total": 2,
"succeeded": 1,
"failed": 1
},
"results": [
{
"success": true,
"index": 0,
"task": {
"id": "b27a21e1-7c39-4aa2-a347-23e828c426f9",
"taskType": "CHATGPT",
"status": "QUEUED",
"priority": 1,
"createdAt": "2026-09-15T09:00:00.000Z",
"latencyMs": null,
"idempotencyKey": "run-0-US-2026-09-15"
},
"credits": {
"creditsToCharge": 0.0015,
"creditsCharged": null
}
},
{
"success": false,
"index": 1,
"error": {
"code": "RESOURCE_ALREADY_EXISTS",
"message": "…",
"timestamp": "2026-09-15T09:00:00.000Z"
}
}
]
}
Item errors are VALIDATION_ERROR, RESOURCE_ALREADY_EXISTS and INSUFFICIENT_CREDITS.
Getting results
GET /v1/async/task/{id} returns task, credits and, once the task has finished, response.
waitForTask / wait_for_task poll it with jittered backoff from 500 ms up to 10 seconds between polls (intervalMs /
interval) for up to 16 minutes (timeoutMs / timeout). A task still queued then keeps its place: the wait
ends with a timeout error, and you can wait again. For many tasks, add a webhook instead and let results come to you; see
webhooks.
A finished task stays readable for at least four days after it finished. Store what you need from a result rather than treating the API as your archive.
GET /v1/async/tasks lists your tasks newest first, a page of summaries at a time: status filters by
QUEUED, PROCESSING, COMPLETED or FAILED, limit sets the page size (50, at most 100), and a
full page's nextBefore is the next page's before. The SDKs do not wrap it yet — call it directly.
Idempotency keys
A task that ends FAILED keeps its key for 24 hours after it failed, then releases it: resubmitting the same key before that answers
409, and after it creates a new task under the key. The key of a task that succeeded, or is still queued or running, stays bound while the task is
readable.
An idempotencyKey is unique across your account. Creating a task with a key in use answers 409 RESOURCE_CONFLICT, or
RESOURCE_ALREADY_EXISTS for a batch item, and creates nothing. Derive keys from what the task means, such as prompt, market and day, never at random
per attempt: then retrying a submission after a timeout can never run a task twice. A request whose every task is refused, for example for
INSUFFICIENT_CREDITS, creates nothing and claims no key, neither its tasks' idempotencyKey nor an Idempotency-Key
header, so the same key can be sent again, with any body, once you have topped up.
Managing the queue
GET /v1/async/status reports queuedTasks, processingTasks, queued counts per priority and concurrency in use.
DELETE /v1/async/queue deletes every task still QUEUED, which were never charged, leaves running and finished tasks alone, and returns how
many it removed; it is safe to repeat. A queue too long to clear in one call answers 202 with clearing: true:
the rest is deleted without further calls, and GET /v1/async/status shows the queue shrink.
curl -X GET "https://api.answerline.dev/v1/async/status" \
-H "Authorization: Bearer $API_KEY"
curl -X DELETE "https://api.answerline.dev/v1/async/queue" \
-H "Authorization: Bearer $API_KEY"
const status = await client.asyncStatus();
const { cleared } = await client.clearQueue();
status = client.async_status()
cleared = client.clear_queue()["cleared"]
Sync or async
A synchronous call is one request that waits up to ten minutes and costs the same as a task. Set the HTTP client's read
timeout above that. If the wait runs out first, the error's details.taskId names the submitted task — fetch its outcome with
GET /v1/async/task/{id} instead of submitting again and paying twice. A task
waits in the queue instead of failing, and can be batched. Use synchronous calls for interactive work and tasks for anything scheduled or large.