Features

AI transcription

TranscriptFetch reads an existing caption track when one is available and can transcribe the audio when it is not. Choose automatic fallback, captions only, or audio only; long-running work returns a job you can poll or receive by callback.

Choose a transcription mode

The mode field on POST /api/v2/transcripts/video decides where the text may come from:

ModeWhat it does
"auto" (default)Read captions first. If none are available, transcribe the audio; long-running work may return a job.
"captions"Read an existing caption track only. Return 422 when there is none; never return a job.
"audio"Skip the caption lookup and transcribe the audio; long-running work may return a job.
HTTP
POST /api/v2/transcripts/video

{ "video": "https://www.tiktok.com/@user/video/7137723462233555205", "mode": "audio" }

ai_fallback: true remains accepted as a legacy alias for "mode": "audio"; omitted or false maps to "mode": "auto". Send mode or ai_fallback, not both. A request containing both fields returns 400 invalid_request.

Single-video only. mode and ai_fallback apply only to /transcripts/video. They are rejected on /channel, /playlist, and /search.

How the response is chosen

The v2 API tells you where the words came from in data.source:

OutcomeResponse and billing
Captions are available200 with data.source: "captions"; 1 credit.
Audio finishes during the inline wait200 with data.source: "audio"; the audio charge appears in usage.credits_spent.
Audio is still running202 with status: "processing", job_id, and poll_url; nothing is charged yet.
The request cannot be servedA standard error envelope; free.

For standard direct API requests, TranscriptFetch waits up to about 45 seconds when the media duration is known to be 20 minutes or less. When duration is unknown, TikTok and Instagram are also held inline. If the work does not finish during that window—or the media is known to be longer—the job continues and the API returns 202.

RapidAPI requests can be held for up to about 90 seconds and for known media up to one hour because each poll also counts against the caller's RapidAPI quota.

mode: "captions" never starts audio transcription. If no caption track exists, it returns a synchronous 422 and costs 0 credits.

The 202 job envelope

Both automatic fallback and mode: "audio" use the same v2 job envelope:

JSON
{
  "ok": true,
  "request_id": "req_8f2c1a90b3d44e01",
  "status": "processing",
  "job_id": "asr_m3k1x9qz4vb2p7",
  "poll_url": "/api/v2/transcripts/jobs/asr_m3k1x9qz4vb2p7",
  "data": {
    "kind": "transcript_job",
    "video_id": "https://www.tiktok.com/@user/video/7137723462233555205",
    "platform": "tiktok"
  }
}

The poll_url is a relative API path. Branch on status; the initial response uses HTTP 202, while later polls use HTTP 200 for processing, completed, and failed jobs.

Polling the job

GET the poll_url until status is no longer processing:

HTTP
GET /api/v2/transcripts/jobs/asr_m3k1x9qz4vb2p7
JSON
{ "ok": true, "request_id": "req_…", "status": "processing", "job_id": "asr_m3k1x9qz4vb2p7", "data": null }

When the job completes, the poll returns the transcript and the actual delivery charge under usage:

JSON
{
  "ok": true,
  "request_id": "req_…",
  "status": "completed",
  "job_id": "asr_m3k1x9qz4vb2p7",
  "data": {
    "kind": "transcript",
    "video_id": "7137723462233555205",
    "url": "https://www.tiktok.com/@user/video/7137723462233555205",
    "platform": "tiktok",
    "title": "How I make my espresso",
    "channel": "@user",
    "duration": 58,
    "language": "en",
    "thumbnail_url": "https://p16-sign.tiktokcdn.com/…",
    "source": "audio",
    "text": "So the first thing people get wrong is the grind…",
    "segments": [
      { "start": 0.0, "duration": 4.1, "text": "So the first thing people get wrong is the grind" }
    ]
  },
  "usage": { "credits_spent": 1, "balance": 96, "bytes": 4812031 }
}

A completed job includes both text and segments. A failed job returns ok: false, status: "failed", and the standard v2 error object. It still uses HTTP 200 because the job resource was found; an unknown job, or one owned by another account, returns 404.

Worth knowing:

  • Jobs are scoped to your account, not to the key that created them; any of your keys can poll a job, and someone else's job id returns 404.
  • Job-status polling is free. It uses a separate account allowance and does not consume your extraction request allowance or credits.
  • Concurrent requests collapse. Two requests for the same video while a job is live join the same job rather than starting a second one.
  • Re-requesting a finished video is free. A video that already went through AI transcription is served from cache, with a guard against charging twice.
  • Delivery can fail on credits. If your balance runs out between enqueue and completion, the job fails with insufficient_credits. The result stays cached; top up and request the same media again to claim it.
  • RapidAPI callers: every poll is a metered gateway call against your plan, so prefer callback_url below. (The API also holds the connection longer for RapidAPI requests, about 90 seconds, precisely to spare you the poll.)

Webhooks instead of polling

Pass callback_url with the original transcript request to receive the finished result by POST. It must be a public HTTPS URL on the standard port.

JSON
{
  "video": "https://www.tiktok.com/@user/video/7137723462233555205",
  "callback_url": "https://example.com/webhooks/transcriptfetch"
}

A successful delivery has this shape:

JSON
{
  "ok": true,
  "status": "completed",
  "job_id": "asr_m3k1x9qz4vb2p7",
  "data": {
    "kind": "transcript",
    "video_id": "7137723462233555205",
    "url": "https://www.tiktok.com/@user/video/7137723462233555205",
    "platform": "tiktok",
    "title": "How I make my espresso",
    "channel": "@user",
    "duration": 58,
    "language": "en",
    "thumbnail_url": "https://p16-sign.tiktokcdn.com/…",
    "source": "audio",
    "text": "So the first thing people get wrong is the grind…",
    "segments": [ { "start": 0.0, "duration": 4.1, "text": "…" } ]
  }
}

A failed delivery has the same outer fields, with ok: false and an error containing code and message.

  • The webhook body does not include usage or request_id; poll the job when you need those fields.
  • Delivery is attempted up to three times. The job remains pollable even if every callback attempt fails.

What AI transcription costs

Audio transcription is billed by delivered audio length: 1 credit per started minute of audio, minimum 1 credit.

Delivered resultCredits
Caption transcript, any length1
Up to 1 minute of audio transcription1
10 minutes of audio transcription10
1 hour of audio transcription60
4 hours (the transcription limit)240

Failures are free. A 202 has no usage because nothing has been delivered or charged yet. The completed response reports the actual charge in usage.credits_spent.

When retry_with appears

When mode: "captions" finds no caption track, the v2 error includes retry_with only when an audio request is eligible and your available credits can cover the estimated charge:

JSON
{
  "ok": false,
  "request_id": "req_…",
  "error": {
    "code": "no_captions",
    "number": 4103,
    "message": "No caption track is available for this video.",
    "docs": "https://transcriptfetch.com/docs/errors/no_captions",
    "retry_with": { "mode": "audio" }
  }
}

Merge retry_with into the original request body and send it again to start transcription. If the field is absent, changing to audio mode would not currently help—for example, the content has no usable speech, cannot be reached, is not an eligible media input, or the estimated charge exceeds the available balance.