EasyTranscriberEasyTranscriber
Back to blogYouTube Transcript API in Python: A Working Guide
youtubetranscriptapipython

YouTube Transcript API in Python: A Working Guide

Updated EasyTranscriber

To get a YouTube transcript in Python with EasyTranscriber, send a JSON POST request to /api/v1/transcribe on www.easytranscriber.com using HTTPS. Authenticate with a bearer API key and read the returned transcript string. You do not need to install a Python scraping library or automate the YouTube page.

This tutorial uses Python's standard library and the EasyTranscriber REST API. It covers one video, saves both the text and original JSON response, and stops with a readable error when a request fails. Request and response fields were checked against the route implementation and OpenAPI document on 7 November 2026.

Is this the youtube-transcript-api package?

No. youtube-transcript-api is an open-source Python package that fetches captions from YouTube directly from your machine. Its README notes that YouTube blocks most cloud-provider IPs, so deployed code often needs rotating residential proxies. This tutorial calls EasyTranscriber's hosted REST API instead: one HTTPS request with an API key, speech-to-text when a video has no captions, and a separate summary endpoint. It uses credits; the package is free but you run and maintain it.

What do I need before calling the API?

You need Python 3, an EasyTranscriber account, an API key, and a YouTube video you are allowed to access and process. You can start with a free account; API access is not restricted to a paid plan.

Create a key from Dashboard → API Keys. Copy it when it is displayed and keep it private. A key authorizes requests against your account, so do not put it in a blog comment, public repository, browser JavaScript, or shared notebook output.

A free account starts with five credits and refills by one credit daily while its balance is below five. New transcriptions cost one credit, or two for videos longer than two hours. The supported video limit is four hours. Cached transcripts cost zero credits, but requests still pass through authentication and rate limiting.

Set the key as an environment variable in your terminal or through your deployment's secret settings. The example below uses a deliberately invalid value to show the variable name; replace it locally with your own key before a real transcription:

export EASYTRANSCRIBER_API_KEY='et_replace_with_your_key'

This is POSIX shell syntax for macOS or Linux. In another shell, use its equivalent environment-variable mechanism. The Python program itself does not depend on a particular shell, and you do not need to install anything with pip.

What is the exact request and response shape?

The JSON request body has one required field: url. Use a complete YouTube video URL. Authentication belongs in the HTTP header, not inside the JSON object or URL query string.

{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}

The successful response has top-level fields. The following is an illustrative shape, not the result of a recorded authenticated request; the title and transcript text are deliberately shortened:

{
  "video_id": "dQw4w9WgXcQ",
  "video_title": "Video title",
  "transcript": "Transcript text appears here.",
  "method": "innertube",
  "cached": false,
  "credits_used": 1
}

The important detail is transcript: it is a string, not an object with text and segments properties. Read data["transcript"], not data["transcript"]["text"]. The public endpoint does not return a separate timed-segment array.

video_title can be null, so do not require it to name a file. method identifies the retrieval method; current values include innertube, yt-dlp, and deepgram. Preserve that field as provenance rather than interpreting it as a quality score. cached tells you whether this request used a cached transcript, and credits_used records the charge reported for the request.

How do I call the YouTube transcript API in Python?

Save this complete program as transcribe_youtube.py. It reads the video URL from the command line, sends JSON, handles HTTP and connection errors, then writes files only after a successful response. It deliberately makes one request rather than silently retrying a billable operation.

import json
import os
import sys
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen

ENDPOINT = "https://www.easytranscriber.com/api/v1/transcribe"


def get_transcript(video_url, api_key):
    request = Request(
        ENDPOINT,
        data=json.dumps({"url": video_url}).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "Accept": "application/json",
        },
        method="POST",
    )
    try:
        with urlopen(request, timeout=300) as response:
            return json.load(response)
    except HTTPError as error:
        raw = error.read().decode("utf-8", errors="replace")
        try:
            detail = json.loads(raw)["error"]
            message = f"{detail['code']}: {detail['message']}"
        except (ValueError, KeyError, TypeError):
            message = "Non-JSON or unexpected error response"
        raise RuntimeError(f"HTTP {error.code}: {message}") from error
    except OSError as error:
        raise RuntimeError("Connection failed or timed out") from error


