Async tasks and batches
Task lifecycle
| Status | Meaning |
|---|---|
QUEUED | Waiting for a concurrency slot. Not charged; can be cleared. |
PROCESSING | Running. Transient failures are retried automatically within five minutes of the first start. |
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.
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.
| Field | Type | Description |
|---|---|---|
taskType * | "AIMODE" | "GOOGLE" | "GOOGLE_NEWS" | "GEMINI" | "CHATGPT" | "COPILOT" | "PERPLEXITY" | "GROK" | The AI provider to use for this task. |
payload * | object | Provider-specific request payload. Must include at least `prompt` (or `query` for Google Search). |
priority | integer | Task priority level (1-10). Higher numbers are processed first. Defaults to 1. |
idempotencyKey | string | Unique string to prevent duplicate task creation. Must be unique across your account. |
webhook | object | Webhook configuration for task completion notification. |
webhook.url * | string | URL to receive the webhook POST request when the task completes. |
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":"Best CRM for small agencies","country":"US"},"idempotencyKey":"crm-0-US-2026-09-15"},{"taskType":"CHATGPT","payload":{"prompt":"Best CRM for small agencies","country":"GB"},"priority":5,"idempotencyKey":"crm-0-GB-2026-09-15"}]'const prompts = ["Best CRM for small agencies", "Best CRM for real estate teams"];
const tasks = prompts.flatMap((prompt, i) =>
["US", "GB"].map((country) => ({
taskType: "CHATGPT" as const,
payload: { prompt, country },
idempotencyKey: `crm-${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 = ["Best CRM for small agencies", "Best CRM for real estate teams"]
tasks = [
{"taskType": "CHATGPT", "payload": {"prompt": p, "country": c}, "idempotencyKey": f"crm-{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": "crm-0-US-2026-09-15"
},
"credits": {
"creditsToCharge": 5,
"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 10 minutes (timeoutMs / timeout). For many tasks, add a webhook instead and let results come to you; see
webhooks.
Idempotency keys
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.
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.
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 five minutes, costs the synchronous surcharge and needs a free concurrency slot. A task costs the base price, waits for a slot instead of failing, and can be batched. Use synchronous calls for interactive work and tasks for anything scheduled or large.