Skip to main content
Set webhook.url on a task and querying.ai POSTs the result there when the task reaches COMPLETED or FAILED.

Delivery payload

Each delivery is a POST with Content-Type: application/json and this body: For example, a completed Perplexity task (Perplexity costs 1 credit):
A failed task still delivers:
Prices per task type are on Credits and limits. With include.citationAnalysis, the answer’s webhook waits until the attached analysis finishes, so you get both in one delivery. See Citation Analysis.

Verify the signature

Tasks submitted with an API key are signed with that key. Each delivery carries three headers: The HMAC key is the 32-byte SHA-256 digest of the API key that submitted the task (for a batch, the key that created the batch). Compute the signature over the raw body before you parse it, compare in constant time, and accept timestamps within five minutes:
A delivery is unsigned only when the task has no API key, or for a batch whose API key was revoked more than 30 days earlier. If every task you create comes from an API key, reject requests without a valid signature.
After you rotate a key, tasks submitted with the new key are signed with the new key. Deliveries for tasks submitted earlier are still signed with the key that submitted them.

Retries

Return any 2xx within 30 seconds to acknowledge. Any other status, a timeout or a connection error is a failed attempt. Failed deliveries are retried after 2, 4, 8 and 16 minutes: five attempts in all over about 30 minutes, each with the same querying-webhook-id. After the fifth failure the delivery is abandoned. Redirects are not followed: a 3xx counts as a failed attempt. Delivery runs separately from the task. A task whose webhook never arrives still shows COMPLETED on GET /v1/async/task/{id}. To recover, poll within the 24-hour window described in Async tasks.

URL rules

webhook.url is checked at submit. A URL that breaks a rule is rejected with 400 VALIDATION_ERROR on the webhook.url field. At delivery time the host must resolve only to public addresses, or the attempt fails. The connection goes to the address that was checked.

Write a safe handler

1

Verify, then acknowledge fast

Check the signature, store the payload durably, and return 200. Parse and write to your database afterwards, so a slow handler does not hit the 30-second timeout and cause a retry.
2

Deduplicate

Delivery is at least once. Skip a message whose querying-webhook-id you have already processed, or match on task.idempotencyKey and make the write idempotent.
3

Branch on status

Check task.status explicitly. A failed task also delivers a webhook, with response.error.

Batch completion event

A batch created with POST /v1/batches and a webhook.url sends each task’s result to that URL as above, then posts one completion event when every item has finished:
The event is signed with the batch’s API key, its querying-webhook-id is batch_<batch id>, and it follows the same retry schedule.

Next steps

Async tasks

Statuses, polling and retention.

Batches and grids

One webhook URL for thousands of tasks.