openapi: 3.2.0 info: title: Transcriptfetch Transcripts API version: '1.0' description: 'Operations tagged Transcripts across 2 of this provider''s published API definitions: transcriptfetch-api-v1-openapi.json, transcriptfetch-api-v2-openapi.json. Each path carries the servers of the definition it was published in.' servers: - url: https://transcriptfetch.com description: Production security: - bearerAuth: [] tags: - name: Transcripts description: Transcript and video-list endpoints paths: /api/v1/transcripts/video: post: tags: - Transcripts summary: Fetch a transcript (YouTube, TikTok, Instagram, podcast, or file URL) description: 'Returns a transcript - text plus timestamped segments. Accepts YouTube, TikTok, and Instagram URLs (or a bare TikTok video id), direct media file URLs, and podcast links (Spotify, Apple Podcasts, or an RSS feed), which resolve to the episode''s audio automatically and come back with a podcast block naming the show and episode. Podcast transcriptions may include best-effort speaker diarization: when labels were produced, each segment carries a `speaker` integer (0, 1, ...) identifying who is talking and a top-level `diarized: true` marks the transcript. Speaker ids are hints from voice separation, not named identification, and non-podcast sources never carry them. When no captions exist the audio is transcribed automatically: when we can determine the media length, media under 20 minutes simply waits (the request is held open for up to 45 seconds) and returns the finished transcript, so no polling is needed; when the length cannot be determined, only short-form platforms (TikTok and Instagram) are held inline. Longer media, or a transcription still running when the 45-second hold expires, returns 202 with a job to poll instead - the work continues either way, so the same request is safe to retry and will hit the cache once it finishes. Supply callback_url to have the finished transcript POSTed to you instead of polling. Every failure carries an ai_fallback block saying whether captions were definitively unavailable and whether retrying would work.' operationId: fetchVideoTranscript parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoRequest' examples: example: value: video: dQw4w9WgXcQ externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: transcript video_id: dQw4w9WgXcQ platform: youtube title: Example video thumbnailUrl: https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg segments: - start: 0 duration: 3.5 text: We're no strangers to love usage: credits_spent: 1 balance: 99 bytes: 14233 '202': description: Accepted - audio transcription started. Poll poll_url for the result, or supply callback_url to have it delivered. content: application/json: schema: $ref: '#/components/schemas/JobAcceptedEnvelope' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v1/transcripts/channel: post: tags: - Transcripts summary: List a channel's videos description: Resolve a channel into a paginated list of videos (metadata only), newest first. Pass since_video_id to get back only the uploads newer than one you've already seen - a page with nothing new is free, which makes this safe to poll on a schedule. operationId: fetchChannelVideos parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChannelRequest' examples: example: value: channel: '@lexfridman' limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: channel_videos videos: - videoId: dQw4w9WgXcQ title: Example video thumbnailUrl: https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg duration: 212 channel: Example Channel next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v1/transcripts/playlist: post: tags: - Transcripts summary: List a playlist's videos description: Resolve a playlist into a paginated list of videos (metadata only). operationId: fetchPlaylistVideos parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PlaylistRequest' examples: example: value: playlist: PLrAXtmRdnEQy6nuLMt9H1aZIuhcGOcZQ4 limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: playlist videos: - videoId: dQw4w9WgXcQ title: Example video thumbnailUrl: https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg duration: 212 channel: Example Channel next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v1/transcripts/search: post: tags: - Transcripts summary: Search YouTube for videos description: Resolve a keyword search into a paginated list of videos (metadata only). operationId: searchVideos parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SearchRequest' examples: example: value: query: how transformers work limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: search videos: - videoId: dQw4w9WgXcQ title: Example video thumbnailUrl: https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg duration: 212 channel: Example Channel next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v1/transcripts/jobs/{jobId}: get: tags: - Transcripts summary: Poll an audio-transcription job description: 'Returns the state of a transcription job created when a request escalated to audio. 200 for any job your account owns (an unknown id, or a job belonging to someone else, is a 404): read the status field, which is "processing" while the job is running, "completed" with the transcript in data, or "failed" with an error. The job_id and a ready-to-use poll_url come back in the 202 that started it. Free to poll on our side - credits are charged once, on delivery (1 credit per started minute of audio, minimum 1). NOTE for RapidAPI subscribers: every poll is still a billable request on your Hub plan, so prefer callback_url, or simply re-send the original request once the job has had time to finish (the result is cached, so it returns instantly and the delivery is charged once, not twice). Most transcriptions never reach this endpoint at all: when we can determine the media length, media under 20 minutes is returned synchronously, and short-form platforms (TikTok and Instagram) are held inline even when we cannot.' operationId: getTranscriptJob parameters: - name: jobId in: path required: true schema: type: string externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/JobStatusEnvelope' examples: example: value: ok: true request_id: req_… status: completed job_id: asr_m3k1x9qz4vb2p7 data: kind: transcript video_id: dQw4w9WgXcQ title: Example video text: We're no strangers to love … segments: - start: 0 duration: 3.5 text: We're no strangers to love usage: credits_spent: 1 balance: 942 bytes: 18244 '401': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v1/transcripts/batch: post: tags: - Transcripts summary: Fetch up to 500 transcripts in one call (plan-dependent) description: 'Fetch a batch of transcripts concurrently. Batch size follows your plan: up to 50 entries on the free tier, Basic and Pro, and up to 500 on Mega and Scale (a request over your plan''s cap fails whole with a clear error before anything is fetched or charged). Accepts the same inputs as /transcripts/video - YouTube, TikTok and Instagram URLs as well as bare YouTube IDs. Charges 1 credit per successfully fetched transcript; failed videos are free. Entries with no caption track are transcribed from audio by default: those come back with outcome "processing" and a job_id, cost nothing on this call, and are charged on delivery at the audio rate. Re-send the same batch once they have finished and the text is returned normally - polling is optional. Send mode: "captions" to keep the old behaviour and have captionless entries fail as no_transcript instead.' operationId: fetchTranscriptsBatch parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchRequest' examples: example: value: video_ids: - dQw4w9WgXcQ - https://www.tiktok.com/@user/video/7137723462233555205 mode: auto externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BatchEnvelope' examples: example: value: ok: true request_id: req_… data: kind: transcript_batch results: - video_id: dQw4w9WgXcQ outcome: ok reason: null message: null title: Example video text: Full transcript text … segments: - start: 0 duration: 3.5 text: Full transcript … bytes: 14233 - video_id: 9bZkp7q19f0 outcome: processing reason: null message: No caption track; transcribing the audio. Re-request this video to collect the text, or poll poll_url. job_id: asr_… poll_url: /api/v1/transcripts/jobs/asr_… title: null text: null segments: null bytes: 0 - video_id: jNQXAC9IVRw outcome: no_transcript reason: no_captions message: 'No caption track (manual or auto-generated) is available. (Seen with mode: "captions"; the default transcribes instead.)' title: null text: null segments: null bytes: 0 usage: credits_spent: 1 balance: 97 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/video: post: tags: - Transcripts summary: Fetch a transcript (YouTube, TikTok, Instagram, podcast, or file URL) description: 'Returns a transcript - text plus timestamped segments. Accepts YouTube, TikTok, and Instagram URLs (or a bare TikTok video id), direct media file URLs, and podcast links (Spotify, Apple Podcasts, or an RSS feed), which resolve to the episode''s audio automatically and come back with a podcast block naming the show and episode. Podcast transcriptions may include best-effort speaker diarization: when labels were produced, each segment carries a `speaker` integer (0, 1, ...) identifying who is talking and a top-level `diarized: true` marks the transcript. Speaker ids are hints from voice separation, not named identification, and non-podcast sources never carry them. When no captions exist the audio is transcribed automatically: when we can determine the media length, media under 20 minutes simply waits (the request is held open for up to 45 seconds) and returns the finished transcript, so no polling is needed; when the length cannot be determined, only short-form platforms (TikTok and Instagram) are held inline. Longer media, or a transcription still running when the 45-second hold expires, returns 202 with a job to poll instead - the work continues either way, so the same request is safe to retry and will hit the cache once it finishes. Supply callback_url to have the finished transcript POSTed to you instead of polling. Every failure carries an ai_fallback block saying whether captions were definitively unavailable and whether retrying would work.' operationId: postApiV2TranscriptsVideo parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoRequest' examples: example: value: video: dQw4w9WgXcQ externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: transcript video_id: dQw4w9WgXcQ platform: youtube title: Example video thumbnailUrl: https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg source: captions segments: - start: 0 duration: 3.5 text: We're no strangers to love usage: credits_spent: 1 balance: 99 bytes: 14233 '202': description: Accepted - audio transcription started. Poll poll_url for the result, or supply callback_url to have it delivered. content: application/json: schema: $ref: '#/components/schemas/JobAcceptedEnvelope_2' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: fetchVideoTranscript servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/channel: post: tags: - Transcripts summary: List a channel's, profile's, show's, or feed's items description: 'Resolve a creator or show into a paginated list of videos or episodes (metadata only), newest first. The platform is read from the input: a YouTube channel, a TikTok or Instagram profile URL, a Spotify or Apple Podcasts show URL, or any podcast RSS/Atom feed URL. Every row''s url is accepted by the transcript and batch endpoints as-is. Pass since_video_id to get back only the items newer than one you''ve already seen - a page with nothing new is free, which makes this safe to poll on a schedule.' operationId: postApiV2TranscriptsChannel parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChannelRequest_2' examples: example: value: channel: '@lexfridman' limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: channel_videos platform: youtube videos: - videoId: dQw4w9WgXcQ url: https://www.youtube.com/watch?v=dQw4w9WgXcQ title: Example video duration: 212 channel: Example Channel publishedAt: '2009-10-25T00:00:00Z' stats: plays: 1600000000 next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: fetchChannelVideos servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/playlist: post: tags: - Transcripts summary: List a playlist's videos or episodes description: 'Resolve a playlist into a paginated list of videos or episodes (metadata only). The platform is read from the input: a YouTube playlist, a TikTok playlist URL, or a Spotify playlist URL. Instagram and Apple Podcasts have no public playlists; list the profile or show through the channel endpoint instead.' operationId: postApiV2TranscriptsPlaylist parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PlaylistRequest_2' examples: example: value: playlist: PLrAXtmRdnEQy6nuLMt9H1aZIuhcGOcZQ4 limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: playlist platform: youtube videos: - videoId: dQw4w9WgXcQ url: https://www.youtube.com/watch?v=dQw4w9WgXcQ title: Example video duration: 212 channel: Example Channel publishedAt: '2009-10-25T00:00:00Z' stats: plays: 1600000000 next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: fetchPlaylistVideos servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/search: post: tags: - Transcripts summary: Search for videos or episodes description: Resolve a keyword search into a paginated list of videos or podcast episodes (metadata only). Searches YouTube by default; set platform to search TikTok, Instagram, Spotify, Apple Podcasts, or the open podcast index instead. Every row carries a url the transcript endpoint accepts unchanged, plus duration and publish time where the source provides them. operationId: postApiV2TranscriptsSearch parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SearchRequest_2' examples: example: value: query: how transformers work platform: tiktok limit: 10 externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SuccessEnvelope' examples: example: value: ok: true request_id: req_… data: kind: video_list source: search platform: tiktok videos: - videoId: '7398765432101234567' url: https://www.tiktok.com/@examplecreator/video/7398765432101234567 title: the post caption duration: 58 channel: '@examplecreator' publishedAt: '2026-08-30T14:02:00Z' stats: plays: 1240000 next_cursor: eyJvIjoxMH0 usage: credits_spent: 1 balance: 98 bytes: 0 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '422': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' '502': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: searchVideos servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/jobs/{jobId}: get: tags: - Transcripts summary: Poll an audio-transcription job description: 'Returns the state of a transcription job created when a request escalated to audio. 200 for any job your account owns (an unknown id, or a job belonging to someone else, is a 404): read the status field, which is "processing" while the job is running, "completed" with the transcript in data, or "failed" with an error. The job_id and a ready-to-use poll_url come back in the 202 that started it. Free to poll on our side - credits are charged once, on delivery (1 credit per started minute of audio, minimum 1). NOTE for RapidAPI subscribers: every poll is still a billable request on your Hub plan, so prefer callback_url, or simply re-send the original request once the job has had time to finish (the result is cached, so it returns instantly and the delivery is charged once, not twice). Most transcriptions never reach this endpoint at all: when we can determine the media length, media under 20 minutes is returned synchronously, and short-form platforms (TikTok and Instagram) are held inline even when we cannot.' operationId: getApiV2TranscriptsJobsByJobId parameters: - name: jobId in: path required: true schema: type: string externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/JobStatusEnvelope_2' examples: example: value: ok: true request_id: req_… status: completed job_id: asr_m3k1x9qz4vb2p7 data: kind: transcript video_id: dQw4w9WgXcQ title: Example video text: We're no strangers to love … segments: - start: 0 duration: 3.5 text: We're no strangers to love usage: credits_spent: 1 balance: 942 bytes: 18244 '401': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: getTranscriptJob servers: - url: https://transcriptfetch.com description: Production /api/v2/transcripts/batch: post: tags: - Transcripts summary: Fetch up to 500 transcripts in one call (plan-dependent) description: 'Fetch a batch of transcripts concurrently. Batch size follows your plan: up to 50 entries on the free tier, Basic and Pro, and up to 500 on Mega and Scale (a request over your plan''s cap fails whole with a clear error before anything is fetched or charged). Accepts the same inputs as /transcripts/video - YouTube, TikTok and Instagram URLs as well as bare YouTube IDs. Charges 1 credit per successfully fetched transcript; failed videos are free. Entries with no caption track are transcribed from audio by default: those come back with outcome "processing" and a job_id, cost nothing on this call, and are charged on delivery at the audio rate. Re-send the same batch once they have finished and the text is returned normally - polling is optional. Send mode: "captions" to keep the old behaviour and have captionless entries fail as no_transcript instead.' operationId: postApiV2TranscriptsBatch parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchRequest' examples: example: value: video_ids: - dQw4w9WgXcQ - https://www.tiktok.com/@user/video/7137723462233555205 mode: auto externalDocs: description: 'Full API reference: fields, response shape, and error codes' url: https://transcriptfetch.com/docs/endpoints responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BatchEnvelope' examples: example: value: ok: true request_id: req_… data: kind: transcript_batch results: - video_id: dQw4w9WgXcQ outcome: ok title: Example video source: captions text: Full transcript text … segments: - start: 0 duration: 3.5 text: Full transcript … bytes: 14233 - video_id: 9bZkp7q19f0 outcome: processing job_id: asr_… poll_url: /api/v2/transcripts/jobs/asr_… - video_id: jNQXAC9IVRw outcome: error error: code: no_captions number: 4103 message: No caption track (manual or auto-generated) is available. docs: https://transcriptfetch.com/docs/errors/no_captions retry_with: mode: audio usage: credits_spent: 1 balance: 97 '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '429': $ref: '#/components/responses/Error' x-operation-id-source: normalized x-operation-id-original: fetchTranscriptsBatch servers: - url: https://transcriptfetch.com description: Production components: schemas: Video: type: object properties: videoId: type: string title: type: string thumbnailUrl: type: string duration: type: - number - 'null' channel: type: - string - 'null' SuccessEnvelope: type: object required: - ok - request_id - data - usage properties: ok: type: boolean enum: - true request_id: type: string data: oneOf: - $ref: '#/components/schemas/TranscriptData' - $ref: '#/components/schemas/VideoListData' - $ref: '#/components/schemas/MeData' discriminator: propertyName: kind usage: $ref: '#/components/schemas/Usage' MeData: type: object required: - kind properties: kind: type: string enum: - me user_id: type: string credits: type: - integer - 'null' description: Remaining balance, or null for unlimited (admin) accounts. BatchResult: type: object properties: video_id: type: string outcome: type: - string - 'null' enum: - ok - no_transcript - processing - error - null reason: type: - string - 'null' description: Structured failure reason (no_captions, unavailable, upstream_error, …); null on success. job_id: type: - string - 'null' description: 'Set on outcome "processing": the audio-transcription job started for a captionless entry. Re-send the batch later or poll poll_url.' poll_url: type: - string - 'null' description: 'Set on outcome "processing": path to poll for the finished transcript.' message: type: - string - 'null' description: Human-readable explanation of the failure; null on success. title: type: - string - 'null' text: type: - string - 'null' segments: type: - array - 'null' items: $ref: '#/components/schemas/Segment' bytes: type: integer VideoListData: type: object required: - kind properties: kind: type: string enum: - video_list source: type: string videos: type: array items: $ref: '#/components/schemas/Video' next_cursor: type: - string - 'null' description: Pass back as `cursor` for the next page; null when exhausted. BatchEnvelope: type: object required: - ok - request_id - data - usage properties: ok: type: boolean enum: - true request_id: type: string data: type: object properties: kind: type: string enum: - transcript_batch results: type: array items: $ref: '#/components/schemas/BatchResult' usage: $ref: '#/components/schemas/Usage' BatchRequest: type: object required: - video_ids properties: video_ids: type: array minItems: 1 maxItems: 500 items: type: string minLength: 1 maxLength: 200 description: 'Video URLs or bare YouTube IDs, from any supported platform. Up to your plan''s batch cap: 50 on free/Basic/Pro, 500 on Mega/Scale. Also accepted as the legacy key videoIds (send one or the other, not both).' mode: type: string enum: - auto - captions description: 'Where each entry''s text may come from. "auto" (the default) reads captions and transcribes the audio when there are none. "captions" reads an existing caption track only, so a captionless video fails as no_transcript - the behaviour batch had before audio fallback existed. Note "audio" is not accepted here: skipping captions for 50 videos at once is a job queue, not a batch fetch.' TranscriptJobData: type: object required: - kind description: 'Placeholder data block on a 202: identifies what is being transcribed while the job runs.' properties: kind: type: string enum: - transcript_job video_id: type: string platform: type: string AiFallback: type: object description: Attached to transcript failures. Distinguishes 'this video has no captions' from 'we could not check', and says whether transcribing the audio would still work. required: - available - captions_unavailable - message properties: available: type: boolean description: True when retrying the same request with ai_fallback:true would actually start a transcription. captions_unavailable: type: boolean description: True only when we reached the video and confirmed no caption track exists. False means we could not check, NOT that captions exist. unavailable_reason: type: string enum: - unsupported_input - no_speech_to_transcribe - content_inaccessible - retry_captions_first - insufficient_credits description: 'Present when available is false: why AI transcription cannot be used.' message: type: string retry_with: type: object description: Merge into the original request body to trigger the fallback. properties: ai_fallback: type: boolean enum: - true cost_credits: type: integer description: Credits charged on successful delivery. Failures are free. AI transcription bills by audio length, so this is an estimate from the measured media length when cost_estimated is true, and otherwise the one-block minimum. cost_estimated: type: boolean description: True when cost_credits came from the media's real duration; false when it is only the minimum, because the length was not known at that point. balance: type: - integer - 'null' description: Remaining balance; null for admins. Usage: type: object properties: credits_spent: type: integer balance: type: - integer - 'null' description: Remaining balance, or null for unlimited (admin) accounts. bytes: type: integer ChannelRequest: type: object required: - channel properties: channel: type: string minLength: 1 maxLength: 2000 description: Channel @handle, /channel/UC… URL, or UC… ID. limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors encode a list offset capped at 2000, so pagination cannot reach past the first 2000 items. since_video_id: type: string minLength: 1 maxLength: 2000 description: Newest video ID you have already seen. The response is trimmed to videos newer than it, and a page with nothing newer costs no credits - so you can poll a channel for new uploads for free. TranscriptData: type: object required: - kind properties: kind: type: string enum: - transcript video_id: type: string title: type: - string - 'null' thumbnailUrl: type: - string - 'null' description: Poster image for the video. TikTok and Instagram serve signed, expiring URLs, so copy the image rather than hotlinking it. diarized: type: boolean description: True when speaker diarization produced labels (podcast transcriptions only); segments then carry `speaker` ids. text: type: - string - 'null' segments: type: - array - 'null' items: $ref: '#/components/schemas/Segment' podcast: type: object description: Present only when the input was a podcast link. Spotify and Apple do not host podcast audio; both read the publisher's RSS feed, so the link is resolved to that feed and the episode's own audio file. These fields say which show and episode were matched, so you can verify the resolution was correct. properties: show: type: - string - 'null' episode: type: - string - 'null' published_at: type: - string - 'null' format: date-time feed_url: type: - string - 'null' format: uri audio_url: type: string format: uri resolved_via: type: string enum: - spotify - apple - rss - direct JobAcceptedEnvelope: type: object required: - ok - request_id - status - job_id - poll_url description: Returned with HTTP 202 when a request escalates to audio transcription. Poll poll_url (or wait for the callback_url delivery); credits are charged on delivery, not here. properties: ok: type: boolean enum: - true request_id: type: string status: type: string enum: - processing job_id: type: string poll_url: type: string description: Path of the job endpoint to poll. data: description: Either a transcript_job placeholder, or a transcript block with null text/segments, depending on how the request escalated. oneOf: - $ref: '#/components/schemas/TranscriptJobData' - $ref: '#/components/schemas/TranscriptData' ai_fallback: type: object description: Why transcription engaged and what delivery will cost, e.g. {engaged, captions_unavailable, message, cost_credits}. usage: $ref: '#/components/schemas/Usage' VideoRequest: type: object required: - video properties: video: type: string minLength: 1 maxLength: 2000 description: 'A video URL or 11-character YouTube video ID. Accepts YouTube (watch, youtu.be, /shorts/), TikTok, and Instagram URLs, plus direct media file URLs (mp4/mp3/wav/…). Also accepts podcast links: a Spotify or Apple Podcasts episode URL, or a podcast RSS feed URL, which is resolved to that episode''s audio automatically. Videos without captions fall back to AI transcription.' mode: type: string enum: - captions - audio - auto default: auto description: 'Where the text may come from. "captions" reads an existing caption track and fails if there is none, which is the only way to avoid audio transcription. "audio" skips captions and transcribes the audio. "auto" (the default) tries captions first and transcribes the audio when there are none. Short media may finish inline after a wait of up to 45 seconds; longer work returns 202 with a job to poll or deliver by callback. Audio is charged only on delivery: 1 credit per started minute of audio, minimum 1 (a 20-minute video is 20; the 4-hour cap is 240). Caption fetches are always 1 credit.' timestamps: default: true oneOf: - type: boolean - type: string enum: - none - segment description: Which form the transcript comes back in. true (the default) returns the `segments` array, each with start, duration and text (podcast episodes transcribed from audio may also carry a `speaker` id per segment - see the podcast notes). false returns a single joined `text` string instead. On the single-transcript 200 exactly one of the two is present, never both, since segments already contain every word the joined text does; job results and batch entries carry both text and segments. The older strings "segment" and "none" mean the same two things and are still accepted. callback_url: type: string format: uri maxLength: 2000 description: 'Where to POST the finished transcript when a request escalates to audio transcription, instead of polling the job. The delivery body is a trimmed envelope - {ok, status, job_id, data} on success, {ok, status, job_id, error} on failure - without the usage and request_id the poll URL adds. When a signing secret is configured on our side, the body is signed with HMAC-SHA256 over the exact bytes and sent as an X-TranscriptFetch-Signature: sha256= header so you can verify it came from us. Must be a public https URL on the standard port; the URL is checked again at delivery time, so an unreachable or private address still gets a 202 but never receives a delivery. The job stays pollable either way, so a missed delivery is never a lost transcript.' ai_fallback: type: boolean default: false deprecated: true description: 'Legacy alias for "mode", still supported. true is identical to "mode": "audio"; omitted or false is "mode": "auto". Send one or the other, not both. Note that false never disabled the fallback - audio was still transcribed when no captions existed - which is why the field was replaced.' SearchRequest: type: object required: - query properties: query: type: string minLength: 1 maxLength: 2000 description: Keyword search query. limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors encode a list offset capped at 2000, so pagination cannot reach past the first 2000 items. PlaylistRequest: type: object required: - playlist properties: playlist: type: string minLength: 1 maxLength: 2000 description: Playlist URL or playlist ID. limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors encode a list offset capped at 2000, so pagination cannot reach past the first 2000 items. JobStatusEnvelope: type: object required: - ok - request_id - status - job_id description: Response of the job poll endpoint. data holds the transcript once status is completed and is null while processing (usage is absent then); a failed job has ok:false and an error block instead. properties: ok: type: boolean request_id: type: string status: type: string enum: - processing - completed - failed job_id: type: string data: description: The finished transcript; null until the job completes. oneOf: - $ref: '#/components/schemas/TranscriptData' - type: 'null' usage: $ref: '#/components/schemas/Usage' error: type: object description: Present when status is failed. properties: code: type: string message: type: string Segment: type: object properties: start: type: number description: Start time in seconds. duration: type: number description: Cue duration in seconds. text: type: string speaker: type: integer description: Speaker id (0, 1, …) on diarized podcast transcripts only; absent everywhere else. ErrorEnvelope: type: object required: - ok - request_id - error properties: ok: type: boolean enum: - false request_id: type: string error: type: object required: - code - message properties: code: type: string enum: - unauthorized - invalid_request - insufficient_credits - rate_limited - idempotency_conflict - unsupported_platform - upstream_unavailable - internal_error - live_stream - was_live - no_audio_stream - audio_too_long - no_speech - captions_disabled - no_captions - age_restricted - members_only - private - unavailable - bot_gate - rate_limited - ip_blocked - region_blocked - timeout - upstream_error - proxy_unavailable - connection - parse_error - invalid_input - drm_protected - unknown message: type: string issues: type: array description: Field-level validation problems, when applicable. items: type: object reason: type: string description: Structured failure reason on transcript endpoints (no_captions, captions_disabled, age_restricted, rate_limited, …). ai_fallback: $ref: '#/components/schemas/AiFallback' Video_2: type: object description: 'One row of a listing. `url` is accepted as-is by the transcript and batch endpoints. The platform is not repeated per row: the page''s `platform` says it.' properties: videoId: type: string url: type: string format: uri title: type: string duration: type: - number - 'null' description: Seconds, when the source reports it. channel: type: - string - 'null' publishedAt: type: - string - 'null' format: date-time description: Upload time. Exact for TikTok, Instagram and podcasts; approximate on YouTube, whose listings only say "2 days ago", so it is exact to the day for recent videos and up to a year off for old ones. stats: type: - object - 'null' description: Engagement counts where the source exposes them. properties: plays: type: - integer - 'null' BatchResult_2: type: object required: - video_id - outcome description: 'One entry per requested video. Exactly three outcomes: "ok" carries the transcript, "processing" carries the audio-transcription job to poll, "error" carries the same error block a request-level failure does.' properties: video_id: type: string outcome: type: string enum: - ok - processing - error title: type: - string - 'null' description: outcome "ok" only. source: type: string enum: - captions - audio description: outcome "ok" only. text: type: - string - 'null' description: outcome "ok" only. segments: type: - array - 'null' items: $ref: '#/components/schemas/Segment' description: outcome "ok" only. bytes: type: integer description: outcome "ok" only. job_id: type: string description: 'outcome "processing" only: the audio-transcription job started for a captionless entry. Re-send the batch later or poll poll_url.' poll_url: type: string description: 'outcome "processing" only: path to poll for the finished transcript.' error: $ref: '#/components/schemas/ErrorBlock' VideoListData_2: type: object required: - kind properties: kind: type: string enum: - video_list source: type: string description: 'Which listing produced the page: channel, playlist or search.' platform: type: string enum: - youtube - tiktok - instagram - spotify - apple - rss description: 'Where the rows came from: the `platform` field on search, the platform detected from the URL on channel and playlist.' videos: type: array items: $ref: '#/components/schemas/Video_2' next_cursor: type: - string - 'null' description: Pass back as `cursor` for the next page; null when exhausted. TranscriptData_2: type: object required: - kind properties: kind: type: string enum: - transcript video_id: type: string title: type: - string - 'null' source: type: string enum: - captions - audio description: 'Where the words came from: an existing caption track, or AI transcription of the audio (billed by length, see usage.credits_spent).' thumbnailUrl: type: - string - 'null' description: Poster image for the video. TikTok and Instagram serve signed, expiring URLs, so copy the image rather than hotlinking it. diarized: type: boolean description: True when speaker diarization produced labels (podcast transcriptions only); segments then carry `speaker` ids. text: type: - string - 'null' segments: type: - array - 'null' items: $ref: '#/components/schemas/Segment' podcast: type: object description: Present only when the input was a podcast link. Spotify and Apple do not host podcast audio; both read the publisher's RSS feed, so the link is resolved to that feed and the episode's own audio file. These fields say which show and episode were matched, so you can verify the resolution was correct. properties: show: type: - string - 'null' episode: type: - string - 'null' published_at: type: - string - 'null' format: date-time feed_url: type: - string - 'null' format: uri audio_url: type: string format: uri resolved_via: type: string enum: - spotify - apple - rss - direct ChannelRequest_2: type: object required: - channel properties: channel: type: string minLength: 1 maxLength: 2000 description: 'YouTube: @handle, /channel/UC… URL, or UC… ID. TikTok: profile URL (tiktok.com/@user). Instagram: profile URL (instagram.com/user/). Spotify: show URL (open.spotify.com/show/…). Apple Podcasts: show URL (podcasts.apple.com/…/id…). Any other URL is fetched as a podcast RSS/Atom feed.' limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors are scoped to the listing (and, for search, the platform) that issued them. Offset-based sources cannot page past the first 2000 items. since_video_id: type: string minLength: 1 maxLength: 2000 description: Newest video ID you have already seen. The response is trimmed to videos newer than it, and a page with nothing newer costs no credits - so you can poll a channel for new uploads for free. JobAcceptedEnvelope_2: type: object required: - ok - request_id - status - job_id - poll_url description: Returned with HTTP 202 when a request escalates to audio transcription. Poll poll_url (or wait for the callback_url delivery); credits are charged on delivery, not here. properties: ok: type: boolean enum: - true request_id: type: string status: type: string enum: - processing job_id: type: string poll_url: type: string description: Path of the job endpoint to poll. data: $ref: '#/components/schemas/TranscriptJobData' SearchRequest_2: type: object required: - query properties: query: type: string minLength: 1 maxLength: 2000 description: Keyword search query. platform: type: string enum: - youtube - tiktok - instagram - spotify - apple - rss default: youtube description: 'Where to search: youtube (default), tiktok, instagram, spotify, apple, or rss. Every result''s url is accepted by the transcript and batch endpoints as-is. rss searches the open podcast index via Apple''s directory and returns episode audio (enclosure) URLs.' limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors are scoped to the listing (and, for search, the platform) that issued them. Offset-based sources cannot page past the first 2000 items. ErrorBlock: type: object required: - code - number - message - docs description: 'Every failure carries this block. Branch on code (a stable string) or number (a stable integer whose thousands digit is the family: 1 request, 2 account, 3 input, 4 content, 5 transient, 9 ours; 5xxx means retry with backoff). At most one of retry_with, details, issues is present. Failures are never charged.' properties: code: type: string enum: - invalid_request - invalid_cursor - unauthorized - idempotency_conflict - not_found - insufficient_credits - batch_too_large - rate_limited - unsupported_platform - endpoint_platform_mismatch - podcast_feed_not_found - audio_ineligible - invalid_input - audio_too_long - drm_protected - private - unavailable - members_only - age_restricted - region_blocked - live_stream - was_live - no_captions - captions_disabled - no_speech - no_audio_stream - upstream_error - timeout - connection - proxy_unavailable - parse_error - upstream_unavailable - internal_error - unknown number: type: integer enum: - 1001 - 1002 - 1101 - 1201 - 1301 - 2001 - 2002 - 2101 - 3001 - 3002 - 3003 - 3004 - 3005 - 3006 - 3007 - 4001 - 4002 - 4003 - 4004 - 4005 - 4101 - 4102 - 4103 - 4104 - 4105 - 4106 - 5001 - 5002 - 5003 - 5004 - 5005 - 5101 - 9001 - 9002 message: type: string description: Prose for humans. Never parse it. docs: type: string format: uri description: https://transcriptfetch.com/docs/errors/ retry_with: type: object description: 'Present when a different request would succeed: the fields to change, e.g. { "mode": "audio" } to transcribe a captionless video, or { "endpoint": "/api/v2/transcripts/video" } for a platform this endpoint does not list.' additionalProperties: true details: type: object description: 'Structured specifics for the few codes that document one: batch_too_large { max, sent }, podcast_feed_not_found { reason, candidates? }.' additionalProperties: true issues: type: array description: Field-level validation problems (invalid_request only). items: type: object PlaylistRequest_2: type: object required: - playlist properties: playlist: type: string minLength: 1 maxLength: 2000 description: 'YouTube: playlist URL or PL… ID. TikTok: playlist URL (tiktok.com/@user/playlist/name-id). Spotify: playlist URL (open.spotify.com/playlist/…).' limit: type: integer minimum: 1 maximum: 50 default: 5 description: Max videos to return per page. Defaults to 5. cursor: type: string maxLength: 256 description: Opaque pagination cursor from a previous response's next_cursor (max 256 characters). Omit for the first page. Cursors are scoped to the listing (and, for search, the platform) that issued them. Offset-based sources cannot page past the first 2000 items. JobStatusEnvelope_2: type: object required: - ok - request_id - status - job_id description: Response of the job poll endpoint. data holds the transcript once status is completed and is null while processing (usage is absent then); a failed job has ok:false and an error block instead. properties: ok: type: boolean request_id: type: string status: type: string enum: - processing - completed - failed job_id: type: string data: description: The finished transcript; null until the job completes. oneOf: - $ref: '#/components/schemas/TranscriptData_2' - type: 'null' usage: $ref: '#/components/schemas/Usage' error: $ref: '#/components/schemas/ErrorBlock' ErrorEnvelope_2: type: object required: - ok - request_id - error properties: ok: type: boolean enum: - false request_id: type: string error: $ref: '#/components/schemas/ErrorBlock' parameters: IdempotencyKey: name: Idempotency-Key in: header required: false description: 'Unique key (≤255 chars) to safely retry a request without being charged twice. Replays return the original response with `Idempotent-Replayed: true`. Reusing a key with a different body is a 409 conflict.' schema: type: string maxLength: 255 responses: Error: description: Error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' securitySchemes: bearerAuth: type: http scheme: bearer description: 'Send your API key as `Authorization: Bearer `.' externalDocs: description: Documentation url: https://transcriptfetch.com/docs x-refined-from: - transcriptfetch-api-v1-openapi.json - transcriptfetch-api-v2-openapi.json