Speech to text in Python: transcribe audio with timestamps
Speech to text in Python with Requests: send the audio URL, poll the job, then read the transcript and word timestamps. A script for Sume STT 1.0.

To do speech to text in Python without running a model on your own machine, call a hosted speech-to-text API with requests: POST the audio file's URL, poll the job until it finishes, then read the transcript and its timestamps from the JSON result. With Sume STT 1.0 that is POST /v1/stt-1.0/transcribe, then GET /v1/jobs/{id}/status until terminal is true, then GET /v1/jobs/{id}/result for text, words, and segments.
Sume documents a TypeScript SDK, @sume-com/sdk; from Python you call the HTTP API directly, here with Requests as its Quickstart shows. The STT facts come from the STT 1.0 schema in the Sume API reference, the OpenAPI document behind the API reference docs, and Jobs and results, all read on 2026-09-27. The submit-and-poll loop, its timeouts, and the retry key work as in Text to speech API in Python, so this post covers only what is specific to transcription.
How do I send an audio file from Python?
Send a link, not the file. The body's one required field is audio_url, a public HTTPS URL, and the schema has no field for file bytes, so the recording must already be at a public HTTPS address, such as your own storage; the API reference prefers a Sume media URL. It lists no accepted audio formats, and its examples point audio_url at .m4a and .wav files.
language_codeis an optional hint such asenorko. Omit it and the language is detected.duration_seconds(1–600) sizes the usage reservation. Omit it and Sume reserves for 1 minute.segmentation: {"mode": "sentence"}adds sentence segments to the result.
What does the whole script look like?
Submit, poll until terminal is true, then print the detected language, the transcript, and each word's start time.
import os, time, requests
AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
body = {"audio_url": "https://example.com/audio/interview.m4a",
"segmentation": {"mode": "sentence"}}
r = requests.post("https://api.sume.com/v1/stt-1.0/transcribe", json=body,
headers={**AUTH, "Idempotency-Key": "interview-001"}, timeout=30)
r.raise_for_status()
job = r.json()["data"]
while True:
s = requests.get(job["status_url"], headers=AUTH, timeout=30)
s.raise_for_status()
status = s.json()["data"]
if status["terminal"]:
break
time.sleep(status["next_poll_after_seconds"] or 2)
if status["sume_status"] != "completed":
raise RuntimeError(f"STT job ended as {status['sume_status']}")
res = requests.get(job["result_url"], headers=AUTH, timeout=30)
res.raise_for_status()
result = res.json()["data"]["result"]
print(result.get("language_code"), result["text"])
for w in result["words"]:
if w.get("type") != "spacing":
print(f"{w['start']:7.2f}s {w['word']}")How do I get timestamps for each word or sentence?
Word timestamps need no flag: words is always present on an STT result, ordered by start, with times in seconds from the start of the audio. An entry may carry a type such as word or spacing, which is why the script skips spacing. Sentence timestamps need segmentation in the request.
| You want | Add to the body | Read from the result |
|---|---|---|
| Word timestamps | Nothing | result["words"]: word, start, end |
| Sentence timestamps | "segmentation": {"mode": "sentence"} | result["segments"]: index, text, start, end, duration_seconds |
| The detected language | Leave out language_code | result["language_code"] and result["language_probability"], when available |
| The full transcript | Nothing | result["text"] |
Do I need to set the language?
No. Leave out language_code and STT 1.0 detects the language; the result then reports language_code and language_probability, the detection confidence, when available. If you already know the language, send it as a hint, such as ko for a Korean recording. Detect language from audio covers detection on its own, and Translate audio to English picks up from the transcript.
What are the limits, and what does it cost?
STT 1.0 costs $0.01 per audio minute on API pricing, plus a 5.5% agent fee by default. If your balance can't cover the estimate, the submit fails with 402 insufficient_credits and no job starts.
- Up to 10 minutes of audio per request, the documented maximum:
duration_secondsstops at 600. For a longer file, transcribe it in parts and add each part's start time to its timestamps, as Transcribe long audio files shows. - No speaker labels. In current code STT 1.0 runs with speaker separation (
diarize) off, and a request that sendsdiarizeortag_audio_eventsis rejected. - No microphone streaming: the job reads a file at a URL, and the Developer API has no SSE or WebSocket transport today.
wordsis capped at 20,000 entries. A 600-second transcript stays well under that, and a capped result says so withwords_truncated.
Sources
Related posts
More in Developers
- CORS error calling the Sume API from a browser: the fix
Browsers block direct calls from your site to api.sume.com, and API keys must never ship in frontend code. Call Sume from your server and proxy it.
- Sume API endpoints list: routes, scopes, idempotency
An index of the Sume API's public routes by family: which need no key, which scope each needs, where Idempotency-Key applies, and the post on each.
- Sume API error codes by surface: one index with next steps
Sume API error codes indexed by surface: common codes, paid generation, Formats, Scheduled runs, Agent Completions, media tools, and hosted MCP.
- Sume API glossary: Format run, spend cap, idempotency key
Sume API terms in one or two sentences each: Format, run, job, spend cap, idempotency key, wallet, agent fee, webhook, artifact, and more, with links.
Written by Sume