# Whisper-1 API: Your First Audio Transcription

By SeedRouter · Published 2026-10-19 · Updated 2026-10-19

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](https://seedrouter.ai/models/whisper-1) has the Playground and live price; the [API reference](https://seedrouter.ai/docs/whisper-1) lists every field and the outstanding compatibility checks. <sup><a href="#ref-1">\[1]</a></sup>

## 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. <sup><a href="#ref-1">\[1]</a></sup>

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.

<audio preload="none" src="https://static.seedrouter.ai/media/blog/20261019-whisper-1-api/first-transcription.wav" aria-label="Original English speech for the transcription tutorial" />

[Download the 10.31-second sample](https://static.seedrouter.ai/media/blog/20261019-whisper-1-api/first-transcription.wav). 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. <sup><a href="#ref-1">\[1]</a></sup>

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.

```python
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. <sup><a href="#ref-1">\[1]</a></sup>

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 step                     | Choose         | Read the downloaded file as    |
| ---------------------------------- | -------------- | ------------------------------ |
| Put the words into an application  | `json`         | JSON; start with `text`        |
| Edit a plain transcript            | `text`         | Plain text                     |
| Import captions into an editor     | `srt` or `vtt` | Subtitle text                  |
| Align individual words or segments | `verbose_json` | JSON 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. <sup><a href="#ref-2">\[2]</a></sup>

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. <sup><a href="#ref-1">\[1]</a></sup>

## 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. <sup><a href="#ref-3">\[3]</a></sup>

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. <sup><a href="#ref-4">\[4]</a></sup>

## 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.

| Model | USD per minute | USD 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](https://seedrouter.ai/docs/whisper-1) 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. <sup><a href="#ref-5">\[5]</a></sup>

## Sources

<p id="ref-1">
  \[1] 

  <a href="/docs/whisper-1">SeedRouter Whisper-1 API reference and verification status</a>

  , checked October 19, 2026.
</p>

<p id="ref-2">
  \[2] 

  <a href="https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create">OpenAI transcription reference</a>

  .
</p>

<p id="ref-3">
  \[3] 

  <a href="https://cookbook.openai.com/examples/whisper_prompting_guide">OpenAI Whisper prompting guide</a>

  .
</p>

<p id="ref-4">
  \[4] 

  <a href="https://developers.openai.com/api/reference/resources/audio/subresources/translations/methods/create">OpenAI translation reference</a>

  .
</p>

<p id="ref-5">
  \[5] 

  <a href="https://developers.openai.com/api/docs/deprecations#2026-08-26-transcription-models">OpenAI transcription model deprecation notice</a>

  .
</p>
