Stepfun Asr
Transcribe audio with StepFun's stepaudio-3-asr-max — an SSE endpoint (NOT /v1/audio/transcriptions), single call handles long audio with no client-side chunking. Use when transcribing Chinese / English audio with StepFun, when long-form recordings (5-30 min) need to land in one request, when migrating from step-asr / step-asr-1.1 / stepaudio-2.5-asr, or when hitting the misleading `model stepaudio-3-asr-max not supported` error (which actually means wrong endpoint). Triggers on 阶跃 ASR, StepFun ASR, stepaudio-3-asr-max, stepaudio-2.5-asr, 转录, 语音识别, 长音频转写, 语音转文字. For TTS with the sibling stepaudio-3-tts model, use the stepfun-tts skill instead.
- Skill ID
- daymade/claude-code-skills/stepfun-asr
- Publisher
- daymade
- Repository
- claude-code-skills
- Installs
- 0
- Files
- 6
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
daymade/claude-code-skills/stepfun-asrInstalls these files- SKILL.md
- references/api_reference.md
- references/known_issues.md
- scripts/asr_file.py
- scripts/asr_transcribe.py
- scripts/check_params.py
What this skill tells the agent
StepFun stepaudio-3-asr-max
Transcribe audio with StepFun's stepaudio-3-asr-max (StepAudio 3, released 2026-09-15, verified 2026-09-16; supersedes stepaudio-2.5-asr on the same endpoint). Long audio in one call, no chunking — but only if the request hits the right endpoint with the right body shape. The wrong endpoint returns an error that looks identical to "model doesn't exist", which is the #1 reason this skill exists.
Companion: for TTS withstepaudio-3-tts(the sibling model), use thestepfun-ttsskill — they share an API key but live on different endpoints with different body shapes.
Why this skill exists — three traps that cost hours
- Wrong endpoint, wrong error.
stepaudio-3-asr-maxdoes not live on/v1/audio/transcriptions(that endpoint serves the olderstep-asrfamily). It lives on/v1/audio/asr/sse— SSE streaming, JSON body, base64 audio. Sending it to the wrong endpoint returns{"error":{"message":"model stepaudio-3-asr-max not supported"}}, which is identical in structure to a genuinely nonexistent model name. People waste hours filing whitelist tickets.
- Plan key vs Normal key, silent failure. StepFun's "Plan" subscription keys (cheap, text-only) cannot call audio endpoints, but the failure manifests as a 4xx with no auth-shaped error message. If your account has a Plan subscription, you need a separate "Normal" key from the same console.
- SSE error events are real. Censorship can fire on the ASR side too (rarely). Don't assume only
transcript.text.deltaandtranscript.text.doneevents arrive — handletype: errorevents in the stream or you'll silently drop them.
Config and auth
API key resolves in this order (fail-fast, no defaults):
$STEPFUN_API_KEYenvironment variable${CLAUDE_PLUGIN_DATA}/config.jsonwith{"api_key": "..."}(cross-session persistence)
First-time setup:
mkdir -p "${CLAUDE_PLUGIN_DATA}" && cat > "${CLAUDE_PLUGIN_DATA}/config.json" <<EOF
{"api_key": "<paste Normal key here>"}
EOFIf the user has not set a key, ask them to paste it — do not guess or use a placeholder. Get keys at https://platform.stepfun.com/ → API Keys. Use a Normal key, not a Plan key.
Quick start — single file
python3 scripts/asr_transcribe.py /path/to/audio.mp3Output: plain text transcription on stdout.
For machine-readable output with usage / timing:
python3 scripts/asr_transcribe.py /path/to/audio.mp3 --jsonFor non-Chinese audio:
python3 scripts/asr_transcribe.py /path/to/audio.mp3 --language enPer-word timestamps need no flag — --json always carries segments:
"segments": [{"text": "Understand ", "start_ms": 228, "end_ms": 1108}, ...]One entry per word, monotonic. A few words share their predecessor's timestamp (the server flushes in blocks), which is fine for locating a moment but not for forced alignment.
To use an older model:
python3 scripts/asr_transcribe.py /path/to/audio.mp3 --model stepaudio-2.5-asrThe script handles base64 encoding, the nested {audio: {data, input: {transcription, format}}} body, SSE parsing, and the misleading-endpoint pitfall. Prefer it over hand-rolled HTTP calls unless integrating into a larger pipeline.
Decision table
| Scenario | Action |
|---|---|
| Short clip (< 5 min), Chinese or English, mp3/wav/ogg/opus | python3 scripts/asr_transcribe.py audio.mp3 |
| Long audio (5-30 min) | Same script — 32K context handles it in a single call, no chunking needed |
| Audio > 30 min | Split with ffmpeg before sending; the API rejects oversized payloads |
| Need usage/billing data | Add --json to capture usage.input_tokens / usage.total_tokens from transcript.text.done |
| Need to know when each word was said | --json, read segments. On by default |
| Need speaker labels (who said what) | python3 scripts/asr_file.py <public-url> — a different, async endpoint. Takes a URL, not a local file: base64 and StepFun's own file store are both rejected, so hosting the audio somewhere fetchable is a decision for whoever runs it |
--model stepaudio-2-asr-pro returns internal error | That model is not usable on /v1/audio/asr/sse (measured 2026-09-18); use the default or stepaudio-2.5-asr |
| Highly repetitive content (same phrase 5+ times, > 90s) | Cross-validate with step-asr-1.1 — see repetition hallucination in references/known_issues.md (2.5-era issue, unverified on v3) |
Hit model stepaudio-3-asr-max not supported | Wrong endpoint. Switch from /v1/audio/transcriptions to /v1/audio/asr/sse |
| Hit silent 4xx auth failure | Verify your key is "Normal" not "Plan" — Plan keys cannot call audio endpoints |
| Need to write raw HTTP (no Python) | Read references/api_reference.md for exact JSON body and SSE event shapes |
Speaker labels — scripts/asr_file.py
stepaudio-3-asr-max on /v1/audio/asr/sse has no speaker capability at all (14 candidate request fields measured inert). Diarization lives on the async file endpoint:
python3 scripts/asr_file.py https://example.com/talk.mp3
# [ 6.61- 8.43] speaker_0: Hello. Hello. Oh,
# [ 8.21- 10.11] speaker_1: hello! I didn't know you were there.Verified end-to-end 2026-09-18 on a two-speaker sample: correct turn boundaries, per-word timestamps inside each utterance, up to 10 speakers per task. Uses stepaudio-2.5-asr — v3 is not served on this endpoint.
The hard constraint: it fetches a URL and nothing else. Base64 is rejected and so is StepFun's own stepfile:// file store, so there is no way to feed it a local file without first putting that file somewhere publicly fetchable. Treat that as the caller's decision. references/known_issues.md has the three dead ends and the retry/redirect behaviour.
Parameters are free — never omit one silently
Sending more request parameters costs nothing: billing is per audio-hour. So the default is send everything useful, and every field we do not send has to carry a written reason in REQUEST_PARAMS at the top of scripts/asr_transcribe.py.
python3 scripts/check_params.py # diff official field table vs REQUEST_PARAMS
python3 scripts/check_params.py --selftest # calibrate the check before trusting itTwo guards, one per direction:
- Request side —
check_params.pyfetches the official field table and fails if it lists a fieldREQUEST_PARAMSdoes not mention.--selftestcalibrates both ways: the real manifest must pass (no false alarms), and a manifest withenable_timestampremoved must be caught (the actual historical gap — per-word timestamps were missing for months because only the response field table was ever read). - Response side — the parser reports
unhandled_response_fieldsfor anything the server sends that it does not consume, because that is what the timestamp gap looked like from this side:start_time/end_timearrived on every delta and were thrown away.
Supported audio formats
The script auto-detects from extension; pass --format to override:
| Extension | Format flag | Notes |
|---|---|---|
.mp3 | mp3 | Most common, default |
.wav | wav | Lossless |
.ogg | ogg | OGG container |
.opus | ogg | Opus codec in OGG container — pass through unchanged |
.pcm | pcm | Raw PCM — also pass --rate, --bits, --channel (and --codec) |
For mp4/m4a/webm/etc., transcode to one of the above first via ffmpeg. Production pipelines often pre-transcode everything to OGG/Opus 16kHz mono to minimize base64 payload size.
Capacity and performance
More from daymade
Use this skill in a project
Open any RiverX project, search the Skills panel for daymade/claude-code-skills/stepfun-asr, and install it in one click.
