Add transcription to your own API
If your application needs to upload a recording and receive its text, start with that single round trip. A local transcription endpoint lets your team check the request and response before adding real traffic or a public gateway.
The example uses an authorized audio.wav: a client uploads the file to a local SenseVoice service, then reads the JSON text field. It supplies a callable path, not a fabricated transcript or a speed comparison.
This article covers the packaged funasr-server in FunASR 1.4.15. It offers part of the OpenAI transcription interface, not every hosted feature or response format; a working URL alone does not complete a migration.
Start with one local JSON transcription
Prerequisite: a separate, already prepared and verified FunASR 1.4.15 environment. This is not a from-scratch installation tutorial. The Python and funasr-server commands must use the same activated environment, with funasr==1.4.15, PyTorch, audio dependencies, FastAPI, Uvicorn, python-multipart, model-weight access and an audio file ready. Use the installation guide for platform dependencies; other versions require a fresh behavior check.
python -c 'from importlib.metadata import version; assert version("funasr") == "1.4.15", version("funasr")'
python -m pip check
The version assertion and pip check inspect package metadata; they do not prove working CUDA, audio decoding or acoustic inference. The command below explicitly selects SenseVoice CPU for an integration check, not a performance recommendation. Do not start two servers on the same port. The separate repository example HTTP service guide is an implementation comparison: it prepares and starts the example server, not the packaged 1.4.15 environment required here.
funasr-server --host 127.0.0.1 --port 8000 --model sensevoice --device cpu
From another terminal on the same host, upload an audio.wav file you are authorized to process. The command prints transcript content; keep private audio results out of public logs.
curl --fail --show-error --silent --max-time 120 \
http://127.0.0.1:8000/v1/audio/transcriptions \
-F "file=@audio.wav" \
-F "model=sensevoice" \
-F "response_format=json"
The response is a JSON object containing text. Its content depends on the recording and model. The 120-second client timeout is an example, not a duration guarantee or proof that timed-out inference is cancelled.
OpenAI Python SDK: specify the model and format
The following basic call uses an environment with the separate openai client installed. A migration must still check model names, fields, response parsing, errors and gateway authentication, not just URL reachability.
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8000/v1",
api_key="not-needed",
timeout=120.0,
max_retries=0,
)
with open("audio.wav", "rb") as audio:
result = client.audio.transcriptions.create(
model="sensevoice", file=audio, response_format="json"
)
print(result.text)
The basic JSON call above does not produce subtitles. The packaged handler does not generate SRT/VTT; its text format returns a JSON string rather than plain text. Check the exact format contract in the appendix before changing the application parser.
Before connecting Open WebUI or another application
- Inspect the application's actual model alias, file type and
response_format, plus how it handles success, empty text, errors and timeouts. - A container's
127.0.0.1is the container itself. A host loopback listener does not become reachable merely by changing a URL tohost.docker.internal. Cross-container setups need a restricted private network and matching gateway authentication. - Basic, Bearer and OIDC credentials are not interchangeable. Do not reuse the local placeholder as a production credential. Preloading one model is not a model-admission policy.
- Verify the entire path with controlled audio before admitting real traffic. Follow the maintained client recipes.
Compare complete deployments, not just API prices
Self-hosting has compute, storage, bandwidth, maintenance and monitoring costs. Data control depends on the client, proxy, temporary files, logs and retention policy; neither no-disk handling nor leak-free transcripts are automatic. Review model-weight licenses separately from the toolkit's code license.
Choose by language, latency, hardware and speaker requirements using model selection, MOSS transcription and diarization and the deployment matrix. This article supplies no new accuracy or performance ranking. For acceptance criteria, see the meeting-audio checklist.
Appendix: verify the response contract
Compatibility of the packaged handler
| Item | Current behavior and integration requirement |
|---|---|
| Request | Multipart upload. The route declares file, model, language, response_format and the FunASR extension spk. Language and speaker capabilities depend on the selected model. |
json | A JSON object with text, suitable for a first basic transcription check. |
verbose_json | Returns structured JSON with fields including segments and duration. Actual timing and speaker information depend on the model and result. |
text | The packaged handler returns a JSON string, not a standard plain-text response. Do not assume identical cloud-client parsing. |
srt | Does not produce SRT subtitles; this value reaches the JSON text-object branch. HTTP 200 does not prove that a subtitle file was returned. |
vtt | Does not produce VTT subtitles; this value also reaches the JSON text-object branch. |
| Other fields and protocols | prompt, temperature and timestamp_granularities are not declared capabilities of this route. It is not a streaming WebSocket, translation or complete cloud API. An extra field causing no error does not prove it was applied. |
For the packaged service, verify fields against its running /openapi.json over a private or restricted operational path and the pinned packaged handler source. The example-service API schema documents a different implementation, not this packaged handler. Packaged, repository example, vLLM and llama.cpp servers do not share every default, field or response shape.
Before another user connects, work through the service security guide and verify that the gateway cannot be bypassed. Keep the local example private until those deployment checks pass.