# Get transcript



<MethodPage method="GET" path="/v1/transcripts" credits={1}>
  <MethodSignature name="getTranscript" args={[{ name: "query" }, { name: "options", optional: true }]} returns="TranscriptResponse" />

  Same extraction as `POST /v1/transcripts`, using query parameters. Useful for browser checks and short scripts. Prefer POST when sending `languages` arrays or larger option sets.

  <Security permission="transcripts:read" />

  <SchemaGroup title="Query Parameters">
    <SchemaField name="video_id" type="string" location="query" required>
      11-character YouTube video ID or a full video URL.
    </SchemaField>

    <SchemaField name="format" type="string" optional location="query">
      Output format: `segments` (default), `word_timestamps`, `sentences`, `markdown`, `text`, `srt`, `vtt`, or `json3`.
    </SchemaField>

    <SchemaField name="word_level" type="boolean" optional location="query">
      Include millisecond word offsets on ASR tracks. Default `false`.
    </SchemaField>

    <SchemaField name="languages" type="string" optional location="query">
      Comma-separated ISO language codes, for example `en,es`. `*` means the video's own language, the one spoken in it. Default `*`. For English when the video has it and its own language otherwise, pass `en,*`.
    </SchemaField>

    <SchemaField name="track_policy" type="string" optional location="query">
      Track selection: `manual_first` (default) prefers creator captions and falls back to auto-generated; `asr_first` prefers auto-generated and falls back to creator captions; `exact_only` accepts only a track whose language code matches exactly (`en` does not match `en-GB`), creator captions first. Any other value returns `400 invalid_request`. Languages are tried in order first, then track kind within the matched language: if the English track is auto-generated only, `en,*` returns it rather than moving on. To see which languages and kinds a video has before spending a credit, call the free [basic info](/videos/basic-info) endpoint: each entry in `available_languages` has `kind` `manual` or `asr`. The response `track_kind` says which one you got.
    </SchemaField>
  </SchemaGroup>

  <div id="returns" className="mt-8">
    Response (HTTP 200) [#response-http-200]

    **When `format=word_timestamps`, `segments`, `sentences`, or `json3` (`application/json`):**

    ```jsonc
    {
      "video_id": string,            // Echoed YouTube video ID
      "language": string,            // Negotiated ISO language code (e.g. "en")
      "track_kind": string,          // "asr" (auto-generated) or "manual" (creator-uploaded)
      "duration_seconds": number,    // Total duration of caption track in seconds
      "has_word_level": boolean,     // Whether word-level offsets are present
      "segments": [                  // Array of caption segments with timing offsets
        {
          "text": string,            // Segment transcript text
          "start": number,           // Start offset in seconds
          "end": number,             // End offset in seconds
          "duration": number,        // Segment duration in seconds
          "words": [                 // Word-level timestamps (populated when word_level=true)
            {
              "word": string,        // Individual word
              "start": number,       // Word start offset
              "end": number          // Word end offset
            }
          ]
        }
      ]
    }
    ```

    **When `format=markdown`, `text`, `srt`, or `vtt`:**

    Returns the raw document directly with matching `Content-Type` (e.g. `text/markdown`, `text/plain`, `text/vtt`) without JSON wrapper overhead.
  </div>

  <MethodSamples>
    <LanguageSample language="TypeScript">
      ```ts
      const params = new URLSearchParams({
        video_id: "dQw4w9WgXcQ",
        format: "word_timestamps",
      });

      const response = await fetch(
        `https://api.ytapi.dev/v1/transcripts?${params}`,
        {
          headers: {
            Authorization: `Bearer ${process.env.YT_API_KEY}`,
          },
        },
      );

      const transcript = await response.json();
      console.log(transcript.language, transcript.segments);
      ```
    </LanguageSample>

    <LanguageSample language="Python">
      ```python
      import os
      import requests

      response = requests.get(
          "https://api.ytapi.dev/v1/transcripts",
          headers={"Authorization": f"Bearer {os.environ['YT_API_KEY']}"},
          params={"video_id": "dQw4w9WgXcQ", "format": "word_timestamps"},
      )

      data = response.json()
      print(data["language"], len(data["segments"]))
      ```
    </LanguageSample>

    <LanguageSample language="cURL">
      ```bash
      curl -X GET "https://api.ytapi.dev/v1/transcripts?video_id=dQw4w9WgXcQ&format=word_timestamps" \
        -H "Authorization: Bearer $YT_API_KEY"
      ```
    </LanguageSample>

    <LanguageSample language="Go">
      ```go
      package main

      import (
      	"fmt"
      	"io"
      	"net/http"
      	"os"
      )

      func main() {
      	url := "https://api.ytapi.dev/v1/transcripts?video_id=dQw4w9WgXcQ&format=word_timestamps"
      	req, _ := http.NewRequest("GET", url, nil)
      	req.Header.Set("Authorization", "Bearer "+os.Getenv("YT_API_KEY"))

      	resp, err := http.DefaultClient.Do(req)
      	if err != nil {
      		panic(err)
      	}
      	defer resp.Body.Close()

      	body, _ := io.ReadAll(resp.Body)
      	fmt.Println(string(body))
      }
      ```
    </LanguageSample>
  </MethodSamples>
</MethodPage>
