# YouTube (/docs/youtube) YouTube [#youtube] The widest surface on the API after LinkedIn: every tab of a channel, full video detail, threaded comments, two kinds of search, trending, transcripts and subtitle files, the raw audio/video/thumbnail streams, and batch endpoints that take a thousand ids at a time. Base URL: `/v1/youtube/...` Choosing between `/v1/youtube/video/subtitles` (1cr caption files) and `/v1/youtube/video/transcript` (3cr cleaned transcript)? See [Which endpoint should I use?](/docs/choosing-endpoints.md) . How do I get started with YouTube data? [#how-do-i-get-started-with-youtube-data] 1\. Fetch a profile [#1-fetch-a-profile] ```bash curl "https://www.socialcrawl.dev/v1/youtube/channel?handle=MrBeast" \ -H "x-api-key: sc_your_api_key_here" ``` 2\. Fetch their content [#2-fetch-their-content] ```bash curl "https://www.socialcrawl.dev/v1/youtube/channel/videos?handle=MrBeast" \ -H "x-api-key: sc_your_api_key_here" ``` 3\. Read computed fields [#3-read-computed-fields] When an endpoint supports a computed field and the required source inputs are present, the unified response includes that optional field. Depending on the endpoint, optional fields can include `engagement_rate`, `language`, `content_category`, and `estimated_reach`. See [Computed fields](/docs/computed-fields.md) for formulas, clamping rules, and null semantics. A channel has five tabs, and each has its own endpoint [#a-channel-has-five-tabs-and-each-has-its-own-endpoint] `GET /v1/youtube/channel/videos` returns **regular uploads only**. Everything else on a channel lives behind its own call, and this is the single most common cause of "where did the rest of their content go": | What you want | Endpoint | | --------------------- | ----------------------------------------- | | The account snapshot | `GET /v1/youtube/channel` | | Regular uploads | `GET /v1/youtube/channel/videos` | | Shorts | `GET /v1/youtube/channel/shorts` | | Live and past streams | `GET /v1/youtube/channel/lives` | | Playlists | `GET /v1/youtube/channel/playlists` | | Text and image posts | `GET /v1/youtube/channel/community-posts` | All five channel-scoped endpoints take **either `channelId` or `handle`** — one is required — and all cost 1 credit. `channel` additionally accepts a `url`. They page with `continuationToken`, YouTube's own token rather than the universal cursor: pass back what the previous response returned. `channel/videos` returns light rows by default. Add `includeExtras=true` to fill in `post.engagement.likes`, `post.engagement.comments`, and `post.ext.description` on each row — it slows the response slightly but saves a `video` call per row. ```bash # Shorts, not uploads curl "https://www.socialcrawl.dev/v1/youtube/channel/shorts?handle=MrBeast" \ -H "x-api-key: sc_your_api_key_here" # Uploads with engagement counts attached curl "https://www.socialcrawl.dev/v1/youtube/channel/videos?handle=MrBeast&includeExtras=true&sort=popular" \ -H "x-api-key: sc_your_api_key_here" ``` `GET /v1/youtube/profile/full` (5cr) collapses the first two rows into one call: the channel, its recent videos, and computed metrics — average engagement rate, posting cadence with the window it was measured over, the top post, and the format mix. `posts` sets how many videos to average over (1-100, default 25). The profile leg is the only critical one, so a failure to fetch videos still returns the profile with null post-dependent metrics and a `legs[]` explaining why. Two searches, and they return different things [#two-searches-and-they-return-different-things] * **`GET /v1/youtube/search`** (1cr) is the general one. It returns **every content kind** — videos, channels, playlists, shorts, live streams — with `uploadDate`, `sortBy`, `type`, `duration`, and `region` filters. * **`GET /v1/youtube/search/advanced`** (1cr) returns **videos only**, but with the full filter set: `published_after` / `published_before` windows, `license`, `category`, `event_type`, `video_caption`, `video_definition`, `video_dimension`, `video_embeddable`, `topic_id`, `location` with `location_radius`, `safe_search`, and more. Reach for it when a plain keyword search is too blunt, and for `search` when you need channels or playlists back too. `search/advanced` is the one metered endpoint here. The base page is 1 credit; **`includeExtras=true` adds a flat 5 credits** to hydrate the page with view, like, and comment counts, because that hydration is a batch call under the hood. The page caps at 50 results upstream, so the increment is a single flat +5, never a multiple — **1cr or 6cr, nothing in between.** Two more search surfaces: `GET /v1/youtube/search/hashtag` (1cr) reads a hashtag feed rather than a keyword query, with `type=shorts` to restrict it, and `GET /v1/youtube/search/suggestions` (1cr) returns YouTube's own autocomplete list for a partial query — the cheapest keyword-research call on the platform. **`search`'s `sortBy=popular` is not a view-count sort.** It is a popularity-weighted ranking, and results do not come back in descending view order — a `new york` search returned 9.5M, then 502.9M, then 66K views in that order, on 4 of 4 test keywords. It also caps the page at roughly 20 results and cannot be combined with `type=shorts` (that pair is rejected with a `400` ). For a view-faithful ordering use `search/advanced` with `order=viewCount&includeExtras=true` , then sort the page yourself on the returned `engagement.views` . Separately, `search` 's `duration` filter is currently not applied by the provider and can degrade the `type` filter — filter client-side on `post.content.duration_seconds` instead. ```bash # Videos from a date window, with counts hydrated curl "https://www.socialcrawl.dev/v1/youtube/search/advanced?query=ai%20agents&published_after=2026-06-01&order=viewCount&includeExtras=true" \ -H "x-api-key: sc_your_api_key_here" # What does YouTube think people are searching for? curl "https://www.socialcrawl.dev/v1/youtube/search/suggestions?query=how%20to%20train" \ -H "x-api-key: sc_your_api_key_here" ``` One video, and everything hanging off it [#one-video-and-everything-hanging-off-it] * **`GET /v1/youtube/video`** (1cr) — the complete record from a video `url`: title, view/like/comment counts, description, tags, duration, channel, publish date. List endpoints return lighter rows without the description or tags, so this is the call when those matter. * **`GET /v1/youtube/video/comments`** (1cr) — top-level comments with author, text, like count, reply count, and timestamp. `order`, `searchTerm`, and `max_results` narrow it. * **`GET /v1/youtube/video/comment/replies`** (1cr) — one thread opened up. It takes the **`continuationToken` that `video/comments` returned for that comment**, not a comment id, and you keep paging until no replies remain. * **`GET /v1/youtube/video/transcript`** (3cr) — the spoken words as timestamped segments with language, word count, and speech rate. A video without captions returns a `404` naming the reason; that is the expected result, not a failure. * **`GET /v1/youtube/video/subtitles`** (1cr) — the caption *track files* instead: language code and name, format, and a direct download URL, auto-generated tracks included. Cheaper than `transcript` when you want files rather than text. * **`GET /v1/youtube/video/sponsors`** (10cr) — whether a video carries a paid-promotion disclosure, plus the brands likely sponsoring it, each with supporting evidence and a confidence score. Sponsors are **inferred** from the description, links, promo codes, and transcript, not stated by YouTube, which is why every row carries confidence. * **`GET /v1/youtube/video/audio`** (5cr), **`GET /v1/youtube/video/files`** (5cr), **`GET /v1/youtube/video/thumbnails`** (1cr) — the downloadable streams. Audio gives mime type, bitrate, quality, sample rate and channels; files gives resolution, quality label, frame rate and bitrate; thumbnails gives every available size with width, height, and format. The media URLs from `video/audio` and `video/files` are **time-limited** . Fetch them immediately; do not store them in a database and expect them to resolve later. Store the video id and re-request when you need the bytes. ```bash # The whole comment tree on one video curl "https://www.socialcrawl.dev/v1/youtube/video/comments?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ&order=top" \ -H "x-api-key: sc_your_api_key_here" # Then open one thread with the token that came back curl "https://www.socialcrawl.dev/v1/youtube/video/comment/replies?continuationToken=TOKEN_FROM_PREVIOUS_RESPONSE" \ -H "x-api-key: sc_your_api_key_here" ``` Comments from a date window [#comments-from-a-date-window] There is no date parameter on `video/comments`, because YouTube exposes none for comments — a server-side filter would read the same pages at the same cost. What makes a window pull work instead is that `order=newest` is an exact sort: rows arrive newest-first on `comment.published_at` with per-second timestamps, and each next page continues strictly older with no overlap. So to pull, say, the last two years of comments on a video: 1. Request `order=newest` — up to 100 comments per credit. 2. Keep rows where `comment.published_at` is on or after your cutoff. 3. Stop paging when the **last** row of a page is older than the cutoff. The one row that can defy the sort is a pinned comment, which YouTube serves first regardless of `order` — so filter rows by timestamp (step 2) rather than stopping at the first old row you see, and step 3 stays correct. ```bash curl "https://www.socialcrawl.dev/v1/youtube/video/comments?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ&order=newest" \ -H "x-api-key: sc_your_api_key_here" ``` The same shape works on the replies side: `video/comment/replies` rows carry the same `comment.published_at`, so apply the cutoff to the rows as you drain each thread. Playlists and community posts [#playlists-and-community-posts] `GET /v1/youtube/playlist` and `GET /v1/youtube/playlist/items` (1cr each) both take a `playlist_id` and both return the same videos. The difference is what comes with them: `playlist` attaches view counts and durations, `playlist/items` preserves **playlist order** and fills in when each video was added. Pick by whether you care about the videos or the sequence. `GET /v1/youtube/community-post` (1cr) takes a single post `url`; `GET /v1/youtube/channel/community-posts` lists them for a whole channel. Trending [#trending] `GET /v1/youtube/videos/trending` (1cr) takes `region`, `category`, `language`, and `max_results` — this is the filterable one. `GET /v1/youtube/shorts/trending` (5cr) is a Shorts snapshot and takes **no parameters at all**, so it is one global list rather than a per-country view. Batch: one call, up to a thousand ids [#batch-one-call-up-to-a-thousand-ids] Three `POST` endpoints take a JSON body when you already hold ids and want to stop calling the singular endpoint in a loop: * **`POST /v1/youtube/videos`** — up to 1000 video ids, each row the same object `/v1/youtube/video` returns. Ids that do not resolve are simply absent from the list. * **`POST /v1/youtube/channels`** — up to 1000 channel ids, each row the same channel object `/v1/youtube/channel` returns, **subscriber count included** (`author.followers`). Ids that do not resolve are absent from the list rather than returned empty, so **join on `author.id`, never on array position** — the response can be shorter than the request. Note that only the singular `channel` endpoint accepts a handle or URL; the batch takes bare `UC…` ids and rejects anything else with a `400` before billing. * **`POST /v1/youtube/transcripts`** — up to 100 video ids, one transcript row each with the caption language it resolved to and an `ok` or not-found status. A video without captions comes back not-found rather than failing the request, and **failed ids are refunded**. Flat 3cr. `videos` and `channels` are metered **per chunk of 50 ids at 5 credits a chunk**: 50 ids is 5cr, 200 ids is 20cr, the full 1000 is 100cr. The charge is per chunk regardless of how many ids in it resolve, so there is no per-id refund on these two. ```bash curl -X POST "https://www.socialcrawl.dev/v1/youtube/transcripts" \ -H "x-api-key: sc_your_api_key_here" \ -H "content-type: application/json" \ -d '{"ids":["dQw4w9WgXcQ","9bZkp7q19f0"],"language":"en"}' ``` Subscriber counts next to search results [#subscriber-counts-next-to-search-results] Search returns the video, not the creator behind it — YouTube keeps subscriber counts out of its search index, so no search surface on this API carries one and no parameter can add it. Attaching them is a join, and the key is `post.ext.channel_id`. The subscriber count itself lives on `author.followers`, on both `GET /v1/youtube/channel` (1cr, and cached for 15 minutes, so a repeat lookup of the same channel inside that window is free) and `POST /v1/youtube/channels` (5cr per 50 ids). For a crawl, collect channel ids across the **whole run**, deduplicate, then batch: 23 unique channels resolve in one 5-credit call rather than 23 separate ones. Which surface hands you the join key matters: | Search call | Carries `post.ext.channel_id`? | | -------------------- | ----------------------------------------------- | | `search?type=videos` | Yes, on every result | | `search/advanced` | Yes, on every result, no `includeExtras` needed | | `search?type=shorts` | **No — never.** Nor `post.author.*` | **Shorts results carry no creator at all.** YouTube's Shorts shelf ships the video without a channel object, so `post.author.username` , `post.author.display_name` , `post.author.avatar_url` , and `post.ext.channel_id` are all null on every `type=shorts` result — measured 0 of 98 items across three keywords, and `includeExtras=true` does not change it. Recover the creator by feeding the `post.id` values to `POST /v1/youtube/videos` , which returns `post.ext.channel_id` for all of them at 5 credits per 50. So a page of 25 Shorts costs 1cr to search, 5cr to resolve the 25 videos to 23 unique channels, and 5cr to resolve those channels to subscriber counts — **11 credits for a fully attributed page**. The same page of regular videos skips the middle step: **6 credits.** ```bash # 1. Regular videos already carry the channel id curl "https://www.socialcrawl.dev/v1/youtube/search?query=iphone17&type=videos" \ -H "x-api-key: sc_your_api_key_here" # 2. Shorts don't — resolve post.id → channel id first curl -X POST "https://www.socialcrawl.dev/v1/youtube/videos" \ -H "x-api-key: sc_your_api_key_here" \ -H "content-type: application/json" \ -d '{"ids":["AeBZjMXIoa4","IbDFupquJf4"]}' # 3. Either way, deduplicate the channel ids and resolve them in one call curl -X POST "https://www.socialcrawl.dev/v1/youtube/channels" \ -H "x-api-key: sc_your_api_key_here" \ -H "content-type: application/json" \ -d '{"ids":["UCn9l4gU5mkmmlC2eiVu0LHw","UC_x5XG1OV2P6uZZ5FSM9Ttw"]}' ``` `author.followers` is YouTube's own published number, and **YouTube rounds it to three significant figures** above 1,000 subscribers — you will see 12,800,000 and 85,700 and 1,240, never the digit-exact figure behind them. That rounding is YouTube's, applied to everyone, so there is no exact count being withheld from you; below 1,000 subscribers it is exact. Channels at or above 1,000 subscribers also set `author.ext.followers_approximate` to `true` . A channel that hides its count entirely sets `author.ext.hiddenSubscriberCount` to `true` , which is the signal to drop it from a ratio rather than treat a zero as real. What YouTube data can I access? [#what-youtube-data-can-i-access] What should I know about YouTube data? [#what-should-i-know-about-youtube-data] * Everything is `GET` with query parameters except the three batch endpoints (`videos`, `channels`, `transcripts`), which take a `POST` JSON body * Authentication via `x-api-key` header * Responses follow the unified SocialCrawl schema * Channel-scoped endpoints take `channelId` **or** `handle`; video-scoped endpoints take the video `url`; playlist endpoints take `playlist_id` * Pagination is `continuationToken` on the channel, search, comments, and hashtag endpoints, and `cursor` on `playlist`, `playlist/items`, `videos/trending`, and `search/advanced`. Pass back whatever the previous response returned rather than assuming one name * `hl` on `channel`, `video`, and the two id-batch endpoints sets the localisation language of the returned metadata Official YouTube Resources [#official-youtube-resources] * [YouTube Data API v3](https://developers.google.com/youtube/v3) — Official Google developer documentation * [YouTube Analytics API](https://developers.google.com/youtube/analytics) — Channel and video analytics