Claude Opus 5.5 is live on SeedRouter

Whisper-1 API: Your First Audio Transcription

Send a recording to Whisper-1, save the task ID, and download text or subtitles. Follow a Python example with file checks, timestamps and live pricing.

Read as Markdown

A first Whisper-1 API request needs an audio file, an explicit model ID and an output format. On SeedRouter, you upload the recording, save the returned task ID, then download the transcript when that task completes. Start with a short recording whose words you already know: it makes missing words and unusual spellings easier to notice.

This guide follows SeedRouter's asynchronous contract. It is not a drop-in synchronous OpenAI SDK example. The Whisper-1 model page has the Playground and live price; the API reference lists every field and the outstanding compatibility checks. [1]

Prepare one recording

Use speech you have permission to process. A short WAV or MP3 is a useful starting point. The current upload limit is 26,214,400 bytes; the file must contain readable audio with a measurable duration. A .mp3 extension does not make a renamed text file valid audio. The reference separates the documented nine input formats from those that passed live checks. [1]

The recording below is original synthetic English speech, made for this tutorial. Its waveform appears on the cover. The quoted sentence on the cover comes from the recording script, rather than an accuracy claim.

Download the 10.31-second sample. The script says: “This is a sample recording for an audio transcription tutorial. Our meeting starts at nine thirty on Monday. Please bring the project notes and review the final checklist before we begin.”

Keep an API key in a server-side environment variable. Do not put it in a public webpage, mobile bundle or shared screenshot. The Python example below uses requests and reads SEEDROUTER_API_KEY from the environment.

Submit the Whisper-1 API request once

Choose json for a first request. You can then read the downloaded artifact's text field without handling subtitle timecodes. The multipart file part must contain actual bytes; supplying a URL as its value is not equivalent to uploading the file. Let the client set the multipart content type and boundary. [1]

Save this as transcribe.py. It writes a submission marker before the POST and saves the accepted task ID afterward. Running it again reads that existing task. If the POST's outcome is uncertain, the marker stops the script from creating an accidental duplicate.

import json
import os
from pathlib import Path

import requests

base = "https://api.seedrouter.ai/v1"
headers = {"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"}
state = Path("whisper-task.json")
audio = Path("recording.wav")

if not state.exists():
    if not 0 < audio.stat().st_size <= 26_214_400:
        raise ValueError("Choose a nonempty recording within the upload limit")
    with state.open("x") as marker:
        json.dump({"status": "submission_unconfirmed"}, marker)
    with audio.open("rb") as recording:
        response = requests.post(
            f"{base}/audio/transcriptions",
            headers=headers,
            files={"file": (audio.name, recording, "audio/wav")},
            data={"model": "whisper-1", "response_format": "json"},
            timeout=180,
            allow_redirects=False,
        )
    response.raise_for_status()
    accepted = response.json()
    task_id = accepted.get("id")
    if not isinstance(task_id, str) or not task_id.startswith("task_"):
        raise RuntimeError("Submission could not be confirmed; check task history")
    state.write_text(json.dumps({"id": task_id}), encoding="utf-8")

saved = json.loads(state.read_text(encoding="utf-8"))
if "id" not in saved:
    raise RuntimeError("Check task history before submitting another recording")

response = requests.get(
    f"{base}/tasks/{saved['id']}", headers=headers, timeout=30,
    allow_redirects=False,
)
response.raise_for_status()
task = response.json()
print(task["status"])
if task["status"] == "completed":
    result = requests.get(task["output"]["url"], timeout=60)
    result.raise_for_status()
    print(result.json()["text"])
elif task["status"] == "failed":
    print(task["error"])

Run python transcribe.py. If it prints processing, wait several seconds and run the same command again. It will read the saved ID instead of uploading again. Save a separate state file for each intentionally new recording. A client timeout alone does not establish a task failure.

Read the completed transcript