def main():
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python3 transcribe_youtube.py YOUTUBE_URL")
    api_key = os.environ.get("EASYTRANSCRIBER_API_KEY")
    if not api_key:
        raise SystemExit("Set EASYTRANSCRIBER_API_KEY before running")

    try:
        data = get_transcript(sys.argv[1], api_key)
    except (RuntimeError, ValueError) as error:
        raise SystemExit(str(error)) from error

    Path("transcript.txt").write_text(data["transcript"], encoding="utf-8")
    Path("transcript.json").write_text(
        json.dumps(data, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    print(f"Saved transcript.txt and transcript.json for {data['video_id']}")
    print(f"Cached: {data['cached']}; credits used: {data['credits_used']}")


if __name__ == "__main__":
    main()

Run it with a video URL enclosed in quotes. Quoting prevents a shell from treating characters such as & in a longer URL as shell syntax:

python3 transcribe_youtube.py 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'

The program uses Python's documented urllib.request interface. Encoding the request body as UTF-8 bytes and declaring application/json matters: an ordinary form body is not the same request format.

The timeout bounds blocking network operations. It is not a promise that every transcript completes within that interval, and a timeout does not prove the server stopped processing. Check the outcome before repeating a request.

How do I save and inspect the transcript?

Open transcript.txt in a text editor and compare its opening and closing passages with the video. Check a section containing names, numbers, or technical terminology. An HTTP success confirms that text was returned, not that every recognized word is correct.

The script writes UTF-8 explicitly so non-English characters survive saving and reopening. The JSON companion preserves metadata alongside the text; ensure_ascii=False keeps those characters readable rather than writing escape sequences for every non-ASCII character.

These fixed filenames are convenient for a first request, but a later successful run overwrites them. Move the files into a project folder before running another video, or deliberately adapt the filenames for your application. Retain the original video URL and retrieval date with your research records as well; the response is not a complete citation record.

For Markdown notes, start a document with your own source heading and paste the returned transcript underneath. Do not assume the API produces the same combined note as the web app's download control. If you prefer a no-code file workflow, see the TXT and Markdown download guide.

How should I handle API errors?

API errors normally use an error object containing code and message. For example, an invalid key produces HTTP 401 with an unauthorized code. The script reads that body instead of assuming every HTTP response contains transcript data.

Use the status to choose your next action:

  • 400: check the URL and JSON body. Missing url, an invalid YouTube URL, or a video exceeding the duration limit needs corrected input.
  • 401: check the bearer key. A missing, invalid, expired, or revoked key is an authentication problem; repeating the same request will not repair it.
  • 402: check your credit balance. Wait for an applicable refill or adjust your plan rather than looping failed requests.
  • 429: reduce request frequency. Free and Plus accounts allow ten requests per minute; Pro allows fifty and Scale allows three hundred.
  • Other failures: read the returned message and distinguish video access, upstream retrieval, and network problems. Do not save an error message as a transcript.

A proxy or hosting layer can return HTML instead of the API's JSON error shape. The example handles that case without printing the entire response body or your authorization header. Keep diagnostic logs useful, but avoid dumping keys and private transcript text into shared logs.

What happens if the video has no captions?

An authenticated request can use speech-to-text when a caption transcript is unavailable. The response still contains a transcript string, with method indicating the processing method. Speech-to-text output does not include timestamps.

This is not a bypass for private, unavailable, or otherwise inaccessible videos. Clear spoken audio also matters. YouTube's automatic caption documentation explains how mispronunciation, background noise, overlapping speech, and language can affect captions; changing the retrieval tool does not eliminate the need to review important text.

The transcribe request does not expose a transcript translation parameter. Summary language and transcript language are separate concepts. Do not add an undocumented language field expecting the transcript to be translated.

Does the transcript API also return an AI summary?

Not in this response. POST /api/v1/summarize is a separate endpoint that accepts transcript text and optionally video_id and language. Its ordinary JSON response contains summary, video_id, and credits_used.

For that summary request, language uses a display name such as "English" or "Spanish", not an ISO code such as "en" or "es". Use "Auto" to match the source content's language. This changes summary output, not the transcript. An unrecognised value such as "en" isn't rejected: it's ignored and the summary follows the source language, as with "Auto".

The REST summary request costs one additional credit. Do not assume the web app's included first summary applies to an independent API call. Consult the API reference before adding summarization, especially if you choose its streaming mode, which is not an ordinary JSON response.

Frequently asked questions

Can I start with a free account?

Yes. Create an account and API key, then use the available credits. Free API access still has credit and request-rate limits; it is not unlimited transcription.

Does the response include subtitle timing?

It does not expose a timed-segment array. Do not invent start and end times or rename the transcript as SRT. Speech-to-text transcripts have no timestamps.

Can I send a playlist URL instead of a video URL?

This endpoint expects a video URL. Use GET /api/v1/youtube/playlist/videos to list a playlist's videos (1 credit per page of results), then send each video URL to /api/v1/transcribe. Start with one verified request before building a larger job.

Try it on your next video

Paste a YouTube link to get a transcript and AI summary. Get 5 credits on signup, then 1 a day up to a balance of 5. No card required.

Start free
5 credits · 1 more every day · no card
Start free