Hear2Text

Polling

There are no webhooks in v1. This is how to wait for a recording without wasting requests or missing the finish.

Why there are no webhooks yet

v1 has no callbacks. A webhook is a delivery guarantee — retries, signatures, replay protection, an endpoint we keep trying for hours — and shipping half of one is worse than shipping none, because an integration would trust it. Polling is boring and it works. Webhooks are the first thing on the list after v1.

Nor does a recording created through the API send the push notification or the “your transcript is ready” email the apps’ recordings do: an integration creates too many for either to be anything but noise in somebody’s inbox.

The four statuses

StatusMeaning
queuedAccepted. Waiting for a machine, or fetching your URL.
processingBeing transcribed right now.
completedFinished. Segments and exports are available.
failedIt will not finish. failure_reason says why.

These four words are the whole vocabulary, on purpose: our pipeline has more states than this internally, and an integration that branched on those would break the day we split a job in two. completed and failed are terminal — nothing moves after them.

The progress field

GET /transcriptions/{id} carries a progress object. Use it to show a bar; do not use it to decide whether the work is done — status is the only thing that says that.

A recording being worked on
{
  "id": "0f2f5f6a-2c6c-4f4e-9d1e-5d8f2a1b3c4d",
  "status": "processing",
  "duration_seconds": 3742,
  "progress": { "step": "transcribe", "percent": 46, "indeterminate": false },
  "failure_reason": null
}
  • percent is 0–100 when we can measure it, and null when we cannot.
  • indeterminate is true for the stretches with no measurable progress — show a spinner rather than a bar stuck at a number.
  • step is a coarse name for what is happening. Treat it as a label to display, not a value to branch on: the steps may change within v1.

The listing endpoint reports a lighter progress — the step only. Per-recording progress is a question about one recording, so ask it about one recording.

Recommended intervals

Transcription takes a fraction of the recording’s length, so a sensible schedule is:

  • First check after 10 seconds. Nothing finishes faster than that, and polling immediately only costs you a request.
  • Then every 5 seconds while the recording is short (under ten minutes).
  • Then back off — double the interval up to a ceiling of 30 seconds — for anything longer.
  • Give up after twice the recording’s length plus ten minutes and alert a human. A recording that has not moved by then is a support conversation, not a longer wait.

At one request every 5 seconds a single recording uses 12 requests a minute out of 120. That leaves room for 10 concurrent waits; if you run more than that, back off sooner. See rate limits.

A polling loop

Respect Retry-After if you ever meet a 429, and treat a 5xx as a reason to wait rather than to fail.

# A shell loop, for completeness -- in production this belongs in your application.
until [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; do
  sleep 5
  STATUS=$(curl -s "https://heartotext.com/api/public/v1/transcriptions/$ID" \
    -H "Authorization: Bearer $H2T_API_KEY" | jq -r .status)
done

When it fails

failure_reasonWhat happened, and what to do
no_speechWe found nothing to transcribe. Check the file has audio. Retrying will not help.
too_shortThe recording is shorter than we can work with.
quota_exceededThe account had no allowance left when it came to be processed. Retry after the allowance resets.
internalOur fault. Retry once; if it fails again, send us the recording’s id.