Hear2Text

Creating transcriptions

Creating a recording from a file or a URL, what we accept, and the limits that bind.

Creating a recording

POST /transcriptions takes exactly one source: a file or a url. Sending neither is a 422. Both fields, and the optional language and title, can be sent as multipart form data or — for a URL — as JSON.

FieldTypeNotes
filefileAudio or video, multipart only. Required without url.
urlstringA public http(s) link. Required without file.
languagestringAn ISO code from GET /languages. Omit to auto-detect.
titlestringYour own name for it, up to 255 characters.

Always send an Idempotency-Key. A create is the one request here that costs money if it happens twice.

From a file

The upload is part of the request, so give your HTTP client a generous timeout — a large file over a slow link takes as long as it takes. We accept anything ffmpeg can read: m4a, mp3, wav, flac, ogg, aac, mp4, mov, mkv, webm and the rest.

curl https://heartotext.com/api/public/v1/transcriptions \
  -H "Authorization: Bearer $H2T_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F file=@interview.wav \
  -F title="Interview with Dr Yilmaz" \
  --max-time 1800

From a URL

Send a link and we fetch it ourselves — public video and audio hosts, podcast feeds’ episode files, and any plain http(s) file. Three things are refused:

  • Playlists. Send one recording at a time; a playlist link is refused as a 422.
  • DRM-protected sites. We do not break content protection.
  • Private addresses. Anything that resolves to a local or internal host is refused, both when you send it and again immediately before we fetch it.

A URL that turns out to be unfetchable produces a recording that reaches status: "failed" rather than an error on the create — we cannot know until we try.

Languages

Omit language and we detect it. Send an ISO code when you already know it: it is faster and it removes the one thing detection can get wrong, which is a recording that opens in a different language from the one it is mostly in. GET /languages lists every code we accept.

GET /languages
{ "data": [ { "code": "ar", "name": "Arabic" }, { "code": "de", "name": "German" }, { "code": "en", "name": "English" } ] }

Limits

  • 512 MB per upload. A larger file is refused as a 422, or as a 413 when the body is too large to reach the application at all.
  • 6 hours per recording on a paid plan. GET /me reports the exact figure for your account.
  • A daily allowance across all recordings, and a ceiling on how many may run at once. Both are on the rate limits page.

Listing recordings

GET /transcriptions returns the recordings this key can see, newest first, with limit (up to 1000), cursor and an optional status filter.

curl "https://heartotext.com/api/public/v1/transcriptions?status=completed&limit=50" \
  -H "Authorization: Bearer $H2T_API_KEY"

Paging is by cursor everywhere, never by page number. next_cursor is opaque: pass it back unchanged and do not try to construct one.

Deleting a recording

DELETE /transcriptions/{id} removes the audio, the transcript and the embeddings, and answers 204. It is permanent and there is no undelete. A recording that has not finished — still queued or processing — answers 409 transcription_still_processing, so wait for it to complete or fail first.