Skip to content
Docs / Platform / Speech-to-text

Speech-to-text

Whisper large-v3 transcription with an async jobs API, URL and YouTube endpoints, cleaned and bilingual subtitle modes, and an OpenAI-compatible drop-in.

Overview

All transcription runs Whisper large-v3 locally, one job at a time. Subtitle cleanup (perfect-srt) and translation modes use Gemini Flash. Auth is a key or an approved session (require_api_key); the key needs the media capability. Job results live in memory and expire about one hour after completion.

The jobs API is the canonical way to transcribe over HTTP. The synchronous endpoints still exist but die on the public edge's 100-second timeout for long files.

Jobs API

Submit an uploaded file

POST /jobs (multipart/form-data)

curl -X POST https://maximind.dev/jobs \
  -H "x-api-key: $MAXIMIND_API_KEY" \
  -F "file=@lesson.mp4" -F "type=perfect-srt" \
  -F "language=vi" -F "target_language=English"
{"job_id": "a1b2c3d4e5f6", "type": "perfect-srt"}
Name Type Required Description
file file Yes Any audio or video file. Public uploads are capped at about 100 MB by the edge.
type string No Output mode, default transcribe. See the table below. Unknown value: HTTP 400 unknown type: ….
language string No Source language code (vi, en, zh). Default: auto-detect.
target_language string No Language name for bilingual-srt (second line) and translated-srt (only output). Default English.
with_segments boolean No Include per-segment detail in meta. Default false.

Output modes

type Output
transcribe Plain text transcript in the source language.
translate Whisper English translation as text.
srt Standard Whisper SRT.
perfect-srt AI-cleaned SRT, resegmented to at most 10 words per line, sentence-boundary aware.
bilingual-srt Each cue shows the original line with a target_language translation underneath.
translated-srt Single-track SRT fully in target_language.
word-srt Word-level timestamped SRT.

Submit a URL

POST /jobs/url (application/x-www-form-urlencoded or multipart)

Same parameters as /jobs with url instead of file. YouTube, TikTok or a direct audio/video URL. Free native captions are used when available; otherwise the media is downloaded and run through Whisper.

curl -X POST https://maximind.dev/jobs/url \
  -H "x-api-key: $MAXIMIND_API_KEY" \
  -d "url=https://www.youtube.com/watch?v=VIDEO_ID" \
  -d "type=bilingual-srt" -d "language=en" -d "target_language=Vietnamese"
{"job_id": "…", "type": "bilingual-srt"}

Poll

GET /jobs/{job_id}

curl https://maximind.dev/jobs/a1b2c3d4e5f6 -H "x-api-key: $MAXIMIND_API_KEY"

In progress:

{"id": "a1b2c3d4e5f6", "type": "perfect-srt", "status": "running", "stage": "transcribing audio",
 "progress": 0.42, "elapsed_seconds": 12.3, "filename": "lesson.mp4", "error": null}

Done (meta, is_srt and result appear only when status is done):

{"id": "a1b2c3d4e5f6", "type": "perfect-srt", "status": "done", "stage": "done",
 "progress": 1.0, "elapsed_seconds": 47.8, "filename": "lesson.mp4", "error": null,
 "meta": {"language": "vi", "source": "whisper"}, "is_srt": true,
 "result": "1\n00:00:00,000 --> 00:00:01,200\n..."}
Field Meaning
status queued, running, done, failed (or cancelling).
stage Human-readable progress label.
progress 0 to 1.
error Failure message when failed.
result Text or SRT body.
is_srt true when result is SRT.
meta language, source (youtube_captions, whisper, …) and segments when with_segments=true.

Unknown or expired id: HTTP 404 no such job (or it expired after 1h).

Cancel or forget

DELETE /jobs/{job_id}

Cancels a queued or running job (in-flight Whisper work may still finish in the background) or drops a finished one.

curl -X DELETE https://maximind.dev/jobs/a1b2c3d4e5f6 -H "x-api-key: $MAXIMIND_API_KEY"
{"id": "…", "status": "…", "stage": "…", "cancelled": true}

Synchronous endpoints

These block until done and are only safe for short media through the public edge.

POST /transcribe-url (JSON) — direct audio or video URL to a JSON transcript. Fields: url (required), mode (transcribe | translate | both, default transcribe), language (auto), with_segments (false). Returns {text, language, segments, duration_seconds, processing_seconds}; segments is null unless requested. Download failure: HTTP 400 Failed to download: ….

curl -X POST https://maximind.dev/transcribe-url \
  -H "x-api-key: $MAXIMIND_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/audio.mp3","mode":"transcribe","language":"vi"}'

POST /youtube-to-srt (JSON) — YouTube URL to a raw .srt file (application/x-subrip). Fields: url (required), language (falls back to en, also names the file {video_id}_{lang}.srt). The X-SRT-Source header is captions or whisper.

curl -X POST https://maximind.dev/youtube-to-srt \
  -H "x-api-key: $MAXIMIND_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=VIDEO_ID","language":"en"}' -o out.srt -D -

POST /audio/transcriptions (multipart) — drop-in for OpenAI's /v1/audio/transcriptions. Fields: file (required), model (ignored; always Whisper large-v3), language, response_format (json → {"text": "…"}, text, srt; verbose_json and vtt fall through to json).

curl -X POST https://maximind.dev/audio/transcriptions \
  -H "x-api-key: $MAXIMIND_API_KEY" \
  -F "file=@clip.m4a" -F "response_format=srt" -F "language=en"

GET /v1/transcript — platform captions first, Whisper fallback; see Files for the media API.

Whisper hallucination guard

Silent or empty audio (often a failed in-browser ffmpeg.wasm extraction) makes Whisper invent text. The server pre-checks for silent audio and returns HTTP 422, decodes without conditioning on previous text, and scrubs known hallucinated phrases from the output.

Errors

Status Meaning
400 Unknown type; download failed; URL unreadable.
401 Missing or invalid key; no session.
403 Pending account.
404 Job unknown or expired after 1 h.
422 Silent or empty audio detected.
524 Public edge timeout at about 100 s on a synchronous call. Use jobs.

Limits and costs

  • Public edge: 100 s per request, 100 MB body. Use the jobs API for anything longer.
  • Job results expire one hour after completion.
  • The in-browser extractor in the web app at /app/ has a memory ceiling of about 2 GB; extract audio locally with ffmpeg for very large videos.
  • Defined rates: file_transcribe_per_min 2 credits per minute, translate_per_min 30 credits per minute. The /jobs*, /transcribe-url, /youtube-to-srt and /audio/transcriptions handlers do not currently record usage, so these calls are not metered in practice. See Limits.