API

Send a file, get a transcript back.

The API draws on the same hours as the app - plan hours first, then top-up credits. A file costs exactly its audio length and failed jobs refund automatically.

Create a key in Settings → API keys and get hours on the pricing page.

Authentication

Pass your key as a bearer token on every request. Keys start with fs_live_ and can be revoked in Settings at any time.

Authorization: Bearer fs_live_...

Create a transcription

POST /api/v1/transcriptions - the file is the raw request body (not multipart), with the name in X-Filename. Audio and video both work; files can be up to 10 hours. The response returns immediately while transcription runs in the background - it also shows the hours you have left. If you know the spoken language, pass its ISO code in X-Language (e.g. de, es) - it skips auto-detection, which helps on short or noisy audio. Unknown codes are rejected with a 400.

curl -X POST https://flashscribe.ai/api/v1/transcriptions \
  -H "Authorization: Bearer $FLASHSCRIBE_API_KEY" \
  -H "X-Filename: interview.mp3" \
  -H "X-Language: de" \
  --data-binary @interview.mp3
{
  "id": "1f0f9a4e-...",
  "status": "pending",
  "original_name": "interview.mp3",
  "duration_sec": 1847.3,
  "credits_charged_sec": 1847.3,
  "credits_remaining_sec": 16152.7,
  "created_at": "2026-08-26T14:03:07.512Z"
}

Fetch the result

GET /api/v1/transcriptions/{id} - poll until status is completed (roughly a minute per audio-hour). Completed responses include the full text, word-level timestamps, and sentence-shaped subtitle cues. Transcriptions paid from plan hours or credits also label speakers: words and cues carry a speaker field ("A", "B", …) whenever more than one voice was heard.

curl https://flashscribe.ai/api/v1/transcriptions/1f0f9a4e-... \
  -H "Authorization: Bearer $FLASHSCRIBE_API_KEY"
{
  "id": "1f0f9a4e-...",
  "status": "completed",
  "language": "english",
  "duration_sec": 1847.3,
  "text": "Welcome back to the show - today we're talking about...",
  "words": [{ "word": "Welcome", "start": 1.24, "end": 1.61, "speaker": "A" }, ...],
  "cues":  [{ "start": 1.24, "end": 3.81, "text": "Welcome back to the show -", "speaker": "A" }, ...],
  "speaker_names": { "A": "Dana" }
}

Add ?format=srt, ?format=vtt or ?format=txt to the same URL to get a ready-made file instead of JSON - subtitles carry the speaker name on every change of voice. Documents have no timestamps, so they offer txt only. Asking before the transcript is ready answers 409.

curl -o interview.srt \
  "https://flashscribe.ai/api/v1/transcriptions/1f0f9a4e-...?format=srt" \
  -H "Authorization: Bearer $FLASHSCRIBE_API_KEY"

Rate limits

Two limits, both answering 429 with a Retry-After header saying how many seconds to wait. Rejected keys are capped at 10 a minute per IP address, so a key that works is never held up by one that doesn't. Uploads are capped at 30 a minute per key - far past any real batch, and it only counts the two calls that create a transcription. Reading results is not limited.

Sources

Watch a podcast, a YouTube channel, a blog or a page; new entries are scored 0-100 against your topics and, for articles, get facts with verbatim quotes checked against the text. Nothing is transcribed until you approve it. POST /api/v1/sources takes any link - the feed behind it is discovered for you - and an optional topics list. Plans watch 2 sources (Free) or 25 (paid).

curl -X POST https://flashscribe.ai/api/v1/sources \
  -H "Authorization: Bearer $FLASHSCRIBE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"url": "https://feed.syntax.fm/", "topics": ["JavaScript", "web tooling"]}'
{
  "source": {
    "id": "cmf...",
    "kind": "podcast",
    "url": "https://feed.syntax.fm/",
    "title": "Syntax - Tasty Web Development Treats",
    "topics": ["JavaScript", "web tooling"],
    "paused": false,
    "item_count": 0,
    "new_count": 0
  }
}

GET /api/v1/sources lists what you watch; GET /api/v1/sources/{id}/items returns the entries best score first (?status=new by default, or dismissed, done, all) with their facts; DELETE /api/v1/sources/{id} stops watching.

{
  "items": [{
    "id": "cmf...",
    "title": "1037: WebMCP is here (and you should care)",
    "link": "https://syntax.fm/1037",
    "published_at": "2026-09-09T11:00:00.000Z",
    "duration_sec": 3390,
    "media_url": "https://traffic.megaphone.fm/FSI4063165063.mp3",
    "score": 88,
    "score_reason": "A primary-source walkthrough of the new W3C standard you follow.",
    "facts": [{ "text": "...", "quote": "...", "start": 1204, "end": 1290, "match": "exact" }],
    "status": "new",
    "transcription_id": null
  }]
}

POST /api/v1/items/{id}/approve transcribes an episode or video - priced like any link, by its length - or saves an article's text as a finished document at no charge. Either way you get a transcription_id to poll with the endpoint above. Optional language hint in the body.

curl -X POST https://flashscribe.ai/api/v1/items/cmf.../approve \
  -H "Authorization: Bearer $FLASHSCRIBE_API_KEY"

{ "transcription_id": "1f0f9a4e-...", "status": "pending" }

Errors

Errors are JSON with an error message and a meaningful status code:

Hours are charged when the upload is accepted and refunded in full if transcription fails. API usage always draws on your paid hours - the free daily allowance applies only in the app.