Drive the user's authenticated Instagram via the browser skill: profiles, media likers/comments, followers/following, search via the web private API; gated like/comment/follow/DM. Use for Instagram research/outreach as the user.
Install
npx skillscat add agentproto/ts/instagram Install via the SkillsCat registry.
Instagram — recon plan (web private API)
✅ Instagram adapter —
SOCIAL_PLATFORMS.instagramin@agstudio/browser-social(sites/instagram/adapter.ts): profile · authored ·
connection (followers/following) · engagement-received (commenters — the only
IG edge; likers 403), as aSocialSourcePort. In-pageigFetch
(x-ig-app-id + x-csrftoken) + re-focus drift defense. Graph ingest:scripts/profile.ts instagram <username> --graph-only→ registry capture →landFootprintgraph sink (platform"instagram"): profile→SocialPerson,
posts→AUTHORED, commenters→COMMENTED, followers/following→FOLLOWS. Same domain
as LinkedIn → cross-platform queries just work. Likers stay 403.
✅ Validated live (logged in, read-only): headers
x-ig-app-id:936619743392459
x-csrftoken(csrftoken) +x-requested-with:XMLHttpRequest.web_profile_info?username=→200 (pk, full_name, follower/post counts).feed/user/<pk>/?count=N→200 (media: code, pk, like_count, comment_count,
caption).media/<pk>/comments/→200 (commenters: @username + user pk + text
= engagement edge). ⚠️media/<pk>/likers/→403 (IG hides likers). IG
graph = followers/following + commenters, not likers. web_profile_info
media edges are empty → usefeed/user/<pk>/.
Internal API: web private REST https://www.instagram.com/api/v1/... +
GraphQL https://www.instagram.com/graphql/query (and /api/graphql) usingdoc_id. The web UI calls these directly.
Auth (from the page)
- cookies (
sessionid,csrftoken,ds_user_id) ride same-origin. - headers required:
x-ig-app-id: 936619743392459(the web app id — stable; also in the page's
shared-data / a request header, capture to confirm),x-csrftoken: <csrftoken cookie>,x-requested-with: XMLHttpRequest,x-asbd-id(a small constant in requests — capture),x-ig-www-claim(echoed from a prior response headerx-ig-set-www-claim).
In-page fetch:
async path => {
const csrf = (document.cookie.match(/csrftoken=([^;]+)/) || [])[1]
const r = await fetch(path, {
headers: {
"x-ig-app-id": "936619743392459",
"x-csrftoken": csrf,
"x-requested-with": "XMLHttpRequest",
},
})
return await r.json()
}Endpoints to test
| Purpose | endpoint |
|---|---|
| Feed / timeline | POST /api/v1/feed/timeline/ |
| Profile by username | GET /api/v1/users/web_profile_info/?username=<u> → user pk + recent media |
| User media | GET /api/v1/feed/user/<pk>/ |
| Followers / following | GET /api/v1/friendships/<pk>/followers/ · /following/ (max_id paginate) |
| Who liked a post | GET /api/v1/media/<mediaId>/likers/ |
| Comments | GET /api/v1/media/<mediaId>/comments/ |
| Search users | GET /api/v1/web/search/topsearch/?query=<q> (or GraphQL) |
| WRITE: like | POST /api/v1/web/likes/<mediaId>/like/ |
| WRITE: comment | POST /api/v1/web/comments/<mediaId>/add/ |
| WRITE: follow | POST /api/v1/friendships/create/<pk>/ |
| WRITE: DM | POST /api/v1/direct_v2/threads/broadcast/text/ |
Entity model
User{pk, username, full_name, follower_count, is_private} ·Media{id, code(shortcode), caption, like_count, comment_count, owner:User} ·Comment{user, text}.
Capture method
Open instagram.com → list_network_requests for /api/v1/ + /graphql/ → read
endpoint + x-ig-app-id / x-asbd-id / x-ig-www-claim from a request header,
and doc_id for GraphQL ops.
Actions to test (read-first; writes gated)
web_profile_info?username=→ pk + recent media + counts.media/<id>/likers/+/comments/→ who engaged (→ persona/graph).friendships/<pk>/followers/→ network sample.topsearch?query=→ discover users → score → dossier.- (gated) like / comment / follow / DM — explicit confirm, heavily
rate-limited.
Gotchas
doc_id(GraphQL) + endpoints drift; private accounts gate most reads.- IG is aggressive on automation — space requests, low volume, expect
challenges/checkpoints on bursts. Writes especially risky → minimal + gated. - Some POSTs need a
signed_body/ additional params — capture the real
request.