The accepted response contains id, model, status and created_at. Completion adds output.url and output.format. That URL points to the result file; it is not a media-generation task that requires another paid request. A GET downloads the artifact. [1]

Read the words while listening to the recording. Check names, numbers and sentence boundaries before using the text as captions or an exact quote. A transcript is a draft of what was spoken, and timestamps do not remove the need to review it.

Local end-to-end checks on October 19, 2026 completed all five transcription formats and an English-input translation. Those checks establish delivery and billing behavior for the tested recordings. They do not establish an accuracy percentage or translation quality across languages.

Choose text, timestamps or subtitles

Your next stepChooseRead the downloaded file as
Put the words into an applicationjsonJSON; start with text
Edit a plain transcripttextPlain text
Import captions into an editorsrt or vttSubtitle text
Align individual words or segmentsverbose_jsonJSON with requested timestamps

For word timestamps, send response_format=verbose_json and a repeated multipart field named timestamp_granularities[] with value word. Add another part with value segment if you need both. In Python requests, use a list of (name, value) pairs for repeated fields. Each timestamp is in seconds. [2]

Word timestamps and speaker labels are different outputs. Whisper-1 does not turn a meeting into a speaker-labelled transcript merely because you request segments. It also does not stream partial text: stream is ignored for this model. Leave chunking_strategy omitted for the verified path; explicitly selecting auto did not pass the live compatibility check. [1]

Add language and spelling context when needed

An optional language=en tells the transcription request that the input is English. Omit it when you want detection. If a product name is easy to mishear, place its correct spelling in prompt, using the same language as the audio. Whisper prompting provides context; it does not behave like a chat instruction that can reliably order a particular layout. [3]

Keep context relevant to the clip. Whisper considers only the last 224 tokens of its own tokenizer. Sending a long document does not give the recording an unlimited glossary, and counting characters is not a reliable substitute for that token rule.

For English translation, change the endpoint to /audio/translations. Its five request fields are file, model, prompt, response_format and temperature. Translation produces English; it has no target-language selector. Use an English prompt if you supply one. [4]

Check cost before extending the workflow

The live table below uses measured audio duration. A longer transcript does not increase the duration charge for the same recording. Each file's duration is rounded up to whole seconds, then converted into audio metering units. The API reference includes the exact calculation, and the Playground shows an estimate after it can read the recording duration. Failed requests are not charged.

ModelUSD per minuteUSD per million audio metering units
whisper-1$0.036$36

For each recording: units = round(ceil(duration in seconds) × 1000 / 60). Multiply these audio units by the live unit rate. These are duration units, not transcript tokens. Both transcription and English translation use this rule. Failed requests are not charged.

Before processing many files, validate one representative recording and inspect the result. Keep each recording's task ID so you can recover from a connection interruption by reading its existing state. See the full parameter reference before adding subtitles or less-tested format combinations.

Questions from a first integration

Can I use the OpenAI Python SDK example unchanged?

No. The multipart input fields are familiar, but SeedRouter returns an asynchronous task ID. A synchronous client that expects the POST response to contain text needs adaptation. The example here makes the submission and result read explicit.

Why do I have no word timestamps in my JSON result?

The default json format is a text-oriented response. Request verbose_json and the word timestamp granularity together. Segment timing alone does not create one entry per word.

Should I send another POST after a timeout?

First check the saved task ID or task history. The response may have been lost after acceptance. Reading an existing task avoids creating another request for the same recording.

Is Whisper-1 being retired?

OpenAI has announced a February 26, 2027 shutdown date for Whisper on its own API. That notice does not establish a shutdown date for this service. Keep lifecycle notices in your maintenance checklist when building a new integration. [5]

Sources

[1] SeedRouter Whisper-1 API reference and verification status, checked October 19, 2026.

[2] OpenAI transcription reference.

[3] OpenAI Whisper prompting guide.

[4] OpenAI translation reference.

[5] OpenAI transcription model deprecation notice.

Related guides