TranscriptFetch Python SDK
Official, typed Python client for the TranscriptFetch API: fetch transcripts as clean, structured data, plus channel, playlist and search listings across YouTube, TikTok, Instagram, Spotify, Apple Podcasts and RSS. Sync + async, fully type-hinted.
Transcripts come from YouTube, TikTok, Instagram, podcasts, or a direct media file URL (mp3/mp4/wav and friends). A podcast link (a Spotify or Apple Podcasts episode URL, or an RSS feed URL) is resolved to that episode's audio automatically. Channel and playlist take a URL from any of those platforms and detect it; search is YouTube by default, or any of them via platform=.
pip install transcriptfetch-sdk
Quickstart
from transcriptfetch import TranscriptFetch
# api_key falls back to the TRANSCRIPTFETCH_API_KEY env var
tf = TranscriptFetch(api_key="tf_live_...")
t = tf.transcripts.video("https://youtu.be/aircAruvnKk") # or a TikTok / Instagram / podcast / file URL
print(t.title)
print(t.text)
for seg in t.segments:
print(f"[{seg.start:.1f}] {seg.text}")
print("credits left:", t.usage.balance)
Get an API key (100 free credits) at https://transcriptfetch.com/app. One credit per successful fetch; failed/blocked/no-transcript requests are free.
Endpoints
tf.transcripts.video(video) # single transcript (text + segments)
tf.transcripts.batch(video_ids, mode=) # up to 50 transcripts in one call
tf.transcripts.channel(channel, limit=, cursor=) # a channel's or creator's videos (metadata)
tf.transcripts.playlist(playlist, limit=, cursor=) # a playlist's videos
tf.transcripts.search(query, platform=, limit=, cursor=) # keyword search, YouTube by default
tf.transcripts.job(job_id) # poll an audio-transcription job (free)
tf.me() # validate the key + read the balance (free)
tf.health() # unauthenticated liveness probe
video and batch take a YouTube, TikTok or Instagram URL, a podcast link (Spotify or Apple Podcasts episode, or an RSS feed), a direct media file URL, or a bare YouTube ID. channel/playlist take a YouTube, TikTok, Instagram, Spotify, Apple Podcasts or RSS URL (or a YouTube @handle/PL… ID) and detect the platform from it. search takes platform="youtube" | "tiktok" | "instagram" | "spotify" | "apple" | "rss" (rss is the open podcast index). Every listed row carries a url that video/batch accept as-is, plus published_at and stats.plays where the source exposes them; the page carries platform.
page = tf.transcripts.search("lofi hip hop", platform="tiktok", limit=10)
for v in page.videos:
print(v.title, v.published_at, v.stats.plays if v.stats else None)
t = tf.transcripts.video(v.url)
Sources without captions (including every podcast)
When a source has no captions, the API transcribes its audio and answers with a job instead of a transcript. That comes back as a Transcript with status == "processing" and a job_id; poll it for free until it completes. Podcast audio never has captions, so a podcast always takes this path:
import time
t = tf.transcripts.video("https://www.tiktok.com/@user/video/7137723462233555205")
while t.status == "processing":
time.sleep(3)
t = tf.transcripts.job(t.job_id)
print(t.text)
A transcript resolved from a podcast link also carries a podcast block, so the show and episode survive the round trip (otherwise the result would be titled after the mp3 filename):
t = tf.transcripts.video("https://podcasts.apple.com/us/podcast/…")
print(t.platform) # "podcast"
print(t.podcast.show, "-", t.podcast.episode)
Podcast transcriptions include best-effort speaker diarization: each segment may carry a speaker integer (0, 1, …) identifying who is talking. The ids are hints from voice separation, not named identification, and non-podcast sources never carry them.
Batch works the same way by default (mode="auto"): entries with no caption track are transcribed from audio, come back with outcome == "processing" and a job_id, cost nothing on that call, and are charged on delivery at the audio rate. Re-send the same batch once the jobs have had time to finish and the text comes back normally — or poll each job_id with tf.transcripts.job(). Pass mode="captions" to read existing caption tracks only, in which case a captionless video fails as outcome == "error" with error.code == "no_captions" (and error.retry_with naming the audio mode):
res = tf.transcripts.batch(ids) # captionless entries -> "processing" + job_id
pending = [r.job_id for r in res.results if r.outcome == "processing"]
res = tf.transcripts.batch(ids, mode="captions") # captions only, no audio fallback
Pagination
List endpoints are cursor-paginated. Iterate every result without managing cursors:
for video in tf.transcripts.iter_channel("@lexfridman", limit=10):
print(video.video_id, video.title)
Or page manually via page.next_cursor and the cursor= argument.
Async
import asyncio
from transcriptfetch import AsyncTranscriptFetch
async def main():
async with AsyncTranscriptFetch() as tf:
t = await tf.transcripts.video("aircAruvnKk")
print(t.text)
async for v in tf.transcripts.iter_search("how transformers work", limit=10):
print(v.title)
asyncio.run(main())
Errors
All errors subclass TranscriptFetchError. API errors carry .status, .code, .number (the thousands digit is the family; 5xxx means retry), .message, .docs, .retry_with (the request change that would succeed, when there is one), .details, .request_id, and a .retryable property:
from transcriptfetch import (
AuthenticationError, InsufficientCreditsError, InvalidRequestError,
RateLimitError, IdempotencyConflictError, UpstreamUnavailableError,
InternalServerError, APIError, APIConnectionError, APITimeoutError,
)
try:
tf.transcripts.video("bad")
except InsufficientCreditsError:
... # 402: top up at /pricing
except RateLimitError as e:
print(e.retry_after) # 429
except APIError as e:
print(e.status, e.code, e.request_id)
Reliability
- Automatic retries on
429(honoringRetry-After) and5xx, with exponential backoff + jitter (max_retries=2by default). - Idempotency: every write auto-sends an
Idempotency-Keyso a retried request is never double-charged. Override per call withidempotency_key=.... - Configurable:
TranscriptFetch(api_key=..., base_url=..., timeout=30, max_retries=2). Both clients are context managers and accept a customhttp_client=(httpx).
Development
pip install -e ".[dev]"
ruff check . && mypy src && pytest
Tests are fully mocked (no network). MIT licensed.
Metadata
Release files for transcriptfetch-sdk 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| transcriptfetch_sdk-2.1.0.tar.gz | 23.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| transcriptfetch_sdk-2.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.4 kB
Release files / transcriptfetch_sdk-2.1.0.tar.gz
| Download URL | transcriptfetch_sdk-2.1.0.tar.gz |
|---|---|
| Size | 23.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
40d189c67de2e868f7d98bab11a18712809ab356153c6ce7ceb8c40f4c8b512b
|
|
BLAKE2b-256 checksum How to use checksums |
59c46f4f6c493f2d61328542d4d9f6852ec41f8fa6c4aeaf40cf58559496a474
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / transcriptfetch_sdk-2.1.0-py3-none-any.whl
| Download URL | transcriptfetch_sdk-2.1.0-py3-none-any.whl |
|---|---|
| Size | 20.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5544b0ebac77ccdd303eff2fc47429094738abd06a694cbffb8396b0cc22e81c
|
|
BLAKE2b-256 checksum How to use checksums |
07d6dcdf739b77892b6072616b28f1514d176aa05a69b73e8ca6167ef130ddb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log