QuillAIQuillAIDocs
Sign in
GuidesPolling vs webhooks

Polling vs webhooks

Two ways to find out when a transcription is done. Pick polling to keep things simple, webhooks to keep things fast.

TL;DR

Use webhooks in production. Polling is perfectly fine for prototypes, one-off scripts, and CI jobs where a few extra seconds don't matter.

Side-by-side

AspectPollingWebhooks
Latency3–5s typical (you choose)Usually <1s after completion
Infra requiredNone — just an HTTP clientPublic HTTPS endpoint
ReliabilityYou control retriesUp to 5 retries with backoff
IdempotencyTrivial — same GET is safeMust dedupe on X-QuillAI-Delivery
Best forPrototypes, CLIs, CIProduction apps, real-time UX

Polling

Create a transcription, then GET /v1/transcriptions/{id} on an interval until status is one of completed, failed, or cancelled. You control the cadence — we don't rate-limit reasonable polling, but be kind.

poll.shbash
ID="trs_01HZX9K7Q2M4YV8BTA6JRN3PDE"
DELAY=3
while :; do
  RESP=$(curl -s https://api.quillhub.ai/v1/transcriptions/$ID \
    -H "Authorization: Bearer $QAI_KEY")
  STATUS=$(echo "$RESP" | jq -r .status)
  case "$STATUS" in
    completed|failed|cancelled) echo "$RESP" | jq .; break ;;
  esac
  sleep $DELAY
  DELAY=$(( DELAY < 10 ? DELAY + 2 : 10 ))
done

Tip: use jittered exponential backoff for long jobs (start at 3s, widen to 5s then 10s). Don't hammer the API at 100ms — you'll just burn round-trips.

Webhooks

Pass webhook_url when creating the transcription and we POST the finished Transcription object to that URL as soon as it's ready. See Webhooks for signature verification and retry rules.

create.shbash
curl -X POST https://api.quillhub.ai/v1/transcriptions \
  -H "Authorization: Bearer $QAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://youtu.be/dQw4w9WgXcQ",
    "webhook_url": "https://your-app.example.com/hooks/quillai"
  }'

When to use which

Use polling when…
  • You're writing a script, notebook, or CI job.
  • You don't have a public HTTPS endpoint to receive callbacks.
  • Latency doesn't matter — a few seconds of lag is fine.
Use webhooks when…
  • You're running a production app and need low-latency updates.
  • You want to free up workers instead of blocking on a poll loop.
  • You're processing jobs at scale and polling would multiply your API calls.

Hybrid pattern

Belt and braces: run webhooks as the primary path and add a cheap reconciliation poller as a safety net.

In practice, webhook delivery succeeds nearly every time — but networks get weird. After 5 failed retries we stop trying. A small cron that scans for jobs stuck in processing longer than an hour and GETs their current status will catch any orphans without adding real load.

reconcile.tstypescript
// Runs hourly via cron. Picks up any job whose webhook was abandoned
// after 5 delivery attempts and reconciles it with the API.
const stale = await db.jobs.findMany({
  where: { status: "processing", createdAt: { lt: oneHourAgo() } },
});

for (const job of stale) {
  const res = await fetch(`https://api.quillhub.ai/v1/transcriptions/${job.id}`, {
    headers: { Authorization: `Bearer ${process.env.QAI_KEY}` },
  });
  const t = await res.json();
  if (["completed", "failed", "cancelled"].includes(t.status)) {
    await db.jobs.update({ where: { id: job.id }, data: { status: t.status, result: t } });
  }
}

Pitfalls

  • Polling too aggressively — sub-second intervals add no real-world latency win and waste round-trips.
  • Not deduping webhook deliveries — always check X-QuillAI-Delivery and skip IDs you've already processed.
  • Exposing a non-HTTPS webhook URL. We refuse to deliver to http:// endpoints.