{
  "components": {
    "schemas": {
      "BatchStatus": {
        "properties": {
          "batch_id": {
            "title": "Batch Id",
            "type": "string"
          },
          "items": {
            "description": "Every job in the batch, in queue order",
            "items": {
              "$ref": "#/components/schemas/TranscriptionApiResponse"
            },
            "title": "Items",
            "type": "array"
          },
          "title": {
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "batch_id",
          "title",
          "items"
        ],
        "title": "BatchStatus",
        "type": "object"
      },
      "BurnSubtitlesRequest": {
        "properties": {
          "background": {
            "default": false,
            "description": "Draw a semi-transparent black box behind the subtitles for readability. Off = white text with a black outline.",
            "title": "Background",
            "type": "boolean"
          },
          "font_size": {
            "$ref": "#/components/schemas/FontSize",
            "default": "default",
            "description": "Subtitle text size: default ≈7% of video height, large ≈10%, xl ≈12.5%."
          },
          "language_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO-639-1/639-3 code of the video's spoken language, e.g. \"en\"; can improve quality. Omit to auto-detect.",
            "title": "Language Code"
          },
          "lyrics": {
            "default": false,
            "description": "For songs: build the subtitles from the song's published lyrics, timed on the audio. Where several singers sing different lines at once, every line is shown, stacked. Falls back to normal subtitles when no lyrics are found or they do not match what is sung; `lyrics_status` on the result says which happened. Off by default.",
            "title": "Lyrics",
            "type": "boolean"
          },
          "quality": {
            "$ref": "#/components/schemas/Quality",
            "default": "default",
            "description": "Output resolution: default = 720p, hd = 1080p, uhd = 4K"
          },
          "tag_audio_events": {
            "default": false,
            "description": "Mark non-speech audio with tags like (laughter) or [music]. Off by default: the transcriber picks the tag language itself and does not always match the video's language.",
            "title": "Tag Audio Events",
            "type": "boolean"
          },
          "target_languages": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Also produce a burned video per translated language (max 10), e.g. [\"Spanish\", \"French\"]",
            "title": "Target Languages"
          },
          "url": {
            "description": "YouTube video URL",
            "title": "Url",
            "type": "string"
          },
          "verbatim": {
            "default": false,
            "description": "Keep filler words and repetitions instead of cleaning them up",
            "title": "Verbatim",
            "type": "boolean"
          }
        },
        "required": [
          "url"
        ],
        "title": "BurnSubtitlesRequest",
        "type": "object"
      },
      "ErrorResponse": {
        "description": "FastAPI's error body, named so the schema can point at it.",
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorResponse",
        "type": "object"
      },
      "FontSize": {
        "enum": [
          "default",
          "large",
          "xl"
        ],
        "title": "FontSize",
        "type": "string"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "LyricsStatus": {
        "description": "What the `lyrics` option did for a job. Null while it has not run, and on jobs without it.",
        "enum": [
          "found",
          "applied",
          "untrusted",
          "not_found",
          "unavailable"
        ],
        "title": "LyricsStatus",
        "type": "string"
      },
      "PlaylistPreview": {
        "properties": {
          "fits": {
            "description": "Whether the playlist fits the remaining minutes",
            "title": "Fits",
            "type": "boolean"
          },
          "id": {
            "description": "Playlist id, empty for a channel",
            "title": "Id",
            "type": "string"
          },
          "is_channel": {
            "title": "Is Channel",
            "type": "boolean"
          },
          "max_videos": {
            "description": "Cap on how many videos one batch queues",
            "title": "Max Videos",
            "type": "integer"
          },
          "needed_seconds": {
            "description": "Minutes the whole playlist would consume, in seconds",
            "title": "Needed Seconds",
            "type": "integer"
          },
          "remaining_seconds": {
            "description": "What is left on the plan, in seconds",
            "title": "Remaining Seconds",
            "type": "integer"
          },
          "skipped": {
            "description": "Videos that can't be transcribed (private, deleted or live)",
            "title": "Skipped",
            "type": "integer"
          },
          "tab": {
            "description": "Which channel tab was listed: videos, streams or shorts",
            "title": "Tab",
            "type": "string"
          },
          "title": {
            "title": "Title",
            "type": "string"
          },
          "total": {
            "description": "Videos the playlist holds, before the cap and before skipping",
            "title": "Total",
            "type": "integer"
          },
          "videos": {
            "items": {
              "$ref": "#/components/schemas/PlaylistVideo"
            },
            "title": "Videos",
            "type": "array"
          }
        },
        "required": [
          "id",
          "title",
          "total",
          "skipped",
          "max_videos",
          "is_channel",
          "tab",
          "videos",
          "needed_seconds",
          "remaining_seconds",
          "fits"
        ],
        "title": "PlaylistPreview",
        "type": "object"
      },
      "PlaylistVideo": {
        "description": "One video of a playlist listing. Mirrors `youtube_data_service.VideoInfo`.",
        "properties": {
          "duration": {
            "description": "Seconds",
            "title": "Duration",
            "type": "integer"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "is_age_restricted": {
            "default": false,
            "title": "Is Age Restricted",
            "type": "boolean"
          },
          "is_live": {
            "default": false,
            "description": "Live or upcoming broadcasts have no fixed duration to bill against",
            "title": "Is Live",
            "type": "boolean"
          },
          "language": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The uploader's declared audio language, when set",
            "title": "Language"
          },
          "thumbnail": {
            "title": "Thumbnail",
            "type": "string"
          },
          "title": {
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "id",
          "title",
          "duration",
          "thumbnail"
        ],
        "title": "PlaylistVideo",
        "type": "object"
      },
      "Quality": {
        "enum": [
          "default",
          "hd",
          "uhd"
        ],
        "title": "Quality",
        "type": "string"
      },
      "QueuedBatch": {
        "properties": {
          "batch_id": {
            "description": "Poll GET /v1/api/batch/{batch_id} for every job in one call",
            "title": "Batch Id",
            "type": "string"
          },
          "count": {
            "description": "How many jobs were queued",
            "title": "Count",
            "type": "integer"
          },
          "ids": {
            "description": "Job ids, in queue order",
            "items": {
              "type": "string"
            },
            "title": "Ids",
            "type": "array"
          },
          "skipped": {
            "description": "Videos in the playlist that can't be transcribed (private, deleted or live)",
            "title": "Skipped",
            "type": "integer"
          },
          "title": {
            "description": "Playlist or channel title",
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "batch_id",
          "title",
          "count",
          "skipped",
          "ids"
        ],
        "title": "QueuedBatch",
        "type": "object"
      },
      "QueuedJob": {
        "properties": {
          "id": {
            "description": "Job id; poll GET /v1/api/transcription/{id} until `state` is `done`",
            "title": "Id",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "QueuedJob",
        "type": "object"
      },
      "QueuedSubtitlesJob": {
        "properties": {
          "format": {
            "description": "The requested format, echoed back",
            "enum": [
              "srt",
              "webvtt"
            ],
            "title": "Format",
            "type": "string"
          },
          "id": {
            "description": "Job id; poll GET /v1/api/transcription/{id} until `state` is `done`",
            "title": "Id",
            "type": "string"
          }
        },
        "required": [
          "id",
          "format"
        ],
        "title": "QueuedSubtitlesJob",
        "type": "object"
      },
      "State": {
        "enum": [
          "waiting",
          "preparing",
          "downloading",
          "converting",
          "uploading",
          "processing",
          "merging",
          "aligning",
          "translating",
          "burning",
          "done",
          "failed"
        ],
        "title": "State",
        "type": "string"
      },
      "SubtitlesRequest": {
        "properties": {
          "format": {
            "default": "srt",
            "description": "Which field to read from the result; both are always populated",
            "enum": [
              "srt",
              "webvtt"
            ],
            "title": "Format",
            "type": "string"
          },
          "language_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO-639-1/639-3 code of the video's spoken language, e.g. \"en\"; can improve quality. Omit to auto-detect.",
            "title": "Language Code"
          },
          "lyrics": {
            "default": false,
            "description": "For songs: build the subtitles from the song's published lyrics, timed on the audio. Where several singers sing different lines at once, every line is shown, stacked. Falls back to normal subtitles when no lyrics are found or they do not match what is sung; `lyrics_status` on the result says which happened. Off by default.",
            "title": "Lyrics",
            "type": "boolean"
          },
          "tag_audio_events": {
            "default": false,
            "description": "Mark non-speech audio with tags like (laughter) or [music]. Off by default: the transcriber picks the tag language itself and does not always match the video's language.",
            "title": "Tag Audio Events",
            "type": "boolean"
          },
          "target_languages": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Also translate into these languages (max 10), e.g. [\"Spanish\", \"French\"]",
            "title": "Target Languages"
          },
          "url": {
            "description": "YouTube video URL",
            "title": "Url",
            "type": "string"
          },
          "verbatim": {
            "default": false,
            "description": "Keep filler words and repetitions instead of cleaning them up",
            "title": "Verbatim",
            "type": "boolean"
          }
        },
        "required": [
          "url"
        ],
        "title": "SubtitlesRequest",
        "type": "object"
      },
      "TranscribePlaylistRequest": {
        "properties": {
          "background": {
            "default": false,
            "description": "Draw a semi-transparent black box behind burned-in subtitles",
            "title": "Background",
            "type": "boolean"
          },
          "font_size": {
            "$ref": "#/components/schemas/FontSize",
            "default": "default",
            "description": "Subtitle text size for burned jobs"
          },
          "language_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO-639-1/639-3 code of the video's spoken language, e.g. \"en\"; can improve quality. Omit to auto-detect.",
            "title": "Language Code"
          },
          "lyrics": {
            "default": false,
            "description": "Subtitles/burned only. For songs: build the subtitles from the song's published lyrics, timed on the audio. Where several singers sing different lines at once, every line is shown, stacked. Falls back to normal subtitles when no lyrics are found or they do not match what is sung; `lyrics_status` on the result says which happened. Off by default.",
            "title": "Lyrics",
            "type": "boolean"
          },
          "quality": {
            "$ref": "#/components/schemas/Quality",
            "default": "default",
            "description": "Video quality for burned jobs (default/hd/uhd)"
          },
          "tag_audio_events": {
            "default": false,
            "description": "Mark non-speech audio with tags like (laughter) or [music]. Off by default: the transcriber picks the tag language itself and does not always match the video's language.",
            "title": "Tag Audio Events",
            "type": "boolean"
          },
          "target_languages": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Extra subtitle languages (subtitles/burned only, max 10)",
            "title": "Target Languages"
          },
          "type": {
            "$ref": "#/components/schemas/TranscriptionType",
            "default": "transcript",
            "description": "What to produce for every video in the playlist"
          },
          "url": {
            "description": "YouTube playlist or channel URL",
            "title": "Url",
            "type": "string"
          },
          "verbatim": {
            "default": false,
            "description": "Keep filler words and repetitions instead of cleaning them up",
            "title": "Verbatim",
            "type": "boolean"
          }
        },
        "required": [
          "url"
        ],
        "title": "TranscribePlaylistRequest",
        "type": "object"
      },
      "TranscribeRequest": {
        "properties": {
          "language_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO-639-1/639-3 code of the video's spoken language, e.g. \"en\"; can improve quality. Omit to auto-detect.",
            "title": "Language Code"
          },
          "tag_audio_events": {
            "default": false,
            "description": "Mark non-speech audio with tags like (laughter) or [music]. Off by default: the transcriber picks the tag language itself and does not always match the video's language.",
            "title": "Tag Audio Events",
            "type": "boolean"
          },
          "url": {
            "description": "YouTube video URL",
            "title": "Url",
            "type": "string"
          },
          "verbatim": {
            "default": false,
            "description": "Keep filler words and repetitions instead of cleaning them up",
            "title": "Verbatim",
            "type": "boolean"
          }
        },
        "required": [
          "url"
        ],
        "title": "TranscribeRequest",
        "type": "object"
      },
      "TranscriptionApiResponse": {
        "description": "Public-facing subset of Transcription returned by the token API and MCP server.",
        "properties": {
          "background": {
            "title": "Background",
            "type": "boolean"
          },
          "burned_video_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Burned Video Url"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "duration": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "font_size": {
            "$ref": "#/components/schemas/FontSize"
          },
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "is_preview": {
            "default": false,
            "title": "Is Preview",
            "type": "boolean"
          },
          "lyrics": {
            "default": false,
            "title": "Lyrics",
            "type": "boolean"
          },
          "lyrics_status": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LyricsStatus"
              },
              {
                "type": "null"
              }
            ]
          },
          "preview_minutes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Preview Minutes"
          },
          "progress": {
            "title": "Progress",
            "type": "integer"
          },
          "quality": {
            "$ref": "#/components/schemas/Quality"
          },
          "srt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Srt"
          },
          "state": {
            "$ref": "#/components/schemas/State"
          },
          "tag_audio_events": {
            "default": false,
            "title": "Tag Audio Events",
            "type": "boolean"
          },
          "translations": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/TranslationApiResponse"
            },
            "title": "Translations",
            "type": "array"
          },
          "txt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Txt"
          },
          "type": {
            "$ref": "#/components/schemas/TranscriptionType"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          },
          "verbatim": {
            "title": "Verbatim",
            "type": "boolean"
          },
          "video_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Video Url"
          },
          "webvtt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Webvtt"
          }
        },
        "required": [
          "id",
          "state",
          "progress",
          "type",
          "verbatim",
          "quality",
          "font_size",
          "background",
          "created_at",
          "updated_at"
        ],
        "title": "TranscriptionApiResponse",
        "type": "object"
      },
      "TranscriptionType": {
        "enum": [
          "transcript",
          "subtitles",
          "burned"
        ],
        "title": "TranscriptionType",
        "type": "string"
      },
      "TranslationApiResponse": {
        "description": "Public-facing subset of TranscriptionTranslation returned by the token API and MCP server.",
        "properties": {
          "burned_video_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Burned Video Url"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "language": {
            "title": "Language",
            "type": "string"
          },
          "srt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Srt"
          },
          "state": {
            "$ref": "#/components/schemas/TranslationState"
          },
          "webvtt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Webvtt"
          }
        },
        "required": [
          "language",
          "state"
        ],
        "title": "TranslationApiResponse",
        "type": "object"
      },
      "TranslationState": {
        "enum": [
          "pending",
          "translating",
          "burning",
          "done",
          "failed"
        ],
        "title": "TranslationState",
        "type": "string"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      }
    },
    "securitySchemes": {
      "HTTPBearer": {
        "description": "An API token from https://youtubetotext.ai (Settings → API tokens), sent as `Authorization: Bearer ytt_...`.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://youtubetotext.ai/docs/"
  },
  "info": {
    "contact": {
      "name": "YoutubeToText",
      "url": "https://youtubetotext.ai/contact/"
    },
    "description": "Transcribe YouTube videos from your own code: plain text, subtitles (SRT and\nWebVTT), subtitles burned into the video, and playlists in one batch.\n\nEvery endpoint takes an API token from\n[your account settings](https://youtubetotext.ai) as `Authorization: Bearer ytt_...`.\n\nTranscription is asynchronous: the POST endpoints return a job `id` straight\naway, then you poll `GET /v1/api/transcription/{id}` until `state` is `done`\n(or `failed`) and read `txt`, `srt` / `webvtt`, or `burned_video_url` from the\nresult.",
    "termsOfService": "https://youtubetotext.ai/terms-of-service/",
    "title": "YouTube to Text API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/v1/api/batch/{id}": {
      "get": {
        "description": "Status of every job in a playlist batch, in queue order (no transcript text; fetch each job by id for that).",
        "operationId": "get_batch",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchStatus"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such batch on this account"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get a playlist batch",
        "tags": [
          "Playlists"
        ]
      }
    },
    "/v1/api/burn-subtitles": {
      "post": {
        "description": "Queue a video to be downloaded with subtitles burned in.",
        "operationId": "burn_subtitles",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BurnSubtitlesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuedJob"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Burn subtitles into a video",
        "tags": [
          "Subtitles"
        ]
      }
    },
    "/v1/api/playlist": {
      "get": {
        "description": "What a playlist link would queue: its public videos (max 25), the minutes needed and whether they fit your plan.",
        "operationId": "playlist_info",
        "parameters": [
          {
            "description": "YouTube playlist or channel URL",
            "in": "query",
            "name": "url",
            "required": true,
            "schema": {
              "description": "YouTube playlist or channel URL",
              "title": "Url",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/TranscriptionType",
              "default": "transcript"
            }
          },
          {
            "in": "query",
            "name": "quality",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Quality",
              "default": "default"
            }
          },
          {
            "in": "query",
            "name": "target_languages",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Target Languages"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaylistPreview"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Preview a playlist",
        "tags": [
          "Playlists"
        ]
      }
    },
    "/v1/api/subtitles": {
      "post": {
        "description": "Queue a YouTube video for subtitle generation (SRT and WebVTT).",
        "operationId": "subtitles",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubtitlesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuedSubtitlesJob"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Generate subtitles",
        "tags": [
          "Subtitles"
        ]
      }
    },
    "/v1/api/transcribe": {
      "post": {
        "description": "Queue a YouTube video for plain text transcription. Returns a job id.",
        "operationId": "transcribe",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscribeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuedJob"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Transcribe a video",
        "tags": [
          "Transcription"
        ]
      }
    },
    "/v1/api/transcribe-playlist": {
      "post": {
        "description": "Queue every public video of a YouTube playlist (max 25) as separate jobs. Returns a batch id and the job ids; poll each job, or GET /batch/{id} for all of them.",
        "operationId": "transcribe_playlist",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscribePlaylistRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuedBatch"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Transcribe a playlist",
        "tags": [
          "Playlists"
        ]
      }
    },
    "/v1/api/transcription/{id}": {
      "get": {
        "description": "Poll a transcription's status and result by id.",
        "operationId": "get_transcription",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptionApiResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing or invalid API token"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such transcription on this account"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded: 60 job requests and 300 polls per minute per account, shared with the MCP server",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed per minute for this class of call",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get a transcription",
        "tags": [
          "Transcription"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.youtubetotext.ai"
    }
  ],
  "tags": [
    {
      "description": "Queue a video, poll the job, read the text.",
      "externalDocs": {
        "url": "https://youtubetotext.ai/docs/"
      },
      "name": "Transcription"
    },
    {
      "description": "SRT and WebVTT cues, or the captions burned into the video.",
      "externalDocs": {
        "url": "https://youtubetotext.ai/docs/api-reference/"
      },
      "name": "Subtitles"
    },
    {
      "description": "Preview a playlist, queue it as one batch, read every job.",
      "externalDocs": {
        "url": "https://youtubetotext.ai/docs/playlists/"
      },
      "name": "Playlists"
    }
  ]
}
