Fast Transcription
Low-latency transcription over socket.io.
Use Fast transcription for one already-complete, bounded audio unit whose
latency matters, such as a conversational or AI-agent turn. Send the complete
encoded unit once through the binary audio_file event and receive result
events through transcription_result.
Fast transcription is not live microphone input. Use Batch for long or large meetings, podcasts, and archives. This contract defines no latency guarantee.
Limits
Two independent bounds apply to every audio_file upload, and they measure
different things.
The encoded media carried by the event must not exceed 64 MiB
(67108864 bytes). Exceeding it emits an error with code
PAYLOAD_TOO_LARGE and data.bound: fast_audio_bytes, and then closes the
connection: the transport has already buffered an oversized payload, so the
socket is not left available to repeat it. Note this cap is measured on the
MEDIA BYTES ONLY, after the framing header and the four model-key strings.
The equivalent HTTP route bounds the whole multipart request body with the
same number, so an exactly-64-MiB file passes here but cannot fit inside a
64-MiB HTTP body.
The audio must not DECODE to more than 1800 seconds (30 minutes).
Exceeding it emits an error with code AUDIO_DURATION_EXCEEDED and
data.bound: fast_audio_duration. This is a separate bound because a small
compressed upload can decode to many hours. Unlike the byte cap this does
NOT close the connection: nothing oversized was buffered, and a client
multiplexing other transcriptions on the same socket keeps them. Both bounds
are inclusive - exactly at the limit succeeds, and only strictly over fails.
Over-long audio is refused before any inference and consumes no audio-capacity credit. For recordings longer than 30 minutes or larger than 64 MiB, split the audio into shorter units or use the batch transcription API, whose ceiling is 4 hours per file.
audio_file_upload_success does NOT mean the audio was accepted
It acknowledges that the event was received and passed the checks that can be
made from the bytes alone: framing, the byte cap, and a duration a WAV header
declares for itself. The remaining checks - the container allowlist, the
container's own metadata duration, and the authoritative bounded decode - run
after it, so a compressed upload that decodes to more than 1800 seconds, or a
container outside the accepted set, receives audio_file_upload_success and
THEN an error. Treat this event as a byte-level receipt, never as admission.
Only an is_final: true transcription_result means the audio was
transcribed.
Audio format
The complete payload must be AAC (ADTS), FLAC, MP3, WAV, or an ISO base media
file. The container is identified by the server from the payload itself, not
from a filename or a media type, and anything else is rejected with
ASR_UNSUPPORTED_CODEC even when it is otherwise decodable.
The ISO base media entry is a family: MP4 is the intended and supported form, and MOV, M4A, 3GP, 3G2 and MJ2 share one demuxer with it and are therefore admitted by the same check. Only MP4 is supported in the sense of being tested and intended; do not build on the others.
Put the moov atom at the FRONT of an ISO base media file. This is not a
policy the server checks and reject on - it is a practical requirement: the
upload is read forward-only, so a trailing moov cannot be reached and the
file fails to decode.
Audio is resampled to the sample rate configured for the selected ASR model. The client does not select that sample rate.
Multiplexing
Multiple complete units can share one connection. Give every concurrent request
a unique transcription_id; do not reuse it until that request reaches a final
result, a routed error, or application-deadline cleanup. Responses echo it as
id.
Language Options
| ID | Code | Description |
|---|---|---|
| 0 | ar | Arabic |
| 1 | en | English |
| 2 | codeswitch | Arabic-English code-switching |
| 255 | auto | Automatic — resolves to the default configured for the environment, currently the code-switching model |
Connection
Host: wss://api.voice.humain.com
Use the API_URL and API_KEY provisioned for the target environment.
API_PATH is optional and defaults to /socket.io, the single path that
serves every subsystem; set it only to override for a self-hosted or
proxied deployment. This is Socket.IO over WebSocket, not raw WebSocket.
Transport: WebSocket only — set transports: ["websocket"]. Polling is not supported.
Example (JavaScript):
const socket = io(process.env.API_URL, {
path: process.env.API_PATH ?? "/socket.io",
transports: ["websocket"],
extraHeaders: { "x-api-key": process.env.API_KEY, "Origin": process.env.API_URL }
});Example (Python — python-socketio):
async def main() -> None:
await sio.connect(
os.environ["API_URL"],
headers={"x-api-key": os.environ["API_KEY"], "Origin": os.environ["API_URL"]},
socketio_path=os.environ.get("API_PATH", "/socket.io"),
transports=["websocket"],
)
asyncio.run(main())Authentication: x-api-key (header httpApiKey).
Events
| Event | Direction | Description |
|---|---|---|
audio_file | Client → Server | Client sends binary audio file for transcription. |
audio_file_upload_success | Server → Client | Server acknowledges receipt of the audio packet; transcription is not complete yet. |
transcription_result | Server → Client | Server streams transcription results. |
error | Server → Client | Server emits error messages. |
Messages
Audio file upload (binary)
audio_file · Client → Server
Raw binary buffer containing metadata and file bytes.
Content type: application/octet-stream
Raw unencoded binary buffer. Do not send JSON or base64.
Byte layout (offsets in bytes):
| Offset | Size | Field | Description |
|---|---|---|---|
| 0..15 | 16 | transcription_id | UUID identifying this transcription request |
| 16 | 1 | language_id | Language: 0=Arabic, 1=English, 2=Codeswitch, 255=Auto |
| 17..18 | 2 | asr_model_key length | uint16 LE |
| next N | N | asr_model_key | UTF-8 string (optional; empty to use the language default) |
| next 2 | 2 | dia_model_key length | uint16 LE |
| next N | N | dia_model_key | Reserved in the current public Fast contract; send a zero-length value |
| next 2 | 2 | itn_model_key length | uint16 LE |
| next N | N | itn_model_key | Reserved in the current public Fast contract; send a zero-length value |
| next 2 | 2 | redact_model_key length | uint16 LE |
| next N | N | redact_model_key | Reserved in the current public Fast contract; send a zero-length value |
| remaining | - | file_bytes | Audio file (AAC/FLAC/MP3/MP4/WAV) |
Length fields are little-endian uint16. The example below is an
illustrative hexadecimal layout; spaces and <audio-bytes> are not
transmitted.
Upload received
audio_file_upload_success · Server → Client
Acknowledges receipt of the packet and echoes its request ID. This is not transcription completion.
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Request ID echoed from the received packet. |
Examples
Packet received
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8"
}Transcription result
transcription_result · Server → Client
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Request ID echoed from the audio packet. |
seq | integer (int64) | Yes | Sequence value supplied with this result. Ordering and aggregation semantics are not part of the public Fast contract. |
transcription | string | Yes | Transcription text carried by this result event. |
words | WordSegment[] | Yes | Timed words carried by this result event. |
is_final | boolean | Yes | True when this is the terminal result event for the request. |
WordSegment
| Field | Type | Required | Description |
|---|---|---|---|
start_time | number (float) | Yes | Word start time in seconds within the submitted audio unit. |
end_time | number (float) | Yes | Word end time in seconds within the submitted audio unit. |
word | string | Yes | Recognized word text. |
Examples
Non-final result
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"seq": 0,
"transcription": "hello wor",
"words": [
{
"start_time": 0,
"end_time": 0.45,
"word": "hello"
}
],
"is_final": false
}Final result
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"seq": 0,
"transcription": "hello world",
"words": [
{
"start_time": 0,
"end_time": 0.45,
"word": "hello"
},
{
"start_time": 0.46,
"end_time": 0.9,
"word": "world"
}
],
"is_final": true
}Error message
error · Server → Client
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | No | Transcription request UUID (present when parseable from payload) |
code | string | Yes | Machine-readable error code. Switch on this value rather than on the message text. |
data | object | No | Present only on limit rejections. Names the bound that was exceeded, its configured value and the observed value, so a client can tell which limit it hit without parsing prose. For fast_audio_duration the unit is seconds; where the service stopped decoding at the ceiling it never learned the true total length, so observed is a MINIMUM. For fast_audio_bytes the unit is bytes and observed is the exact media size. |
message | string | Yes | Human-readable error text (same values as previous bare strings) |
retryable | boolean | Yes | Whether the client should retry |
timestamp | string (date-time) | Yes | Server timestamp for the error event. |
Examples
Invalid data type
{
"code": "VALIDATION_INVALID_FORMAT",
"message": "Invalid data type",
"retryable": false,
"timestamp": "2025-05-07T10:00:00.000Z"
}Encoded media exceeds the 64 MiB cap (connection then closes)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "PAYLOAD_TOO_LARGE",
"message": "audio payload too large",
"retryable": false,
"timestamp": "2025-05-07T10:00:00.000Z",
"data": {
"limit": 67108864,
"observed": 83886080,
"unit": "bytes",
"bound": "fast_audio_bytes"
}
}Audio decodes to more than 1800 seconds (connection stays open)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "AUDIO_DURATION_EXCEEDED",
"message": "decoded audio duration exceeds the maximum for this endpoint; split the recording or use the batch transcription API",
"retryable": false,
"timestamp": "2025-05-07T10:00:00.000Z",
"data": {
"limit": 1800,
"observed": 3601,
"unit": "seconds",
"bound": "fast_audio_duration"
}
}Container is outside AAC/FLAC/MP3/MP4/WAV
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "ASR_UNSUPPORTED_CODEC",
"message": "audio container is not supported; use AAC, FLAC, MP3, MP4 or WAV",
"retryable": false,
"timestamp": "2025-05-07T10:00:00.000Z"
}