Async AI API Jobs and Webhooks: A Production Implementation Guide
Design reliable asynchronous AI jobs for image, video, audio, and long-running agent tasks using queues, polling, webhooks, and idempotency.

Async AI API Jobs and Webhooks: A Production Implementation Guide#
Image, video, audio, and long-running agent requests should not occupy a browser request until completion. An asynchronous AI API accepts a job, returns an ID, and lets the client poll or receive a webhook when the result is ready. The hard part is designing state transitions and retries so a duplicate notification never creates duplicate work or billing.
What is an async AI API job?#
An async job is a durable record with an input reference, provider request ID, status, timestamps, output location, error code, and usage metadata. Typical states are queued, running, succeeded, failed, cancelled, and expired. State transitions should be monotonic and protected by an idempotency key.
| Delivery method | Best for | Main concern |
|---|---|---|
| Polling | Simple clients | Extra requests and stale intervals |
| Webhook | Production integrations | Signature verification and retries |
| Queue worker | Internal pipelines | Backpressure and visibility |
| Streaming | Incremental text | Disconnect and partial output |
Create a job with a stable contract#
import os, uuid, requests
job_key = str(uuid.uuid4())
payload = {"model": "veo3", "prompt": "A sunset over a quiet harbor"}
response = requests.post(
"https://crazyrouter.com/v1/video/create",
headers={"Authorization": f"Bearer {os.environ['CRAZYROUTER_API_KEY']}",
"Idempotency-Key": job_key},
json=payload, timeout=20)
response.raise_for_status()
job = response.json()
print(job.get("id"))
Store your internal job before calling the provider, then update it with the upstream ID. If the client repeats the request with the same idempotency key, return the existing job rather than creating another one.
Polling and webhooks#
Polling should use exponential intervals and stop at an expiration deadline. A webhook handler must authenticate the sender, verify a timestamped signature, validate the payload, and enqueue processing before returning a success response. Process the event asynchronously. Providers may deliver the same event more than once or deliver events out of order, so compare event version or timestamp before updating state.
app.post("/webhooks/ai", verifySignature, async (req, res) => {
await events.insertIfNew(req.body.event_id, req.body);
await queue.publish("ai-webhook", req.body.event_id);
res.sendStatus(202);
});
Never trust a webhook URL or output URL supplied by a model. Allowlist domains, validate content type, limit download size, and scan files before exposing them to users. Store generated assets behind short-lived signed URLs.
Reconciliation is part of reliability#
Even a good webhook integration needs a reconciliation worker. Periodically find jobs stuck in running, compare them with the provider status endpoint, and repair missing transitions. Mark a job as expired only after a documented deadline; keep the upstream ID and last provider response for support. If a provider reports success but the asset download fails, separate generation state from delivery state so the system can retry the download without generating a second asset. This small distinction prevents duplicate costs and gives users accurate status information.
Expose a status endpoint that returns only the fields the caller is allowed to see: state, progress when trustworthy, safe error code, output reference, and timestamps. Do not return upstream credentials, internal URLs, raw provider errors, or another tenant's metadata. For long-running jobs, let users cancel queued work and make cancellation best-effort once generation has started. Record who requested cancellation and whether the provider confirmed it; a UI checkbox is not proof that compute stopped.
Pricing and job economics#
| Cost | Direct provider | Crazyrouter |
|---|---|---|
| Generation | Official model or second-based rate | Current usage-based rate by model |
| Queue and storage | Your infrastructure | Still your responsibility |
| Multi-model failover | Custom implementation | Centralized access can simplify routing |
| Fixed platform fee | Provider-dependent | No monthly fee or minimum consumption in documented plan |
Check Crazyrouter pricing. Add cost and provider request IDs to the job record so failed retries are visible in billing.
FAQ#
Should an AI job API use polling or webhooks?#
Polling is easiest for prototypes. Webhooks are more efficient for production integrations, provided signatures, retries, and duplicate events are handled.
How do I prevent duplicate generation?#
Use an idempotency key at your API boundary and persist the mapping before dispatching the provider request.
What if a webhook arrives before the job is saved?#
Use a durable event inbox, retry unknown jobs, or reconcile periodically from the provider's status endpoint.
How long should generated files remain available?#
Choose a retention period based on the product promise, then expire files and revoke or rotate signed URLs.
Can Crazyrouter handle video job workflows?#
Its documented API includes unified video creation and query endpoints. Confirm current model and webhook behavior in the documentation before production rollout.
Summary#
Async AI APIs need durable state, idempotency, authenticated webhooks, bounded retries, safe asset delivery, and cost records. Crazyrouter gives developers a unified model access layer; your queue and job contract make the workflow reliable.



