Drive the user's authenticated YouTube via the browser skill: channels, videos, comments + commenters, search via the InnerTube API; gated like/comment/subscribe. Use for YouTube research/automation as the user.
Install
npx skillscat add agentproto/ts/youtube Install via the SkillsCat registry.
YouTube — recon plan (InnerTube API)
Internal API: InnerTube —POST https://www.youtube.com/youtubei/v1/<endpoint>?key=<INNERTUBE_API_KEY>&prettyPrint=false,
body { context: <INNERTUBE_CONTEXT>, ...params }. The web app uses this for
everything.
Config + auth (from the page)
INNERTUBE_API_KEY+INNERTUBE_CONTEXTlive inytcfg. Read in-page:;() => ({ key: ytcfg.get("INNERTUBE_API_KEY"), ctx: ytcfg.get("INNERTUBE_CONTEXT"), })- Public reads: often work with just key + context (no auth).
- Personalized / write (subscriptions, comments, like): need SAPISIDHASH
auth:Authorization: SAPISIDHASH <ts>_<SHA1(ts + " " + SAPISID + " " + origin)>
where SAPISID = theSAPISID(or__Secure-3PAPISID) cookie, origin =https://www.youtube.com, ts = unix seconds. Compute SHA-1 in-page
(SubtleCrypto). Also sendX-Goog-AuthUser: 0,Origin, cookies (auto).
In-page fetch:
;async (endpoint, body) => {
const key = ytcfg.get("INNERTUBE_API_KEY")
const r = await fetch(
`/youtubei/v1/${endpoint}?key=${key}&prettyPrint=false`,
{
method: "POST",
headers: {
"content-type": "application/json" /* + SAPISIDHASH for writes */,
},
body: JSON.stringify({
context: ytcfg.get("INNERTUBE_CONTEXT"),
...body,
}),
}
)
return await r.json()
}Endpoints to test
| Purpose | endpoint | params |
|---|---|---|
| Channel / home / playlist | browse |
browseId (channel UC…), params |
| Watch page → video + comments token | next |
videoId |
| Comments (+ who/likes) | next (continuation token from the watch next) |
continuation |
| Search | search |
query, params |
| Video metadata | player |
videoId |
| WRITE: comment | comment/create_comment / create_comment_reply |
createCommentParams |
| WRITE: like/dislike | like/like / like/dislike / like/removelike |
target.videoId |
| WRITE: subscribe | subscription/subscribe / unsubscribe |
channelIds |
Entity model
Channel{browseId, title, subscriberCount} ·Video{videoId, title, author, viewCount, likeCount} ·Comment{author, text, likeCount, replyCount}. Responses are deeply nested
renderer trees (*Renderer) — walk to the renderer that holds the field
(e.g. commentRenderer, videoRenderer).
Capture method
Open youtube.com → read ytcfg (above) → list_network_requests for/youtubei/v1/ → see the endpoint + the body params + continuation tokens.
Actions to test (read-first; writes gated)
- read ytcfg key+context.
searcha topic → videos.next(videoId)→ video + first comments → who commented + likes (→
graph).browse(channelId)→ a creator's videos/about.- (gated, needs SAPISIDHASH) comment / like / subscribe — explicit confirm.
Gotchas
- SAPISIDHASH required for any authenticated/write call — compute in-page from
the SAPISID cookie (SubtleCrypto SHA-1). Read-only public data may skip it. - Renderer trees are verbose; extract by walking for the specific
*Renderer. - Comments come via a continuation token from the
nextresponse, not a flat
list.