Twitter / X have user intent URLs so that you can auto-resolve from a user id to a profile url:
These work if you are logged in to twitter - but if you're not logged in they go to an error page.
If someone can help me find a way around this I'll pay the bounty.
Thanks! :)
25,000 sats bounty
Use this https://tweeterid.com/
Or use twitter api if you willing to intergrate this in your app
I think this will work
GET https://api.twitter.com/1.1/users/show.json?user_id=IDReplace ID with the user id
Isn't it possible to just request get the website and parse the html statically?
It seems like Twitter user intent URLs are designed to work when you're logged in, providing seamless redirection from user IDs to profile URLs. If you're not logged in, it may result in an error page possibly due to privacy or access restrictions
Solved, with a working logged-out resolver. Your example, just now, from a terminal with no session:
$ node resolve_userid.mjs 2369450425 2369450425 -> https://x.com/MerryOscar (Oscar Merry)Full source (~90 lines): https://paste.rs/imXDa (mirror: https://termbin.com/qxxd)
Why
/i/user/<id>fails logged out: that route is a frontend redirect that only resolves inside an authenticated session — logged out, the SPA has no identity context and dumps you on the error/login page. The trick is to skip the frontend route and ask X's own GraphQL API directly, the same way the web app does. An anonymous visitor can obtain everything it needs:POST https://api.twitter.com/1.1/guest/activate.jsonwith that bearer returns aguest_token, no login, no cookies.x-client-transaction-idheader — since 2024 X requires this on GraphQL calls; it's derived from an animation-key embedded in the x.com home document (the ondemand.s obfuscation). I use the MIT-licensedx-client-transaction-idnpm package which re-implements the web app's derivation — no browser needed.UserByRestIdqueryId — it rotates with every deploy, so the script scrapes it fresh from theclient-web/main.*.jsbundle each run (that's why hard-coded queryIds you find in old gists all 404 today).Then one call:
GET https://api.twitter.com/graphql/<queryId>/UserByRestId ?variables={"userId":"2369450425","withSafetyModeUserFields":true}&features={...}&fieldToggles={...} Authorization: Bearer <public web bearer> x-guest-token: <from step 2> x-client-transaction-id: <from step 3>returns the user object;
screen_namenow lives underresult.core.screen_name(it used to belegacy.screen_name— if you getundefinedthere, that's why).https://x.com/<screen_name>is your resolved profile URL.Notes: this is rate-limited per guest token like normal web traffic (fine for occasional resolution, not bulk scraping); if a call starts 404ing, the queryId rotated — the script already handles that by re-scraping. No account, no API key, no login wall.