{
  "openapi": "3.1.0",
  "info": {
    "title": "YouTubeTranscriptAPI REST API",
    "version": "2.0.0",
    "summary": "Fetch clean YouTube transcripts without 429 errors. Search, browse channels, and extract playlists through one REST API.",
    "description": "Fetch YouTube transcripts, search YouTube, and list channel and playlist videos as JSON.\n\n**Authentication.** Send an API key on every request: `Authorization: Bearer tk_live_…`. Create keys at https://youtubetranscriptapi.com/dashboard/keys.\n\n**Credits.** Each operation's price is in its description and in `x-credit-cost`. Only successful (200) responses are charged; errors and rate-limited requests never are. Free operations still need a balance of at least 1 credit. Without enough credits you get 402 with a link to billing. Plans: https://youtubetranscriptapi.com/pricing.\n\n**Errors.** Error bodies are `{ \"detail\": \"…\", \"code\": \"…\" }`. On 402 (`insufficient_credits`) `detail` is an object with the shortfall, a billing link and the plans.\n\n**Balance.** Successful responses carry X-Credits-Charged and X-Credits-Remaining. GET /account/whoami returns the plan and balance for free.\n\n**Rate limits.** 300 requests per minute per API key by default, on a sliding one-minute window. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; 429, 408 and 503 add Retry-After.\n\n**Caching.** X-Cache-Status is HIT when a stored result was served and MISS otherwise. Stored responses cost the same as fresh ones.",
    "termsOfService": "https://youtubetranscriptapi.com/legal/terms",
    "contact": {
      "name": "YouTubeTranscriptAPI",
      "url": "https://youtubetranscriptapi.com/contact",
      "email": "hello@youtubetranscriptapi.com"
    }
  },
  "externalDocs": {
    "description": "API reference",
    "url": "https://youtubetranscriptapi.com/docs/api"
  },
  "servers": [
    {
      "url": "https://youtubetranscriptapi.com/api/v2"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Transcripts",
      "description": "Captions of a single video.",
      "externalDocs": {
        "url": "https://youtubetranscriptapi.com/docs/api/transcript"
      }
    },
    {
      "name": "Search",
      "description": "Search across YouTube.",
      "externalDocs": {
        "url": "https://youtubetranscriptapi.com/docs/api/search"
      }
    },
    {
      "name": "Channels",
      "description": "Resolve, search, list and watch one channel.",
      "externalDocs": {
        "url": "https://youtubetranscriptapi.com/docs/api/channel"
      }
    },
    {
      "name": "Playlists",
      "description": "Videos in a public playlist.",
      "externalDocs": {
        "url": "https://youtubetranscriptapi.com/docs/api/playlist"
      }
    },
    {
      "name": "Account",
      "description": "Plan and credit balance.",
      "externalDocs": {
        "url": "https://youtubetranscriptapi.com/docs/api#account"
      }
    }
  ],
  "paths": {
    "/youtube/transcript": {
      "get": {
        "operationId": "getTranscript",
        "summary": "Get Transcripts",
        "description": "Extract YouTube transcripts with or without timestamps. Returns JSON segments or one text string, optionally with video metadata. Stored transcripts return X-Cache-Status: HIT. Costs 1 credit per successful (200) response; errors are never charged.",
        "tags": [
          "Transcripts"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/transcript"
        },
        "x-credit-cost": 1,
        "parameters": [
          {
            "name": "video_url",
            "in": "query",
            "required": true,
            "description": "An 11-character video ID, or a youtube.com, m.youtube.com, music.youtube.com or youtu.be URL (watch, shorts, embed and live links).",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "dQw4w9WgXcQ"
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "Preferred caption language code, such as en, es or de. Longer than 12 characters is truncated. If the video has no track in this language another one may be returned; the response's language field says which.",
            "schema": {
              "type": "string",
              "default": "en"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "json returns an array of segments; text returns the transcript as one string.",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "text"
              ],
              "default": "json"
            }
          },
          {
            "name": "include_timestamp",
            "in": "query",
            "required": false,
            "description": "Include start and duration (seconds) on each segment; with format=text, prefix each line with [start s]. 1, true, yes and on mean true; any other value means false.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "send_metadata",
            "in": "query",
            "required": false,
            "description": "Add a metadata block with the video title, author and thumbnail. 1, true, yes and on mean true; any other value means false.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transcript.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transcript"
                },
                "examples": {
                  "json_with_timestamps_and_metadata": {
                    "summary": "format=json&include_timestamp=true&send_metadata=true",
                    "value": {
                      "video_id": "dQw4w9WgXcQ",
                      "language": "en",
                      "transcript": [
                        {
                          "text": "First caption line",
                          "start": 0,
                          "duration": 4.12
                        },
                        {
                          "text": "Second caption line",
                          "start": 4.12,
                          "duration": 3.85
                        }
                      ],
                      "metadata": {
                        "title": "Rick Astley - Never Gonna Give You Up",
                        "author_name": "RickAstleyVEVO",
                        "author_url": "https://www.youtube.com/@RickAstley",
                        "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"
                      }
                    }
                  },
                  "json_without_timestamps": {
                    "summary": "format=json&include_timestamp=false",
                    "value": {
                      "video_id": "dQw4w9WgXcQ",
                      "language": "en",
                      "transcript": [
                        {
                          "text": "First caption line"
                        },
                        {
                          "text": "Second caption line"
                        }
                      ]
                    }
                  },
                  "text": {
                    "summary": "format=text&include_timestamp=true",
                    "value": {
                      "video_id": "dQw4w9WgXcQ",
                      "language": "en",
                      "transcript": "[0.0s] First caption line\n[4.12s] Second caption line"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The video has no captions, is private, or has been removed (`transcript_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "transcript_not_found": {
                    "value": {
                      "detail": "Transcript unavailable: the video has no captions, is private, or has been removed",
                      "code": "transcript_not_found"
                    }
                  }
                }
              }
            }
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_video_url": {
                    "value": {
                      "detail": "Missing required parameter: video_url",
                      "code": "validation_error"
                    }
                  },
                  "invalid_video": {
                    "value": {
                      "detail": "video_url did not resolve to a valid YouTube video ID",
                      "code": "validation_error"
                    }
                  },
                  "invalid_format": {
                    "value": {
                      "detail": "format must be either json or text",
                      "code": "validation_error"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/search": {
      "get": {
        "operationId": "searchYouTube",
        "summary": "Search YouTube",
        "description": "Search YouTube for videos or channels with rich metadata. Up to 50 results per call. Costs 4 credits per successful (200) response; errors are never charged.",
        "tags": [
          "Search"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/search"
        },
        "x-credit-cost": 4,
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query, 1–200 characters after trimming.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Search for videos or for channels.",
            "schema": {
              "type": "string",
              "enum": [
                "video",
                "channel"
              ],
              "default": "video"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results: all videos for type=video, all channels for type=channel.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResults"
                },
                "examples": {
                  "video": {
                    "summary": "type=video",
                    "value": {
                      "results": [
                        {
                          "type": "video",
                          "videoId": "dQw4w9WgXcQ",
                          "title": "Rick Astley - Never Gonna Give You Up",
                          "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
                          "channelTitle": "Rick Astley",
                          "channelHandle": "@RickAstley",
                          "channelVerified": null,
                          "lengthText": "3:33",
                          "viewCountText": "1.5B views",
                          "publishedTimeText": "14 years ago",
                          "hasCaptions": null,
                          "thumbnails": [
                            {
                              "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/default.jpg",
                              "width": 120,
                              "height": 90
                            },
                            {
                              "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/mqdefault.jpg",
                              "width": 320,
                              "height": 180
                            },
                            {
                              "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
                              "width": 480,
                              "height": 360
                            }
                          ]
                        }
                      ],
                      "result_count": 1
                    }
                  },
                  "channel": {
                    "summary": "type=channel",
                    "value": {
                      "results": [
                        {
                          "type": "channel",
                          "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
                          "title": "TED",
                          "handle": "@TED",
                          "url": "https://www.youtube.com/@TED",
                          "description": "Official channel...",
                          "subscriberCount": "26M subscribers",
                          "verified": true,
                          "rssUrl": "https://www.youtube.com/feeds/videos.xml?channel_id=UCAuUUnT6oDeKwE6v1NGQxug",
                          "thumbnails": []
                        }
                      ],
                      "result_count": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_q": {
                    "value": {
                      "detail": "Missing required parameter: q",
                      "code": "validation_error"
                    }
                  },
                  "invalid_type": {
                    "value": {
                      "detail": "type must be either video or channel",
                      "code": "validation_error"
                    }
                  },
                  "invalid_limit": {
                    "value": {
                      "detail": "limit must be an integer between 1 and 50",
                      "code": "validation_error"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/channel/search": {
      "get": {
        "operationId": "searchChannel",
        "summary": "Search Inside a Channel",
        "description": "Search videos within a specific channel. Costs 1 credit per successful (200) response; errors are never charged.",
        "tags": [
          "Channels"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/channel#search"
        },
        "x-credit-cost": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/Channel"
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query, 1–200 characters after trimming.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Videos from the channel that match the query.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelSearchResults"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Channel not found (`channel_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "channel_not_found": {
                    "value": {
                      "detail": "Channel not found",
                      "code": "channel_not_found"
                    }
                  }
                }
              }
            }
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_channel": {
                    "value": {
                      "detail": "Missing required parameter: channel",
                      "code": "validation_error"
                    }
                  },
                  "invalid_channel": {
                    "value": {
                      "detail": "channel did not resolve to a valid @handle, URL or UC… ID",
                      "code": "validation_error"
                    }
                  },
                  "missing_q": {
                    "value": {
                      "detail": "Missing required parameter: q",
                      "code": "validation_error"
                    }
                  },
                  "invalid_limit": {
                    "value": {
                      "detail": "limit must be an integer between 1 and 50",
                      "code": "validation_error"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/channel/videos": {
      "get": {
        "operationId": "listChannelVideos",
        "summary": "Browse Channel Videos",
        "description": "A channel's most recent uploads, up to 100 videos. Only the first page is served for now. continuation_token is always null; has_more says whether more videos exist. Costs 20 credits per successful (200) response; errors are never charged.",
        "tags": [
          "Channels"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/channel#videos"
        },
        "x-credit-cost": 20,
        "parameters": [
          {
            "$ref": "#/components/parameters/Channel"
          },
          {
            "$ref": "#/components/parameters/Continuation"
          }
        ],
        "responses": {
          "200": {
            "description": "The channel's most recent uploads, newest first.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Listing"
                },
                "examples": {
                  "channel_uploads": {
                    "value": {
                      "results": [
                        {
                          "videoId": "abc123xyz00",
                          "title": "Latest Video",
                          "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
                          "channelTitle": "TED",
                          "channelHandle": "@TED",
                          "lengthText": "15:22",
                          "viewCountText": "3.2M views 2 weeks ago",
                          "thumbnails": [
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/default.jpg",
                              "width": 120,
                              "height": 90
                            },
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/mqdefault.jpg",
                              "width": 320,
                              "height": 180
                            },
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/hqdefault.jpg",
                              "width": 480,
                              "height": 360
                            }
                          ],
                          "index": "0"
                        }
                      ],
                      "playlist_info": {
                        "title": "Uploads from TED",
                        "numVideos": null,
                        "ownerName": "TED",
                        "viewCount": null
                      },
                      "continuation_token": null,
                      "has_more": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Channel not found (`channel_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "channel_not_found": {
                    "value": {
                      "detail": "Channel not found",
                      "code": "channel_not_found"
                    }
                  }
                }
              }
            }
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`), or a later page was requested (`continuation_unavailable`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_channel": {
                    "value": {
                      "detail": "Missing required parameter: channel",
                      "code": "validation_error"
                    }
                  },
                  "invalid_channel": {
                    "value": {
                      "detail": "channel did not resolve to a valid @handle, URL or UC… ID",
                      "code": "validation_error"
                    }
                  },
                  "continuation_unavailable": {
                    "value": {
                      "detail": "Only the first page of channel and playlist listings is available right now. Continuation requests are not served and were not charged.",
                      "code": "continuation_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/channel/latest": {
      "get": {
        "operationId": "getLatestVideos",
        "summary": "Track New Uploads",
        "description": "RSS-based monitoring for the latest 15 uploads of a channel. Free (0 credits), but needs a balance of at least 1 credit. Errors are never charged.",
        "tags": [
          "Channels"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/channel#latest"
        },
        "x-credit-cost": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/Channel"
          }
        ],
        "responses": {
          "200": {
            "description": "The channel's latest uploads, newest first.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LatestUploads"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Channel not found (`channel_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "channel_not_found": {
                    "value": {
                      "detail": "Channel not found",
                      "code": "channel_not_found"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_channel": {
                    "value": {
                      "detail": "Missing required parameter: channel",
                      "code": "validation_error"
                    }
                  },
                  "invalid_channel": {
                    "value": {
                      "detail": "channel did not resolve to a valid @handle, URL or UC… ID",
                      "code": "validation_error"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/playlist/videos": {
      "get": {
        "operationId": "listPlaylistVideos",
        "summary": "Extract Playlist Videos",
        "description": "Up to 100 videos from any public YouTube playlist. Only the first page is served for now. continuation_token is always null; has_more says whether more videos exist. Costs 20 credits per successful (200) response; errors are never charged.",
        "tags": [
          "Playlists"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/playlist"
        },
        "x-credit-cost": 20,
        "parameters": [
          {
            "name": "playlist",
            "in": "query",
            "required": true,
            "description": "Playlist ID (PL…, UU…, LL…, FL…, OL…, RD…, UL… or PU…) or any URL with a list= parameter. A channel's uploads playlist (UU…) is listed like /youtube/channel/videos.",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf"
          },
          {
            "$ref": "#/components/parameters/Continuation"
          }
        ],
        "responses": {
          "200": {
            "description": "Videos in playlist order.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Listing"
                },
                "examples": {
                  "playlist": {
                    "value": {
                      "results": [
                        {
                          "videoId": "abc123xyz00",
                          "title": "Playlist Video",
                          "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
                          "channelTitle": "TED",
                          "channelHandle": "@TED",
                          "lengthText": "10:05",
                          "viewCountText": "1.5M views 6 months ago",
                          "thumbnails": [
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/default.jpg",
                              "width": 120,
                              "height": 90
                            },
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/mqdefault.jpg",
                              "width": 320,
                              "height": 180
                            },
                            {
                              "url": "https://i.ytimg.com/vi/abc123xyz00/hqdefault.jpg",
                              "width": 480,
                              "height": 360
                            }
                          ],
                          "index": "0"
                        }
                      ],
                      "playlist_info": {
                        "title": "Best Tech of 2025",
                        "numVideos": "47 videos",
                        "description": "My picks for the best tech this year",
                        "ownerName": "TED",
                        "viewCount": null
                      },
                      "continuation_token": null,
                      "has_more": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Playlist not found or has no public videos (`playlist_not_found`); `channel_not_found` for a channel uploads playlist (UU…).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "playlist_not_found": {
                    "value": {
                      "detail": "Playlist not found or has no public videos",
                      "code": "playlist_not_found"
                    }
                  }
                }
              }
            }
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`), or a later page was requested (`continuation_unavailable`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_playlist": {
                    "value": {
                      "detail": "Missing required parameter: playlist",
                      "code": "validation_error"
                    }
                  },
                  "invalid_playlist": {
                    "value": {
                      "detail": "playlist did not resolve to a valid playlist URL or ID",
                      "code": "validation_error"
                    }
                  },
                  "continuation_unavailable": {
                    "value": {
                      "detail": "Only the first page of channel and playlist listings is available right now. Continuation requests are not served and were not charged.",
                      "code": "continuation_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/youtube/channel/resolve": {
      "get": {
        "operationId": "resolveChannel",
        "summary": "Resolve Channel Handle",
        "description": "Convert any @handle, URL, or UC… ID to a canonical channel ID. Free (0 credits), but needs a balance of at least 1 credit. Errors are never charged.",
        "tags": [
          "Channels"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api/channel#resolve"
        },
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "input",
            "in": "query",
            "required": true,
            "description": "Channel as an @handle, a bare handle, a UC… channel ID, or a youtube.com channel URL. 1–200 characters after trimming.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "example": "@TED"
          }
        ],
        "responses": {
          "200": {
            "description": "The canonical channel ID. Cached results return X-Cache-Status: HIT.",
            "headers": {
              "X-Cache-Status": {
                "$ref": "#/components/headers/XCacheStatus"
              },
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResolvedChannel"
                },
                "examples": {
                  "handle": {
                    "value": {
                      "channel_id": "UCAuUUnT6oDeKwE6v1NGQxug",
                      "resolved_from": "@TED"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Channel not found (`channel_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "channel_not_found": {
                    "value": {
                      "detail": "Channel not found",
                      "code": "channel_not_found"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "A parameter is missing or invalid (`validation_error`). Not charged.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_input": {
                    "value": {
                      "detail": "Missing required parameter: input",
                      "code": "validation_error"
                    }
                  },
                  "input_too_long": {
                    "value": {
                      "detail": "input must be 1–200 characters",
                      "code": "validation_error"
                    }
                  },
                  "invalid_channel": {
                    "value": {
                      "detail": "channel did not resolve to a valid @handle, URL or UC… ID",
                      "code": "validation_error"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/account/whoami": {
      "get": {
        "operationId": "getAccount",
        "summary": "Plan and balance",
        "description": "The plan and credit balance of the account that owns the key. Free (0 credits) and answers at a zero balance too.",
        "tags": [
          "Account"
        ],
        "externalDocs": {
          "url": "https://youtubetranscriptapi.com/docs/api#account"
        },
        "x-credit-cost": 0,
        "responses": {
          "200": {
            "description": "The account's plan and balance.",
            "headers": {
              "X-Credits-Charged": {
                "$ref": "#/components/headers/XCreditsCharged"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/XCreditsRemaining"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "examples": {
                  "free": {
                    "value": {
                      "user_id": "6f1c0d3e-0000-4000-8000-000000000000",
                      "plan": "free",
                      "plan_status": null,
                      "credits": {
                        "spendable": 87,
                        "plan": 0,
                        "topup": 87
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key (tk_live_…)",
        "description": "API key from the dashboard, sent as `Authorization: Bearer tk_live_…`."
      }
    },
    "parameters": {
      "Channel": {
        "name": "channel",
        "in": "query",
        "required": true,
        "description": "Channel as an @handle, a bare handle, a UC… channel ID, or a youtube.com channel URL (/channel/UC…, /@handle, /c/name or /user/name).",
        "schema": {
          "type": "string",
          "minLength": 1
        },
        "example": "@TED"
      },
      "Continuation": {
        "name": "continuation",
        "in": "query",
        "required": false,
        "description": "Reserved for paging. Not served yet: a request with this parameter gets 422 continuation_unavailable, is not charged and needs no balance.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "XRateLimitLimit": {
        "description": "Requests allowed per minute for this API key.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitRemaining": {
        "description": "Requests left in the current sliding one-minute window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "XRateLimitReset": {
        "description": "Unix time, in seconds, when the current minute window ends.",
        "schema": {
          "type": "integer"
        }
      },
      "XRequestId": {
        "description": "Request ID for support. Echoes the X-Request-Id you sent, otherwise generated.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying: until the window ends on 429, 3 on 408 and 503.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "XCreditsCharged": {
        "description": "Credits this call cost. 0 for free operations. Sent on 200 responses only; errors are never charged.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "XCreditsRemaining": {
        "description": "Spendable credits left after this call. Sent on 200 responses; left out rather than estimated when the balance can't be read.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "XCacheStatus": {
        "description": "HIT when a stored result was served (transcripts, resolved channel IDs), otherwise MISS. Same price either way.",
        "schema": {
          "type": "string",
          "enum": [
            "HIT",
            "MISS"
          ]
        }
      },
      "WWWAuthenticate": {
        "description": "Always Bearer.",
        "schema": {
          "type": "string",
          "enum": [
            "Bearer"
          ]
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The bearer token is missing (`missing_auth`) or invalid or revoked (`invalid_auth`).",
        "headers": {
          "WWW-Authenticate": {
            "$ref": "#/components/headers/WWWAuthenticate"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing_auth": {
                "value": {
                  "detail": "Missing bearer token",
                  "code": "missing_auth"
                }
              },
              "invalid_auth": {
                "value": {
                  "detail": "Invalid or revoked API key",
                  "code": "invalid_auth"
                }
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Not enough credits for this call. detail says why and links to billing. Not charged.",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaymentRequiredError"
            },
            "examples": {
              "insufficient_credits": {
                "value": {
                  "detail": {
                    "message": "This call needs 20 credits; you have 5. Choose a plan to keep going.",
                    "reason": "insufficient_credits",
                    "required": 20,
                    "available": 5,
                    "action_label": "Choose a plan",
                    "action_url": "https://youtubetranscriptapi.com/dashboard/billing?from=402&plan=annual",
                    "plans": {
                      "monthly": {
                        "price_usd": 7,
                        "interval": "month",
                        "credits_per_interval": 1000,
                        "topup_usd_per_1k": 4,
                        "url": "https://youtubetranscriptapi.com/dashboard/billing?plan=monthly"
                      },
                      "annual": {
                        "price_usd": 60,
                        "interval": "year",
                        "credits_per_interval": 15000,
                        "topup_usd_per_1k": 3.5,
                        "refund": "Refundable within 14 days if under 1,000 credits are used",
                        "url": "https://youtubetranscriptapi.com/dashboard/billing?plan=annual"
                      }
                    }
                  },
                  "code": "insufficient_credits"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "API access for this account is paused while a payment dispute is reviewed (`account_frozen`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "account_frozen": {
                "value": {
                  "detail": "API access for this account is paused while a payment dispute is reviewed. Contact support.",
                  "code": "account_frozen"
                }
              }
            }
          }
        }
      },
      "Timeout": {
        "description": "The upstream fetch timed out (`upstream_timeout`). Safe to retry after Retry-After seconds. Not charged.",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "upstream_timeout": {
                "value": {
                  "detail": "Upstream timed out; retry in a few seconds",
                  "code": "upstream_timeout"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded (`rate_limited`). Wait Retry-After seconds, until the current one-minute window ends. Not charged.",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "rate_limited": {
                "value": {
                  "detail": "Rate limit exceeded",
                  "code": "rate_limited"
                }
              }
            }
          }
        }
      },
      "Unavailable": {
        "description": "Temporary failure: `upstream_error`, `internal` (the account check failed) or `config_missing`. Safe to retry after Retry-After seconds. Not charged.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "upstream_error": {
                "value": {
                  "detail": "Upstream unavailable; retry in a few seconds",
                  "code": "upstream_error"
                }
              },
              "internal": {
                "value": {
                  "detail": "Could not check your account right now; retry in a few seconds",
                  "code": "internal"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "detail",
          "code"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "Human-readable message."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code: validation_error, continuation_unavailable, missing_auth, invalid_auth, account_frozen, transcript_not_found, channel_not_found, playlist_not_found, upstream_timeout, rate_limited, upstream_error, config_missing or internal."
          }
        }
      },
      "PaymentRequiredError": {
        "type": "object",
        "required": [
          "detail",
          "code"
        ],
        "properties": {
          "detail": {
            "type": "object",
            "required": [
              "reason",
              "required",
              "available",
              "message",
              "action_label",
              "action_url",
              "plans"
            ],
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "insufficient_credits"
                ]
              },
              "required": {
                "type": "integer",
                "description": "Credits the call needs: its cost, or 1 for a free operation."
              },
              "available": {
                "type": "integer",
                "description": "Spendable credits on the account."
              },
              "message": {
                "type": "string",
                "description": "The shortfall in numbers (\"This call needs 20 credits; you have 5.\"), or that the credits have run out, then whether you need a plan, a top-up or an updated payment method."
              },
              "action_label": {
                "type": "string",
                "description": "Label for the suggested next step."
              },
              "action_url": {
                "type": "string",
                "format": "uri",
                "description": "Billing page for the suggested next step."
              },
              "alt_action_label": {
                "type": "string",
                "description": "Monthly plans only: a second option, the upgrade to Annual."
              },
              "alt_action_url": {
                "type": "string",
                "format": "uri",
                "description": "Where alt_action_label leads."
              },
              "plans": {
                "type": "object",
                "description": "Current price and credits of each plan.",
                "required": [
                  "monthly",
                  "annual"
                ],
                "properties": {
                  "monthly": {
                    "$ref": "#/components/schemas/PlanOffer"
                  },
                  "annual": {
                    "$ref": "#/components/schemas/PlanOffer"
                  }
                }
              }
            }
          },
          "code": {
            "type": "string",
            "enum": [
              "insufficient_credits"
            ]
          }
        }
      },
      "PlanOffer": {
        "type": "object",
        "required": [
          "price_usd",
          "interval",
          "credits_per_interval",
          "topup_usd_per_1k",
          "url"
        ],
        "properties": {
          "price_usd": {
            "type": "number",
            "description": "Price per interval in US dollars."
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "credits_per_interval": {
            "type": "integer",
            "description": "Credits granted each interval: monthly each month, annual up front each year."
          },
          "topup_usd_per_1k": {
            "type": "number",
            "description": "Top-up price per 1,000 credits on this plan."
          },
          "refund": {
            "type": "string",
            "description": "Refund terms, where the plan has them."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Billing page with this plan selected."
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "user_id",
          "plan",
          "plan_status",
          "credits"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "description": "Account ID."
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "monthly",
              "annual"
            ]
          },
          "plan_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Subscription status (active, on_hold, past_due, cancelled, expired…); null without a subscription."
          },
          "credits": {
            "type": "object",
            "required": [
              "spendable",
              "plan",
              "topup"
            ],
            "properties": {
              "spendable": {
                "type": "integer",
                "description": "What the API can spend now: top-up credits, plus plan credits while the plan allows them."
              },
              "plan": {
                "type": "integer",
                "description": "Plan credits, including any paused by a failed renewal."
              },
              "topup": {
                "type": "integer",
                "description": "Free and top-up credits. They never expire."
              }
            }
          }
        }
      },
      "Thumbnail": {
        "type": "object",
        "required": [
          "url",
          "width",
          "height"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          }
        }
      },
      "TranscriptSegment": {
        "type": "object",
        "required": [
          "text"
        ],
        "description": "One caption line. start and duration are present when include_timestamp is true.",
        "properties": {
          "text": {
            "type": "string"
          },
          "start": {
            "type": "number",
            "description": "Start time in seconds."
          },
          "duration": {
            "type": "number",
            "description": "Duration in seconds."
          }
        }
      },
      "TranscriptMetadata": {
        "type": "object",
        "required": [
          "title",
          "author_name",
          "author_url",
          "thumbnail_url"
        ],
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video title."
          },
          "author_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "author_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel URL."
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video thumbnail URL."
          }
        }
      },
      "Transcript": {
        "type": "object",
        "required": [
          "video_id",
          "language",
          "transcript"
        ],
        "properties": {
          "video_id": {
            "type": "string",
            "description": "11-character YouTube video ID."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "Language code of the returned caption track. Can differ from the requested language."
          },
          "transcript": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/TranscriptSegment"
                },
                "description": "format=json"
              },
              {
                "type": "string",
                "description": "format=text: one line per segment, each prefixed with [start s] when include_timestamp is true."
              }
            ]
          },
          "metadata": {
            "$ref": "#/components/schemas/TranscriptMetadata",
            "description": "Present when send_metadata is true."
          }
        }
      },
      "SearchVideoResult": {
        "type": "object",
        "required": [
          "type",
          "videoId",
          "title",
          "channelId",
          "channelTitle",
          "channelHandle",
          "channelVerified",
          "lengthText",
          "viewCountText",
          "publishedTimeText",
          "hasCaptions",
          "thumbnails"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "video"
            ]
          },
          "videoId": {
            "type": "string",
            "description": "11-character YouTube video ID."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video title."
          },
          "channelId": {
            "type": [
              "string",
              "null"
            ],
            "description": "UC… channel ID."
          },
          "channelTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "channelHandle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel @handle, e.g. @TED."
          },
          "channelVerified": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the channel is verified; null when unknown."
          },
          "lengthText": {
            "type": [
              "string",
              "null"
            ],
            "description": "Duration as YouTube shows it, e.g. 3:33."
          },
          "viewCountText": {
            "type": [
              "string",
              "null"
            ],
            "description": "View count as YouTube shows it, e.g. 1.5B views."
          },
          "publishedTimeText": {
            "type": [
              "string",
              "null"
            ],
            "description": "Age as YouTube shows it, e.g. 14 years ago."
          },
          "hasCaptions": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "true when the video has creator captions; null when unknown. Auto-generated captions may exist either way."
          },
          "thumbnails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Thumbnail"
            }
          }
        }
      },
      "SearchChannelResult": {
        "type": "object",
        "required": [
          "type",
          "channelId",
          "title",
          "handle",
          "url",
          "description",
          "subscriberCount",
          "verified",
          "rssUrl",
          "thumbnails"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "channel"
            ]
          },
          "channelId": {
            "type": "string",
            "description": "UC… channel ID."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "handle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel @handle, e.g. @TED."
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel description snippet."
          },
          "subscriberCount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Subscriber count as YouTube shows it, e.g. 26M subscribers."
          },
          "verified": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the channel is verified; null when unknown."
          },
          "rssUrl": {
            "type": "string",
            "format": "uri",
            "description": "The channel's YouTube RSS feed."
          },
          "thumbnails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Thumbnail"
            },
            "description": "Can be empty."
          }
        }
      },
      "SearchResults": {
        "type": "object",
        "required": [
          "results",
          "result_count"
        ],
        "properties": {
          "results": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/SearchVideoResult"
                },
                {
                  "$ref": "#/components/schemas/SearchChannelResult"
                }
              ],
              "discriminator": {
                "propertyName": "type",
                "mapping": {
                  "video": "#/components/schemas/SearchVideoResult",
                  "channel": "#/components/schemas/SearchChannelResult"
                }
              }
            }
          },
          "result_count": {
            "type": "integer",
            "description": "Number of items in results."
          }
        }
      },
      "ChannelSearchResults": {
        "type": "object",
        "required": [
          "results",
          "result_count"
        ],
        "properties": {
          "results": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/SearchVideoResult"
            }
          },
          "result_count": {
            "type": "integer",
            "description": "Number of items in results."
          }
        }
      },
      "ResolvedChannel": {
        "type": "object",
        "required": [
          "channel_id",
          "resolved_from"
        ],
        "properties": {
          "channel_id": {
            "type": "string",
            "pattern": "^UC[A-Za-z0-9_-]{22}$",
            "description": "Canonical UC… channel ID."
          },
          "resolved_from": {
            "type": "string",
            "description": "The input, trimmed."
          }
        }
      },
      "ListingVideo": {
        "type": "object",
        "required": [
          "videoId",
          "title",
          "channelId",
          "channelTitle",
          "channelHandle",
          "lengthText",
          "viewCountText",
          "thumbnails",
          "index"
        ],
        "properties": {
          "videoId": {
            "type": "string",
            "description": "11-character YouTube video ID."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video title."
          },
          "channelId": {
            "type": [
              "string",
              "null"
            ],
            "description": "UC… channel ID."
          },
          "channelTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "channelHandle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel @handle, e.g. @TED."
          },
          "lengthText": {
            "type": [
              "string",
              "null"
            ],
            "description": "Duration as YouTube shows it, e.g. 15:22."
          },
          "viewCountText": {
            "type": [
              "string",
              "null"
            ],
            "description": "View count and age together, e.g. 3.2M views 2 weeks ago."
          },
          "thumbnails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Thumbnail"
            }
          },
          "index": {
            "type": "string",
            "description": "Zero-based position in the listing, as a string."
          }
        }
      },
      "PlaylistInfo": {
        "type": "object",
        "required": [
          "title",
          "numVideos",
          "ownerName",
          "viewCount"
        ],
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Playlist title; \"Uploads from <channel>\" for channel uploads."
          },
          "numVideos": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video count as YouTube shows it, e.g. 47 videos. Null for channel uploads."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Playlist description. Absent for channel uploads."
          },
          "ownerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Playlist owner or channel name."
          },
          "viewCount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Not provided yet; always null."
          }
        }
      },
      "Listing": {
        "type": "object",
        "required": [
          "results",
          "playlist_info",
          "continuation_token",
          "has_more"
        ],
        "properties": {
          "results": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/ListingVideo"
            }
          },
          "playlist_info": {
            "$ref": "#/components/schemas/PlaylistInfo"
          },
          "continuation_token": {
            "type": [
              "string",
              "null"
            ],
            "description": "Always null for now: only the first page is served."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more videos than this page holds."
          }
        }
      },
      "LatestVideo": {
        "type": "object",
        "required": [
          "videoId",
          "title",
          "url",
          "channelId",
          "channelTitle",
          "publishedAt",
          "updatedAt",
          "description",
          "thumbnail_url",
          "viewCount"
        ],
        "properties": {
          "videoId": {
            "type": "string",
            "description": "11-character YouTube video ID."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video title."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Watch URL."
          },
          "channelId": {
            "type": [
              "string",
              "null"
            ],
            "description": "UC… channel ID."
          },
          "channelTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Publish time, ISO 8601. Can be approximate.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last update time, ISO 8601, when known.",
            "format": "date-time"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video description, when known."
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video thumbnail URL."
          },
          "viewCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "View count, when known."
          }
        }
      },
      "LatestUploads": {
        "type": "object",
        "required": [
          "channel_id",
          "channel_title",
          "results",
          "result_count"
        ],
        "properties": {
          "channel_id": {
            "type": "string",
            "description": "Canonical UC… channel ID."
          },
          "channel_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Channel name."
          },
          "results": {
            "type": "array",
            "maxItems": 15,
            "items": {
              "$ref": "#/components/schemas/LatestVideo"
            }
          },
          "result_count": {
            "type": "integer",
            "description": "Number of items in results."
          }
        }
      }
    }
  }
}