Transcribe long audio files: limits, chunking, speed math
Long audio breaks naive transcription setups in three places: the file cap on a single request, the streaming session cap almost nobody publishes, and the client that tries to hold hours of audio in memory. This guide puts the duration and session limits of nine speech APIs and this platform in one dated table, answers the 30-minute speed question with a measured real-time factor instead of a human-typist ratio, gives you runnable chunking code for batch and streaming. The same spine then covers text output: model output caps, the continuation pattern, and the character caps on speech synthesis.
Rates for this platform render from the live catalog in the table further down, never typed by hand. The one worked cost formula names its variables against that table. Speed math is different: real-time factor is a measured time ratio, so those numbers stay literal.
Three walls break long audio
The first wall is the request cap. Every speech API bounds what one request may carry, in bytes, in seconds, or both. OpenAI's file transcription endpoint takes files up to 25 MB. Anything larger, their guide says, gets split or compressed, cutting at sentence boundaries rather than mid-sentence (official guide). Others bound duration instead of bytes. Either way, the recording you actually have, the two-hour meeting, the lecture, the deposition, does not fit in one call on most services.
The second wall is the session cap, and it is worse because it is usually invisible. Batch transcription of a finished file fails loudly at the request cap. A live stream that has been healthy for an hour fails by going silent or dropping the socket. Almost no vendor prints the number where that happens. This guide's provider table is the core asset. Part of the reason is that several of those numbers had to be dug out of FAQ pages, quota pages, and error-code tables rather than feature pages.
The third wall is on your side of the wire. Even a local model that "handles long audio" is chunking internally. Whisper, the reference open model, has a 30-second receptive field. Anything longer must go through one of its two long-form algorithms, a sequential sliding window or independent chunks stitched at the boundaries (model card). Long documents hit their own walls on the parsing side; the same split-and-rejoin discipline applies there, and our merged-cell PDF table guide covers that lane.
Audio in and text out are the same problem twice. The rest of this guide walks the audio leg first, then mirrors every move on the text leg: output caps, continuation, and synthesis input caps.
How long does it take? Real-time factor, with real numbers
The pages currently ranking for "how long does it take to transcribe 30 minutes of audio" answer a different question. They time a human typist, with ratios around four to one. For machine transcription the honest unit is real-time factor, or RTF: seconds of compute per second of audio. An RTF below 1.0 means the system outruns the recording; 0.5 means one hour of audio takes about 30 minutes to process.
This platform's speech-to-text lane (model id hayamimi-stt) has a measured effective RTF of about 0.39, taken 2026-08-29 on a 20.32-second speech clip. That clip ran about 8.0 seconds of wall time from audio start to the last final transcript. The first partial text arrived 342 ms after audio start when audio was pushed ahead of realtime, and 1284 ms when paced at 1x. Its round-trip character error rate measured 0.0000 the day before, on a small internal 111-character English reference at concurrency 1 and 4. Those are clean-audio, single-clip conditions, not a promise about your conference audio.
The arithmetic you came for: 30 minutes of audio at RTF 0.39 is about 30 x 0.39, or roughly 11.7 minutes of processing, serially, on one request stream. Two qualifiers matter. First, a 30-minute file exceeds this lane's per-request ceiling, so it arrives as chunks anyway. Process those chunks on parallel requests, and the wall time approaches the longest chunk, not the total. Second, RTF is workload-dependent: concurrent requests, queueing, and hard audio move it. Treat any single RTF number, ours included, as a planning figure you re-measure on your own audio. If you would rather own the GPU than rent the lane, the Ornith local vs hosted guide works that tradeoff for language models. The same logic transfers.
The per-provider limit table nobody ranks with
Every row below was read from the provider's own documentation on 2026-09-11. Where a vendor documents no limit, the cell says undocumented with the check date, which is itself an answer.
| Provider | Batch file limit | Streaming session limit |
|---|---|---|
| OpenAI | 25 MB per file; split or compress longer audio (guide) | undocumented (checked 2026-09-11) |
| Google Cloud STT v2 | synchronous 10 MB or 1 minute; batch up to 8 hours per file, Cloud Storage only (quotas) | 5 minutes per stream; official endless-streaming reconnect pattern |
| Azure Speech | fast transcription hard limits under 500 MB and under 5 hours per file (quotas); diarized batch 240 minutes per file (docs) | no general session cap documented; the real-time diarization variant is capped at 240 minutes per session (quotas) |
| Amazon Transcribe | 4 hours or 2 GB per job (FAQ) | up to 4 hours per connection (same FAQ) |
| AssemblyAI | 5 GB or 10 hours per file (FAQ) | auto-closes at 3 hours; billed for open-connection time (docs) |
| Deepgram | 2 GB per file; processing beyond 10 minutes can 504, 20 minutes on their Whisper tier (docs) | no total-session cap published; closes after 10 seconds with no audio or keep-alive (docs) |
| Gladia | 135 minutes and 1000 MB (4 h 15 m enterprise); vendor suggests about 60-minute chunks (docs) | 3 hours per WebSocket session, terminated at the limit (same page) |
| Speechmatics | batch under 1 GB per file in the request body, larger by URL fetch (docs) | sessions auto-end at 48 hours, 1 hour without audio, or 3 minutes without audio or pings (docs) |
| Soniox | undocumented (checked 2026-09-11) | 300 minutes per session, fixed, not increasable (docs) |
| This platform | about 240 s effective per request, 25 MiB, WAV/FLAC/Ogg Opus | long-form streams chunked around an approximately 100 s edge wall |
Three honest readings of the table. First, the spread is enormous. A five-minute Google stream and a 48-hour Speechmatics session are both "streaming STT." Second, the session column is where documentation goes to die. OpenAI publishes nothing, Azure documents a cap only for its diarization variant, and many vendors that publish bury the number in FAQ and quota pages. Third, limits are documented facts, but accuracy on long audio is not. When a community benchmark of a dozen speech APIs met its own commenters, the loudest requests were for what it lacked: non-English languages, local models, timestamp accuracy. A self-hosted long-file user still reported repetitions and hallucinations at boundaries. Limits you can plan around; quality on your audio you must test.
When to chunk yourself, and how to cut
The decision law is short. Under the caps, send the whole file: one request, one bill, no boundary risk. Over the caps, chunk. And when accuracy at boundaries matters more than speed, prefer cuts at silence.
Three cutting patterns cover the field. Fixed windows are the simplest: cut every N seconds, accept that you will sometimes slice a word in half. Silence-aware cutting is better: detect pauses and cut there. That is what Whisper's own long-form tradeoff formalizes. A sequential sliding window is slightly more accurate, up to about 0.5% WER on their numbers, while independent chunks with a small overlap are faster for a single long file (model card). Overlapped windows are the insurance policy: re-cut with a few seconds of overlap around a suspect boundary and keep the cleaner reading.
Two mechanics bite everyone eventually. Timestamps: a chunk's transcript is relative to the chunk, so add the chunk's start offset to every in-chunk timestamp before you merge. And mid-sentence cuts degrade accuracy, which is OpenAI's own documented advice; cut on silence, never on a fixed tick alone.
A chunked batch client that runs
This client takes any input file, converts it to 16 kHz mono WAV (the format the lane decodes natively), and cuts at silence near a target length below the per-request ceiling. It then loops the multipart call, printing each piece's duration as the response reports it.
# pip install pydub requests (pydub needs ffmpeg on PATH)
import os
import requests
from pydub import AudioSegment
from pydub.silence import detect_nonsilent
API = os.environ["IRONSTRATUM_BASE_URL"] + "/v1/audio/transcriptions"
HEADERS = {"Authorization": "Bearer " + os.environ["IRONSTRATUM_API_KEY"]}
def split_on_silence(path, target_ms=200_000, max_ms=230_000,
min_silence=700, thresh_db=-40):
"""Yield (offset_ms, wav_bytes) pieces under the request ceiling.
Cuts at silences near target_ms; falls back to fixed max_ms windows
where a stretch has no usable silence, so no piece can exceed the
effective per-request ceiling."""
audio = (AudioSegment.from_file(path)
.set_frame_rate(16_000).set_channels(1).set_sample_width(2))
def emit(start, end):
pieces, at = [], start
while end - at > max_ms: # fixed-window fallback
pieces.append((at, audio[at:at + max_ms].export(format="wav")))
at += max_ms
pieces.append((at, audio[at:end].export(format="wav")))
return pieces
pieces, cut = [], 0
for start, _end in detect_nonsilent(audio, min_silence_len=min_silence,
silence_thresh=thresh_db):
if start - cut >= target_ms: # next cut lands on a pause
pieces.extend(emit(cut, start))
cut = start
pieces.extend(emit(cut, len(audio)))
return pieces
for offset_ms, wav in split_on_silence("meeting.wav"):
r = requests.post(
API, headers=HEADERS, timeout=600,
files={"file": ("chunk.wav", wav.read(), "audio/wav")},
data={"model": "hayamimi-stt", "language": "en"},
)
r.raise_for_status()
body = r.json() # {"text": ..., "duration": ...}
print(f"[{offset_ms/1000:8.1f}s] {body['duration']:6.1f}s {body['text']}")
Contract facts that save an afternoon, from this platform's own API contract. The language field is required in practice on this lane. A missing or non-dedicated code returns a terminal 502 that a retry with the same value will hit again, so fix the value, do not retry it. Containers are WAV, FLAC, or Ogg Opus, up to 25 MiB, with an effective ceiling near 240 seconds per request. Unknown form fields are a 400. A failed chunk retries on its own. Each request is metered on its own audio seconds, and the Idempotency-Key header carries no replay semantics on this route, so deduplication is your job.
Streaming: event shapes, and the 100-second wall
Batch and streaming answer different needs. Batch is for finished recordings: accuracy first, no live consumer, simple costs. Streaming is for live audio: captions, voice agents, anything waiting on the words as they land. If nothing is waiting, use batch.
This lane's streaming face is the same endpoint with stream=true: the response becomes server-sent events. transcription.partial events carry draft text while speech continues, transcription.final closes a segment, and one transcription.refine per session re-emits the first final's text with the auto-detected language code. The stream starts with a : ping keep-alive line and ends with a terminal data: [DONE]. The input must be raw PCM or WAV at 16 kHz 16-bit mono; other formats are a 400. A short silence after events is a clean end, not an error.
import json
import os
import requests
# the streaming leg accepts only raw PCM/WAV at 16 kHz 16-bit mono;
# convert first if your clip is not, then send the converted file:
# ffmpeg -i clip.wav -ar 16000 -ac 1 -sample_fmt s16 clip16k.wav
with open("clip16k.wav", "rb") as audio, requests.post(
os.environ["IRONSTRATUM_BASE_URL"] + "/v1/audio/transcriptions",
headers={"Authorization": "Bearer " + os.environ["IRONSTRATUM_API_KEY"]},
files={"file": audio},
data={"model": "hayamimi-stt", "stream": "true"},
stream=True, timeout=300,
) as resp:
for line in resp.iter_lines():
if line.startswith(b":"):
continue # keep-alive ping, not an event
if not line.startswith(b"data: "):
continue
payload = line[6:]
if payload == b"[DONE]":
break # terminal marker, clean end
event = json.loads(payload)
kind = event["type"]
if kind == "transcription.final":
print("FINAL:", event["text"])
elif kind == "transcription.refine":
print("detected language:", event["lang"])
# partials: draft text, safe to display or ignore
Now the wall. Long-form streams on this platform are chunked around an approximately 100-second edge wall. Sustained speech does not ride one uninterrupted session; it is carried in chunks, and your client should treat a chunk boundary as a normal event, not a failure. The number is stated as approximate because it is an edge timeout, not a published product feature. There is also a WebSocket face, a 101-upgrade endpoint designed for the speaker-labeled streaming lane, and it is the honest example of this whole section. That lane was dropped from the catalog on 2026-09-08 because its checkpoint emitted no per-speaker output fields. It restores only when a checkpoint emits real per-speaker fields, passes a three-speaker end-to-end case, and the roughly 220-second WebSocket cap is resolved or chunked sessions exist. The full story, including what ships today, is in our real-time speaker diarization guide.
One metering note that matters for long sessions: this lane bills audio seconds summed from final-event durations, so silence does not bill. At least one major vendor in the table above bills the full time its WebSocket stays open, closed session or not, which is a different contract for the same meeting.
The text mirror: output caps and continuation
The same walls exist on the way out. A chat model's output is capped per request. The response's finish_reason field is null while content streams and settles on the final chunk. A value of length means the model stopped because it hit the output cap, not because it finished the thought. The standard pattern is continuation, and it is generic craft rather than a platform feature. Take the partial text, send it back as context, ask the model to continue exactly where it left off, and stitch the pieces, trimming the duplicated joint.
Two parameters control the cap on this platform's OpenAI-compatible endpoint: max_completion_tokens is the canonical name and max_tokens is honored identically as the legacy name. And output lives inside the context window together with the prompt. The catalog's chat lanes span 100,000 to 1,310,720 tokens of context per model, so a long-document question with a small output cap is usually a budgeting decision, not a hard wall. Set the output cap explicitly when length matters, and when you need more text than one request returns, continuation is the move. For endpoint conventions generally, see the OpenAI-compatible API guide. For chunking documents as inputs rather than outputs, the PDF to Markdown for RAG guide covers the ingestion side.
Long text into speech: character caps and splitting
Text-to-speech lanes cap input length too, and the caps are small enough to surprise anyone who has fed a model a whole article. This platform's four speech lanes, measured and enforced at the gateway with a 400 rejection before any synthesis work: chatterbox-tts 400 input characters, kokoro-tts 3500, pocket-tts 2500, audio8-tts 250.
The reason is physics, not policy. These lanes synthesize the whole request before the first audio byte crosses, so each lane carries a first-byte wall enforced at the gateway: chatterbox-tts 45 seconds, kokoro-tts 55, pocket-tts 55, audio8-tts 60. A request that cannot reach its first byte inside its wall dies at the edge. The operations monitor watches the same budget with alert bars at 45, 55, 50, and 60 seconds, pocket-tts's bar sitting five seconds under its enforced wall. Splitting long text is therefore the client's job. Cut at sentence boundaries under the cap, one request per piece, join at playback, keeping voice settings identical so the seams stay quiet. There is no SSML input on this platform's speech body, so sentence and character boundaries are the levers you have. The economics of running these lanes at volume, including the crossover math against your own GPU, is its own guide: the TTS local vs API break-even guide. Its FAQ question on very long text covers chunking, caps, and streaming in more depth at its Q7.
What a long job costs, and what bounds it
Rates for this platform's lanes render below from the live catalog; the formula reads its one variable, P_stt, straight off the speech-to-text row. The fence is the only place on this page a price appears.
| Model | Context | $/1M in | $/1M out | $/1M cached |
|---|---|---|---|---|
| Qwen | ||||
| qwen3.8-27b | 262K | $0.35 | $2.55 | $0.105 |
| qwen3.6-35b | 131K | $0.11 | $0.8 | $0.044 |
| Minimax | ||||
| minimax-m2.7 | 197K | $0.24 | $0.95 | $0.072 |
| Muse | ||||
| muse-glimmer-30b | 131K | $0.28 | $1.2 | $0.084 |
| Ornith | ||||
| ornith-1.5-35b | 100K | $0.35 | $2.55 | $0.105 |
| ornith-1.5-9b | 100K | $0.1 | $0.3 | $0.03 |
| Glm | ||||
| glm-5.3-flash | 1049K | $0.11 | $0.35 | $0.033 |
| Deepseek | ||||
| deepseek-v4-flash-0731 | 1311K | $0.15 | $0.42 | $0.045 |
| Gemma | ||||
| gemma-4-31b-it | 262K | $0.22 | $0.49 | $0.066 |
| Deepseek | ||||
| deepseek-v4-pro | 1000K | $1.13 | $2.21 | $0.339 |
| Chatterbox | ||||
| chatterbox-tts | — | $25/1M chars | ||
| Kokoro | ||||
| kokoro-tts | — | $15/1M chars | ||
| pocket-tts | — | $16/1M chars | ||
| Audio | ||||
| audio8-tts | — | $8/1M chars | ||
| Hayamimi | ||||
| hayamimi-stt | — | $0.6/audio-hr | ||
| Bge | ||||
| bge-m3 | — | $0.05/1M tokens | ||
| bge-reranker-v2-m3 | — | $1.5/1k searches | ||
| Whisper | ||||
| whisper | — | $0.25/audio-hr | ||
cost of a job = H x P_stt
H = audio hours in the job, the sum of your chunks
P_stt = the per-audio-hour rate from the fence above
The shape is linear, so the sensitivity rule is one line: cost moves exactly pro rata with audio hours and with the rate. Nothing else in this guide's mechanics, chunking, overlap, retries, stream versus batch, changes it, because metering follows reported audio seconds, not wall time. One retry nuance: each chunk meters its own audio, and a retried chunk bills again (no replay semantics). Deduplication in a retry loop is worth writing deliberately.
The hard bound is the wallet. This platform meters against a prepaid wallet, and a key whose balance reaches zero is denied at request time. The worst case of a chunking bug is therefore the balance you loaded, not an open-ended bill. Long jobs are exactly where runaway spend hurts; the failure modes and the guard patterns are their own guide: how to avoid API bill shock.
Decision rules
| Your situation | The rule |
|---|---|
| File under every cap in the table | Send it whole. One request, no boundary risk. |
| File over the cap, boundaries forgivable | Fixed windows, the simple loop, parallel requests for wall time. |
| File over the cap, accuracy matters | Silence-aware cuts with a small overlap; re-read suspect seams. |
| Live consumer waiting on words | Stream, and plan the reconnect around the session cap before the first long meeting. |
| Model output keeps hitting the length cap | Set the output cap explicitly; continuation: partial back, continue, stitch. |
| Narration longer than the lane's char cap | Sentence-boundary split under the cap, one request per piece, join at playback. |
The lane this guide measured is behind one OpenAI-compatible endpoint. Current per-unit rates sit on the pricing page, and a wallet plus an API key takes minutes on the signup page. Run the chunker on your own recording, your own language, your own room, and let the measured numbers decide.
Frequently asked questions
How can I transcribe a long audio file?
Pick your row from the limit table: if the file fits under the duration and size caps, send it in one request. If it does not, split it client-side, cut at silences near a target length, and loop the transcription call per chunk. Add each chunk's start offset to any timestamps before merging. The python client earlier in this guide does exactly this here, and the same shape works against any provider in the table with the endpoint and limits swapped. Send a language code on this lane, and do not retry a 502 with the same one.
How long does it take to transcribe 30 minutes of audio?
For a human typist, the ranking answers say roughly four times the audio length. For a machine, the answer is the real-time factor: processing time equals audio length times RTF. This platform's lane measured an effective RTF of about 0.39 on a 20.32-second clean English clip in August 2026. Thirty minutes of similar audio is therefore about 30 x 0.39, roughly 11.7 minutes, serially. Chunk the file and run the pieces concurrently and the wall time drops toward the longest single chunk. Re-measure on your own audio; RTF moves with content and load.
What's the max file size / duration per provider?
The table above, every row read from provider documentation on 2026-09-11. OpenAI: 25 MB. Google: 10 MB or 1 minute synchronous, 8 hours batch, 5 minutes per stream. Azure: fast transcription hard limits of 500 MB and 5 hours, 240 minutes diarized batch. AWS: 4 hours or 2 GB batch, 4 hours per stream. AssemblyAI: 5 GB and 10 hours batch, 3 hours per session. Deepgram: 2 GB batch, with a 10-minute processing timeout risk. Gladia: 135 minutes and 1000 MB, 3 hours per realtime session. Speechmatics: 1 GB batch, 48-hour realtime sessions. This platform: about 240 seconds per request and 25 MiB. OpenAI publishes no streaming session cap, and Azure documents one only for its diarization variant. These numbers move; re-verify against the vendor's own page before you commit an architecture.
When do I chunk audio myself vs send the whole file?
Send whole when the file clears every cap: it is simpler, cheaper to reason about, and avoids boundary errors entirely. Chunk when any cap is exceeded, and choose the cut by what you can afford. Silence-aware cuts when accuracy matters (pauses are where sentences end), fixed windows when throughput matters, and a small overlap when you cannot afford a sliced word. Merge with offsets, and expect the occasional boundary artifact even on good stacks. The community evidence in this guide includes a self-hosted long-file user who still sees repetitions and hallucinations at boundaries.
Streaming vs batch STT: when is each right?
Batch when the audio is finished and nobody is waiting: archives, voicemail backlogs, media libraries. It is the accuracy-first mode, it is simpler to retry, and costs map to audio duration. Streaming when a consumer needs words as they are spoken: live captions, voice agents, call analytics in the moment. Streaming adds obligations batch does not have: a session cap to plan around (from 5 minutes at Google to 48 hours at Speechmatics across the table), reconnect handling at that cap, and partial-versus-final event handling. On this platform there is also strict input formatting at 16 kHz mono PCM or WAV. A common production shape is both: stream live, and batch the recording afterward for the archival transcript.
How long can a streaming STT session run before it drops?
Per the providers' own documentation: Google 5 minutes per stream, and their own tutorial shows the reconnect loop. AssemblyAI auto-closes at 3 hours, Gladia terminates at 3 hours, Soniox at 300 minutes, and AWS allows 4 hours per connection. Speechmatics auto-ends sessions at 48 hours, 1 hour without audio, or 3 minutes without audio or pings. Deepgram closes 10 seconds after audio stops, with keep-alive messages to hold it open. OpenAI publishes no session cap for live transcription, and Azure documents 240 minutes only for its real-time diarization variant, both as of 2026-09-11. This platform chunks long-form streams around an approximately 100-second edge wall, so long sessions are carried as successive chunks and your client handles boundaries as routine. Whatever the number, the engineering is the same: assume the session ends on its own, keep audio buffered to replay across a reconnect, and re-establish rather than error.
How do I split long TTS text: sentence, chars, or SSML?
By sentence, under the character cap. This platform's measured input caps are chatterbox-tts 400 characters, kokoro-tts 3500, pocket-tts 2500, and audio8-tts 250, enforced with a 400 before synthesis starts. Its speech body has no SSML field, so sentence and character boundaries are the available levers. Cut at sentence ends below the cap, keep voice and settings identical across requests so the join stays inaudible, and concatenate at playback. The TTS break-even guide covers this in depth at its Q7, including the first-byte walls that explain why the caps exist.
Rates and metering units for every lane sit on the pricing page. To run the numbers on real audio: sign up, create a key, load a small wallet balance, and point the chunker above at your longest recording.
Last verified: 2026-09-11