Migrating here
From another transcription API or from your own open-model script: the concept map, what changes in your code, and what to check before you switch.
Two origins cover almost every migration: a transcription API you already use, or a script of yours running an open model. Both change little code; what really changes is where the decisions live.
Coming from another transcription API
The shape is the one almost every asynchronous API uses, so the translation is close to mechanical:
| What you have today | Here |
|---|---|
| Create a job with an audio URL | POST /v1/transcripts with url |
| Create a job with a file | POST /v1/uploads (raw body), then uploadId |
| Polling the job | GET /v1/transcripts/{id}, or ?wait=30 to stop asking |
| Completion callback | webhookUrl at creation |
| Speaker separation | speakers: true, 3 or "2-5" |
| Custom vocabulary / term boosting | glossary, { "what it hears": "what to write" } |
| Your id on the job | externalId (and metadata for the rest) |
| Timed words | words[], in decimal seconds |
| Subtitle-sized blocks | utterances[] |
Five differences that usually bite:
- Times are decimal seconds, never milliseconds.
1.5is one and a half seconds. A client that multiplies by 1000 produces subtitles a thousand times out of place, and the mistake only shows up in the player. - Money is in the currency's minor unit, with a fraction.
cost: 0.0167is a sixtieth of a dollar. Storing it in an integer column zeroes it. - The output is not deterministic and there is no
seed. The same file can come back slightly different. Do not diff runs: keep the transcript you delivered, whoseidis permanent. - One language per file. Detection is automatic, but it picks ONE language for the whole audio: there are no per-span language marks.
- No SDK. It is plain HTTP with the key in the
Authorization: Bearerheader, and the OpenAPI spec generates a client in your language if you want one.
Before you switch, check it with your own audio (not with a sample of ours): the dashboard playground puts a transcript from here next to the one you already have, on the same audio, with the sound playing along. It is the only comparison that answers for YOUR material.
Coming from your own open-model script
If you run the model on a machine of your own today, what changes is not the quality of the text: it is who carries the operations.
What leaves your side:
- The queue, and the retry when a machine dies mid-file.
- Turning GPUs on and off with demand (and paying for them idle).
- Decoding odd containers, truncated audio, files that are not audio.
- Diarization, punctuation and word alignment, each with its own model.
- Keeping all of that running while you work on something else.
What becomes yours:
- One HTTP call and a webhook (or
?wait=). - A published cost per audio hour, with no idle GPU.
The shortest path is to keep your script running and send the same batch here for a few days, comparing on your own material before switching anything off. A new account starts with enough credit for that.
Checklist
- Key on the server, never in a browser or a distributed app.
-
Idempotency-Keyon creation, keyed on the firstuploadId(why). - Branch on
error.retryableinstead of keeping your own retry table. - Keep
requestIdin the log for failures. -
webhookUrl(with signature verification) or?wait=instead of a polling loop. - Read
GET /v1/limitsinstead of hardcoding the ceilings. - Decide retention:
retentionDaysper job, if your material needs a period. - Check the estimated cost against the first real invoice: billing is by measured audio duration, not per file and not per request.
Retention and deletion
What is stored, for how long, how to delete it early, what DELETE actually erases, and who processes your audio.
List transcripts GET
Transcripts on the account, newest first. Items omit `text` and `words`; fetch a single transcript to get those. `since` and `until` cut by creation date in the database, so closing out one day of cost does not mean paging through the whole account: `?since=2026-08-27T00:00:00Z&until=2026-08-28T00:00:00Z`.