Transcription
Transcribe audio files, optionally with speaker labels and meeting notes. Poll status and export documents.
Transcribe audio files, optionally with speaker labels and meeting notes. Poll status and export documents.
Upload an audio file, get a job id, and fetch the transcript when it is ready. Large files run asynchronously: the upload answers 202 immediately.
Two modes: transcript returns only the text. meeting also creates a title and summaries, like the meeting view in the app.
POST creates the job and answers before recognition finishes. Poll GET with the job id until status is completed or failed.
| Parameter | Type | Description |
|---|---|---|
id | uuid | UUID of the job. id and job_id are the same identifier. |
mode | "transcript" | "meeting" | transcript (text only) or meeting (text plus notes). |
status | "processing" | "pending" | "completed" | "failed" | processing, pending, completed or failed. |
text | string | null | The finished transcript. With anonymization, the redacted text, never the raw original. |
segments | array | null | Chunks with time and optional speaker. Only when requested and not anonymized. |
summaries | array | Only in meeting mode: prepared notes per template. |
error_code | "AUDIO_TOO_LONG" | "UPLOAD_INCOMPLETE" | "AUDIO_UNREADABLE" | "ASSEMBLY_FAILED" | Only with failed and only when the reason is known: AUDIO_TOO_LONG, UPLOAD_INCOMPLETE, AUDIO_UNREADABLE or ASSEMBLY_FAILED. ASSEMBLY_FAILED means the parts could not be put together, even after several tries. Upload the recording again. |
1curl "https://app.anymize.ai/api/v1/transcriptions" \2 -H "Authorization: Bearer YOUR_API_KEY" Upload a file and start recognition. The answer is 202 with the job id.
multipart/form-data with the file and optional switches. Fetch the transcript later via GET.
/api/v1/transcriptionsRequest: multipart/form-data
Body
| Parameter | Type | Description |
|---|---|---|
filerequired | File | The audio file. MP3, WAV, M4A, OGG, WEBM. |
mode | "transcript" | "meeting" | transcript or meeting. Default transcript. |
speaker_detection | boolean | Speaker detection. Default on for meeting, otherwise off. |
timestamps | boolean | Chunks with milliseconds. Always on when speaker detection is on. |
language | string | ISO-639-1, or omit / auto to detect after transcription. |
prompt | string | Short hint for recognition, at most 1000 characters. |
context_bias | string | Comma-separated terms so names are spelled right. At most 100 tokens. |
anonymize | boolean | Then send through anonymization. Supported: de, en, fr, es, it. |
{
"id": "11111111-1111-4111-8111-111111111111",
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "processing",
"mode": "transcript",
"title": "standup.m4a",
"filename": "standup.m4a",
"message": "Audio submitted for transcription. Poll GET /api/v1/transcriptions/:id until status is completed or failed."
}| 401 | UNAUTHORIZED | No valid key and no session. |
| 400 | NO_FILE | The form is missing the file field. |
| 400 | INVALID_FORMAT | Unsupported audio format. |
| 413 | FILE_TOO_LARGE | The file is larger than 100 MB. Upload larger recordings in parts. |
| 429 | RATE_LIMIT | Too many uploads in this minute. |
1curl -X POST "https://app.anymize.ai/api/v1/transcriptions" \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -F "file=@standup.m4a" \4 -F "mode=transcript" \5 -F "language=de" One request carries at most 100 MB, and big requests like to break off on shaky lines. For longer recordings there is the upload in parts: up to 1 GB and 6 hours, and in the end you still get exactly one job with one transcript.
How it works: open a session, send the parts in order with Content-Range, then call complete. You get the same job ID as with the single upload and poll its status via GET /api/v1/transcriptions/:id.
JSON with file name, size and type, plus the same options as the single upload. Format, size and language are checked right here, before a single byte is on its way. The response names sessionId, chunkSize and nextExpectedRanges.
/api/v1/transcriptions/uploadsRequest: application/json
Body
| Parameter | Type | Description |
|---|---|---|
filenamerequired | string | The recording's file name, e.g. meeting.wav. It becomes the job's title. |
sizerequired | integer | The size of the whole file in bytes, at most 1 GB. |
mimeType | string | The MIME type, e.g. audio/wav. Same formats as the single upload. |
mode | "transcript" | "meeting" | mode, speaker_detection, timestamps, language, prompt, context_bias and anonymize work as in the single upload, here as JSON fields. |
{
"sessionId": "22222222-2222-4222-8222-222222222222",
"uploadUrl": "/api/v1/transcriptions/uploads/22222222-2222-4222-8222-222222222222",
"chunkSize": 8388608,
"nextExpectedRanges": [
"0-"
],
"expiresAt": "2026-09-24T10: 15: 00.000Z",
"maxBytes": 1073741824
}| 401 | UNAUTHORIZED | No valid key and no session. |
| 400 | INVALID_BODY | Not a JSON object, or filename or size is missing or invalid. |
| 400 | INVALID_FORMAT | Not a supported audio format (MP3, WAV, M4A, OGG, WEBM). |
| 413 | FILE_TOO_LARGE | The file is larger than 1 GB. |
| 429 | RATE_LIMIT | Too many uploads in this minute. |
| 503 | STORAGE_UNAVAILABLE | Storage is not answering right now. Send the same request again in a moment. |
1curl -X POST "https://app.anymize.ai/api/v1/transcriptions/uploads" \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{"filename":"workshop.wav","size":734003200 ,"mimeType":"audio/wav","mode":"meeting"}' The raw part as the body, with Content-Range: bytes start-end/total and Content-Length. The 202 response names the next expected byte in nextExpectedRanges. You can safely send a part that was already accepted again.
/api/v1/transcriptions/uploads/:sessionIdRequest: application/octet-stream
{
"sessionId": "22222222-2222-4222-8222-222222222222",
"receivedBytes": 8388608,
"nextExpectedRanges": [
"8388608-"
],
"expiresAt": "2026-09-24T10: 16: 02.000Z"
}| 400 | INVALID_RANGE | Content-Range is missing, malformed or does not match the body's length. |
| 400 | UPLOAD_INCOMPLETE | The body arrived shorter than its Content-Length, the connection broke off. Send the same part again. |
| 401 | UNAUTHORIZED | No valid key and no session. |
| 404 | SESSION_NOT_FOUND | This session does not exist or belongs to another account. |
| 409 | RANGE_MISMATCH | The part does not continue without a gap. nextExpectedRanges names the byte to continue from. |
| 410 | SESSION_EXPIRED | The session has expired and its parts are gone. Start the upload again. |
| 411 | LENGTH_REQUIRED | Content-Length is missing. Without a length the server accepts no part. |
| 413 | FILE_TOO_LARGE | The part is larger than the session's chunkSize. |
| 503 | STORAGE_UNAVAILABLE | Storage is not answering right now. Send the same request again in a moment. |
1# One part at a time, in order. Here: the second 8 MiB part. 2curl -X PUT "https://app.anymize.ai/api/v1/transcriptions/uploads/22222222 -2222 -4222 -8222 -222222222222 " \3 -H "Authorization: Bearer YOUR_API_KEY" \4 -H "Content-Type: application/octet-stream" \5 -H "Content-Range: bytes 8388608 -16777215 /734003200 " \6 --data-binary @part-001 .binHow many bytes arrived and where to continue. Once the upload is completed, the jobId shows up here too.
/api/v1/transcriptions/uploads/:sessionId{
"sessionId": "22222222-2222-4222-8222-222222222222",
"filename": "workshop.wav",
"sizeBytes": 734003200,
"receivedBytes": 83886080,
"nextExpectedRanges": [
"83886080-"
],
"chunkSize": 8388608,
"expiresAt": "2026-09-24T10: 25: 40.000Z"
}| 404 | SESSION_NOT_FOUND | This session does not exist or belongs to another account. |
| 410 | SESSION_EXPIRED | The session has expired and its parts are gone. Start the upload again. |
Cancels the upload and removes all parts right away. Without this call we clean them up ourselves one hour after the last part.
/api/v1/transcriptions/uploads/:sessionId{
"success": true
}| 404 | SESSION_NOT_FOUND | This session does not exist or belongs to another account. |
| 409 | SESSION_COMPLETED | The session already became a job and can no longer be aborted. |
Once all parts are up: creates the job and answers 202 with its ID right away. Assembling, converting and transcribing run in the background afterwards. Calling it again returns the same job.
/api/v1/transcriptions/uploads/:sessionId/complete{
"id": "11111111-1111-4111-8111-111111111111",
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "processing",
"mode": "meeting",
"title": "workshop.wav",
"filename": "workshop.wav",
"message": "Audio submitted for transcription. Poll GET /api/v1/transcriptions/:id until status is completed or failed."
}| 404 | SESSION_NOT_FOUND | This session does not exist or belongs to another account. |
| 409 | UPLOAD_INCOMPLETE | Parts are still missing. nextExpectedRanges shows where to continue. |
| 410 | SESSION_EXPIRED | The session has expired and its parts are gone. Start the upload again. |
1curl -X POST "https://app.anymize.ai/api/v1/transcriptions/uploads/22222222 -2222 -4222 -8222 -222222222222 /complete" \2 -H "Authorization: Bearer YOUR_API_KEY" Status, text, segments and, for meetings, the notes. Audio bytes are not included.
Returns the current state. Poll until status is completed or failed. text is null while it runs.
/api/v1/transcriptions/:id{
"id": "11111111-1111-4111-8111-111111111111",
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "completed",
"mode": "transcript",
"title": "standup.m4a",
"filename": "standup.m4a",
"language": "de",
"words": 12,
"duration_ms": 18400,
"has_audio": true,
"speaker_detection": false,
"anonymize": false,
"anonymization_applied": false,
"text": "Guten Morgen. Lasst uns mit dem Stand-up beginnen.",
"segments": [
{
"start_ms": 0,
"end_ms": 18400,
"speaker": "",
"text": "Guten Morgen. Lasst uns mit dem Stand-up beginnen."
}
],
"created_at": "2026-09-11T08: 15: 00.000Z"
}{
"id": "11111111-1111-4111-8111-111111111111",
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "completed",
"mode": "meeting",
"title": "Q3 Planung",
"filename": "meeting.wav",
"language": "de",
"words": 842,
"duration_ms": 3120000,
"has_audio": true,
"speaker_detection": true,
"anonymize": false,
"anonymization_applied": false,
"text": "(((Speaker 1))) Guten Morgen. (((Speaker 2))) Danke, dass ihr da seid.",
"segments": [
{
"start_ms": 0,
"end_ms": 2100,
"speaker": "Speaker 1",
"text": "Guten Morgen."
},
{
"start_ms": 2200,
"end_ms": 5400,
"speaker": "Speaker 2",
"text": "Danke, dass ihr da seid."
}
],
"summaries": [
{
"template_key": "general",
"title": null,
"content": "Stand-up zur Q3-Planung. Naechster Schritt: Budget bis Freitag.",
"status": "completed",
"locale": "de"
}
],
"created_at": "2026-09-11T08: 15: 00.000Z"
}| 401 | UNAUTHORIZED | No valid key and no session. |
| 404 | NOT_FOUND | No audio job with this id, or it is not yours. |
1curl "https://app.anymize.ai/api/v1/transcriptions/11111111 -1111 -4111 -8111 -111111111111 " \2 -H "Authorization: Bearer YOUR_API_KEY" Your audio transcriptions, without the full text. Fetch that with GET on the individual id.
Audio jobs only. Filter by mode and status. No text and no segments, so the list stays small.
/api/v1/transcriptionsQuery
| Parameter | Type | Description |
|---|---|---|
mode | "transcript" | "meeting" | Only transcript (kind input or live) or only meeting. |
status | string | processing, pending, completed or failed. |
limit | integer | Count per page, at most 100. |
offset | integer | Offset for the next page. |
{
"data": [
{
"id": "11111111-1111-4111-8111-111111111111",
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "completed",
"mode": "transcript",
"title": "standup.m4a",
"filename": "standup.m4a",
"language": "de",
"words": 12,
"created_at": "2026-09-11T08: 15: 00.000Z"
}
],
"total": 1
}| 401 | UNAUTHORIZED | No valid key and no session. |
1curl "https://app.anymize.ai/api/v1/transcriptions?mode=transcript&limit=20 " \2 -H "Authorization: Bearer YOUR_API_KEY" The mode you pick decides what happens after recognition, not the quality of the text.
transcript stores only the text. No automatic notes, no meeting title.
meeting detects speakers by default, assigns a title and generates the default summary.
Formats: MP3, WAV, M4A, OGG, WEBM. Up to 100 MB in one request, up to 1 GB and 6 hours with the upload in parts. Rate limit: 50 uploads per minute.
1curl -X POST "https://app.anymize.ai/api/v1/transcriptions" \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -F "file=@meeting.wav" \4 -F "mode=meeting" \5 -F "speaker_detection=true" \6 -F "timestamps=true" The older endpoint stays. It starts the same engine and answers with the historical 202 shape so existing clients can keep polling /api/status/:id.
API key only, no session cookie. The same form fields as POST /api/v1/transcriptions, including mode.
/api/transcribeRequest: multipart/form-data
Body
| Parameter | Type | Description |
|---|---|---|
filerequired | File | The audio file. MP3, WAV, M4A, OGG, WEBM. |
mode | "transcript" | "meeting" | transcript or meeting. Default transcript. |
speaker_detection | boolean | Speaker detection. Default on for meeting, otherwise off. |
timestamps | boolean | Chunks with milliseconds. Always on when speaker detection is on. |
language | string | ISO-639-1, or omit / auto to detect after transcription. |
prompt | string | Short hint for recognition, at most 1000 characters. |
context_bias | string | Comma-separated terms so names are spelled right. At most 100 tokens. |
anonymize | boolean | Then send through anonymization. Supported: de, en, fr, es, it. |
{
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "processing",
"message": "Audio submitted for transcription. Use /api/status/{job_id} to check status."
}1curl -X POST https://app.anymize.ai/api/transcribe \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -F "file=@standup.mp3" \4 -F "language=de" Check the current status of an asynchronous job.
/api/status/{jobId}| Parameter | Type | Description |
|---|---|---|
jobIdrequired | string | The job ID you received when creating the job. |
A job goes through the following states:
{
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "processing"
}When the job is complete, the response contains the result.
{
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "completed",
"anonymized_text_raw": "Max [[Person-A1B2C3]] lives in [[Location-D4E5F6]].",
"original_text": "Max Mustermann lives in Berlin.",
"metadata": {
"filename": "document.pdf",
"language": "en",
"words": 6
}
}{
"job_id": "11111111-1111-4111-8111-111111111111",
"status": "failed"
}Query the status at regular intervals until the job is finished.
Tip
1# Poll every 3 seconds until completed 2while true; do3 RESULT=$(curl -s https://app.anymize.ai/api/status/11111111 -1111 -4111 -8111 -111111111111 \4 -H "Authorization: Bearer YOUR_API_KEY" )5 STATUS=$(echo $RESULT | jq -r '.status' )6 echo "Status: $STATUS" 7 if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then8 echo $RESULT | jq .9 break10 fi11 sleep 3 12doneExport completed transcriptions in various formats.
/api/v1/jobs/{jobId}/exportSpecify job ID, format, and optionally a variant.
| Parameter | Type | Description |
|---|---|---|
jobIdrequired | string | The job ID of the completed transcription. |
format | string | Output format: pdf or txt. docx returns HTTP 501. |
textType | string | Export variant (e.g. anonymized or original). |
pdfPDF — Formatted document with layout and metadata.txtTXT — Plain text without formatting.The download includes appropriate Content-Type and Content-Disposition headers.
Content-Type: application/pdf
Content-Disposition: attachment; filename="document_anonymized.pdf"
Content-Length: 245832Choose between the original and anonymized version.
Tip
1curl -OJ https://app.anymize.ai/api/v1/jobs/11111111 -1111 -4111 -8111 -111111111111 /export?format=pdf \2 -H "Authorization: Bearer YOUR_API_KEY" DOCX returns 501. Missing jobs return 404, and incomplete jobs return 400.
{
"error": "DOCX export not yet implemented"
}{
"error": "Job not found"
}