X

get-profile

x.com
InstallationInstalls just this skill for your agent
Side effectread
Authsession
Needsnavigate · evaluate
Summary

Fetch an X/Twitter user's profile by their handle, VIA API (fast, no render).

Params
{"handle":"string (the @ without the at-sign)"}
Returns
{"bio":"string","name":"string","handle":"string","location":"string","verified":"bool","followers":"int","following":"int","profileUrl":"url","recentTweets":"array of { text, url, time }"}
SKILL.md45 lines

Intent

Fetch an X/Twitter user's profile by their handle, VIA API (fast, no render). Unlike X's search, the profile endpoints do NOT require the anti-bot signature (x-client-transaction-id) -> they're called directly with the public bearer + ct0 cookie. Makes 2 in-page fetches: (1) UserByScreenName(screen_name) -> name, bio, followers, following, location, verified, rest_id; (2) UserTweets(rest_id) -> recent tweets. PARAM: handle (the @ without the at-sign, e.g. 'naval'; comes from x-search-tweets). Returns { handle, profileUrl, name, bio, followers, following, location, verified, recentTweets:[{text,url,time}] }. recentTweets (~20, includes RTs) = the richest signal to personalize. NOTE: depends on the queryId hashes (UserByScreenName, UserTweets) and the public bearer, which rotate per X deploy; if it breaks (errors/empty), recapture from a real profile visit. Requires an X session.

Preconditions

  • Logged-in session in the browser you drive (X/Twitter session started (x.com cookies, incl. ct0)).
  • If not logged in: navigate to https://x.com and let the user log in, then retry.

Execute

  1. navigate → https://x.com/home
  2. evaluate the Extractor below with params. It does the in-page work and returns the JSON in returns.

Extractor

( async (root, params) => { const handle = String((params&&params.handle)||'').replace(/^@/,'').trim(); if(!handle) return { error:'missing handle' }; const ct0=(document.cookie.match(/ct0=([^;]+)/)||[])[1]||''; const BEARER='Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA'; const H={authorization:BEARER,'x-csrf-token':ct0,'x-twitter-active-user':'yes','x-twitter-auth-type':'OAuth2Session','x-twitter-client-language':'en','content-type':'application/json'}; const enc=encodeURIComponent; const ubsnFeat={hidden_profile_subscriptions_enabled:true,profile_label_improvements_pcf_label_in_post_enabled:true,responsive_web_profile_redirect_enabled:false,rweb_tipjar_consumption_enabled:false,verified_phone_label_enabled:false,subscriptions_verification_info_is_identity_verified_enabled:true,subscriptions_verification_info_verified_since_enabled:true,highlights_tweets_tab_ui_enabled:true,responsive_web_twitter_article_notes_tab_enabled:true,subscriptions_feature_can_gift_premium:true,creator_subscriptions_tweet_preview_api_enabled:true,responsive_web_graphql_skip_user_profile_image_extensions_enabled:false,responsive_web_graphql_timeline_navigation_enabled:true}; let u, lg, restId; try { const r=await fetch('/i/api/graphql/681MIj51w00Aj6dY0GXnHw/UserByScreenName?variables='+enc(JSON.stringify({screen_name:handle,withGrokTranslatedBio:false}))+'&features='+enc(JSON.stringify(ubsnFeat)),{headers:H,credentials:'include'}); const d=await r.json(); u=d.data.user.result; lg=u.legacy||{}; restId=u.rest_id; } catch(e){ return { handle, error:'profile fail: '+String(e) }; } if(!restId) return { handle, error:'user not found' }; const prof={ handle, profileUrl:'https://x.com/'+handle, name:(u.core&&u.core.name)||lg.name||null, bio:(lg.description!=null?lg.description:((u.profile_bio&&u.profile_bio.description)||null)), followers:(lg.followers_count!=null?lg.followers_count:null), following:(lg.friends_count!=null?lg.friends_count:null), location:(u.location&&u.location.location)||lg.location||null, verified:!!(u.is_blue_verified||lg.verified), recentTweets:[] }; const twFeat={rweb_video_screen_enabled:false,rweb_cashtags_enabled:true,profile_label_improvements_pcf_label_in_post_enabled:true,responsive_web_profile_redirect_enabled:false,rweb_tipjar_consumption_enabled:false,verified_phone_label_enabled:false,creator_subscriptions_tweet_preview_api_enabled:true,responsive_web_graphql_timeline_navigation_enabled:true,responsive_web_graphql_skip_user_profile_image_extensions_enabled:false,premium_content_api_read_enabled:false,communities_web_enable_tweet_community_results_fetch:true,c9s_tweet_anatomy_moderator_badge_enabled:true,responsive_web_grok_analyze_button_fetch_trends_enabled:false,responsive_web_grok_analyze_post_followups_enabled:true,rweb_cashtags_composer_attachment_enabled:true,responsive_web_jetfuel_frame:true,responsive_web_grok_share_attachment_enabled:true,responsive_web_grok_annotations_enabled:true,articles_preview_enabled:true,responsive_web_edit_tweet_api_enabled:true,rweb_conversational_replies_downvote_enabled:false,graphql_is_translatable_rweb_tweet_is_translatable_enabled:true,view_counts_everywhere_api_enabled:true,longform_notetweets_consumption_enabled:true,responsive_web_twitter_article_tweet_consumption_enabled:true,content_disclosure_indicator_enabled:true,content_disclosure_ai_generated_indicator_enabled:true,responsive_web_grok_show_grok_translated_post:true,responsive_web_grok_analysis_button_from_backend:true,post_ctas_fetch_enabled:false,freedom_of_speech_not_reach_fetch_enabled:true,standardized_nudges_misinfo:true,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:true,longform_notetweets_rich_text_read_enabled:true,longform_notetweets_inline_media_enabled:false,responsive_web_grok_image_annotation_enabled:true,responsive_web_grok_imagine_annotation_enabled:true,responsive_web_grok_community_note_auto_translation_is_enabled:true,responsive_web_enhance_cards_enabled:false}; try { const r2=await fetch('/i/api/graphql/RyDU3I9VJtPF-Pnl6vrRlw/UserTweets?variables='+enc(JSON.stringify({userId:String(restId),count:20,includePromotedContent:false,withQuickPromoteEligibilityTweetFields:false,withVoice:true}))+'&features='+enc(JSON.stringify(twFeat))+'&fieldToggles='+enc(JSON.stringify({withArticlePlainText:false})),{headers:H,credentials:'include'}); const d2=await r2.json(); const seen=new Set(); const walk=o=>{ if(!o||typeof o!=='object')return; if(o.__typename==='Tweet'&&o.legacy){ const lg2=o.legacy; const id=lg2.id_str||o.rest_id; if(id && !seen.has(id) && prof.recentTweets.length<20){ seen.add(id); prof.recentTweets.push({ text:(lg2.full_text||'').slice(0,280), url:'https://x.com/'+handle+'/status/'+id, time:lg2.created_at||null }); } } for(const k in o) walk(o[k]); }; walk(d2); } catch(e){} return prof; } )

Success assertion

{
  "expr": "#react-root",
  "type": "dom"
}
get-profile: a X skill for AI browsing agents · browser-memory