Closed devgaucho closed 1 year ago
It appears that using Togetter, a service that compiles tweets, it can go back to all tweets(probably). https://togetter.com https://togetter.com/create this service is in Japanese, but it seems to be able to trace back tweets from overseas as well.
https://github.com/zedeus/nitter/assets/50985323/bbc85bf2-5461-4c1c-a7e9-3040cd18333e
Login is required but not API restricted, because togetter uses Twitter-API provided by NTT-DATA .(probably) https://nttdata-nazuki.jp/certifiedserviceprogram/index.html
OK so this is a source for the tweets. But you have to deal with that, know how to use it and how to integrate it to Nitter. And if the whole Nitter community use that source, not sure it will be working for a long time... due to a massive traffic increase.
you can add "showReplies" parameter to the syndication endpoint and will show the user tweet replies also https://syndication.twitter.com/srv/timeline-profile/screen-name/elonmusk?showReplies=true
then to get more info about a tweet with just the tweet id you can use https://syndication.twitter.com/tweet-result?id=1675214274627530754
https://cdn.discordapp.com/attachments/874455648883052544/1125145584798548119/image.png
"Parse HTML with regex" I feel attacked
Sometimes that's not even a bad option 😉
Looks like this instance (rss-bridge.org) is modified to be able to parse the syndication (git.twitter-fix-2.be59a1e). I am running my own instance of rss-bridge with latest docker image (git.master.d8bc015). This is not working for syndication (yet).
the twitter issues might resolve itself by its own so im waiting a bit before merging to master.
do you want it merged asap?
Syndication sometimes works, sometimes doesn't. Right now, it is not working.
Looks like this instance (rss-bridge.org) is modified to be able to parse the syndication (git.twitter-fix-2.be59a1e). I am running my own instance of rss-bridge with latest docker image (git.master.d8bc015). This is not working for syndication (yet).
the twitter issues might resolve itself by its own so im waiting a bit before merging to master. do you want it merged asap?
Syndication sometimes works, sometimes doesn't. Right now, it is not working.
I'm getting this same issue. Thought my IP is blocked as I've been checking this link a lot, but VPN didn't help either. They seem to be on guard with this syndication thing.
Looks like this instance (rss-bridge.org) is modified to be able to parse the syndication (git.twitter-fix-2.be59a1e). I am running my own instance of rss-bridge with latest docker image (git.master.d8bc015). This is not working for syndication (yet).
the twitter issues might resolve itself by its own so im waiting a bit before merging to master. do you want it merged asap?
Syndication sometimes works, sometimes doesn't. Right now, it is not working.
I'm getting this same issue. Thought my IP is blocked as I've been checking this link a lot, but VPN didn't help either. They seem to be on guard with this syndication thing.
Twitter employees are reading this thread for sure, this is when openness actually backfires
I guess at this point the only actual solution is to create *a lot of* accounts for readonly bots and deploy a load balancer so you guys wont rail the Twitter's potato servers
https://cdn.discordapp.com/attachments/874455648883052544/1125145584798548119/image.png
Do Nitter instances pay for captcha solvers?
I think the coward privated his account, too. EDIT: Nevermind. The reason I don't see anything is because posts are hidden, but the syndication page itself is not.
I think Nitter can be saved... Look at this browser extension that - while its advertised feature is to bring back the old Twitter UI - due to the "secret APIs" it uses, which seem to be something GraphQL-related similar to Nitter's internal API (I only skimmed trough the source code, haven't really read it), it can bypass rate limits: https://github.com/dimdenGD/OldTwitter.
As said by the developer (on Twitter), it needs an active login session. I tried logging into Twitter.com, installing the extension, and I think I've scrolled far more than the free 1000 posts I would be allowed to, so I'd say this works. If we can look at the extension source, we can maybe see what needs to be fixed in Nitter's source to make it work again (apart from requiring the instance admin to login with their own Twitter account on the Nitter server)
I'm sure they can see where the traffic goes and just block it, like they are doing with syndication now. So any trick or loophole will be closed as soon as too many people start using it.
OldTwitter uses Twitter API 1.1 used by old TweetDeck. It's not secret at all and can be blocked like embed tweet
Has anyone built a tool to get an rss from a syndication link?
@TempUser13 dvikan has linked to rss-bridge in this issue 🙈 [#919 (comment)](https://github.com/zedeus/nitter/issues/919#issuecomment-1615253896
doesn't seem to work but thanks for the reply, ill try to selfhost with the specific commit, nvm syndication is broken i didn't see
Their crappy official API changes ruined my small project that checked 1 persons timeline every hour. Now I can't even check it at all.
The official API is completely useless at the free tier. Wanted to use Nitter as a backup option, but looks like Twitter screwed that up too. Hopefully they fallback on their changes.
@devgaucho can that be used to bypass login walls as well as archiving?
good question, besides GoogleBot the twitter robots.txt still allows access from Slurp, Yandex, msnbot and bingbot
is it possible that it doesn't do ip verification on all these bots
but robots.txt seems to be outdated since it is still allowing bots access to /search?q=%23
Does anyone know how bird.makeup still manage to work correctly ? According to it's author it has 40% failure rate but that's still more than enough. https://sr.ht/~cloutier/bird.makeup/
His Twoot : https://social.librem.one/@vincent/110645408061048325
Does anyone know how bird.makeup still manage to work correctly ? According to it's author it has 40% failure rate but that's still more than enough. https://sr.ht/~cloutier/bird.makeup/
His Twoot : https://social.librem.one/@vincent/110645408061048325
probably he uses twitter's api and makes a local cache to avoid repeating requests
even so he seems to be suffering with the platform's instability
Does anyone know how bird.makeup still manage to work correctly ? According to it's author it has 40% failure rate but that's still more than enough. https://sr.ht/~cloutier/bird.makeup/ His Twoot : https://social.librem.one/@vincent/110645408061048325
probably he uses twitter's paid_ api _and makes a local cache to avoid repeating requests
even so he seems to be suffering with the platform's instability
confirmated.
Does anyone know how bird.makeup still manage to work correctly ? According to it's author it has 40% failure rate but that's still more than enough. https://sr.ht/~cloutier/bird.makeup/ His Twoot : https://social.librem.one/@vincent/110645408061048325
probably he uses twitter's paid_ api _and makes a local cache to avoid repeating requests even so he seems to be suffering with the platform's instability
confirmated.
But that doesn't make any sense, you can selfhost it, there's no way he just share publicly his paid apikey .. ?
In his documentation you don't have to fill any apikey https://git.sr.ht/~cloutier/bird.makeup/tree/master/item/VARIABLES.md
He seems to use some sort of API 1.1 and / GraphQL combo : https://git.sr.ht/~cloutier/bird.makeup/commit/612637fdc7f667d2e491117ea84cd2e60b5c85e6
EDIT : Nevermind he seems to use some sort of API Key indeed, as you can see in the function private async Task<string> GenerateBearerToken()
in src/BirdsiteLive.Twitter/Tools/TwitterAuthenticationInitializer.cs
Ok this seems interesting, he seems he is able to get a Bearer tokens using built-in tokens from APPS as seens here
He then picks one randomly and send a post request in GenerateBearerToken() which correctly returns a Bearer Tokens
You can try yourself with curl : curl -X POST "https://api.twitter.com/oauth2/token?grant_type=client_credentials" -u "CjulERsDeqhhjSme66ECg:IQWdVyqFxghAtURHGeGiWAsmCAGmdW3WmbEx6Hck"
You can then activate your fresh access_token :
curl -X POST -H "Authorization: Bearer your_access_token" "https://api.twitter.com/1.1/guest/activate.json"
This will return you a guest_token.
You can then access graphql endpoints :
curl -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \
-H "x-guest-token: 1675958319943393280" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com/" \
"https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName?variables=%7B%22screen_name%22%3A%22elonmusk%22%2C%22withSafetyModeUserFields%22%3Atrue%7D&features=%7B%22hidden_profile_likes_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D"
where x-guest-token is the token returned when you activated your Bearer.
You can get the user numeric ID from previous request, from twitter name : for elonmusk :
curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \
-H "x-guest-token: 1675958319943393280" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com/" \
"https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName?variables=%7B%22screen_name%22%3A%22elonmusk%22%2C%22withSafetyModeUserFields%22%3Atrue%7D&features=%7B%22hidden_profile_likes_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D" \
| jq -r '.data.user.result.rest_id'
Then get all his timeline :
curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \
-H "x-guest-token: 1675958319943393280" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com" "https://api.twitter.com/graphql/pNl8WjKAvaegIoVH--FuoQ/UserTweetsAndReplies?variables=%7B%22userId%22%3A%2244196397%22,%22count%22%3A40,%22includePromotedContent%22%3Atrue,%22withCommunity%22%3Atrue,%22withSuperFollowsUserFields%22%3Atrue,%22withDownvotePerspective%22%3Afalse,%22withReactionsMetadata%22%3Afalse,%22withReactionsPerspective%22%3Afalse,%22withSuperFollowsTweetFields%22%3Atrue,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22vibe_api_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse,%22interactive_text_enabled%22%3Atrue,%22responsive_web_text_conversations_enabled%22%3Afalse,%22longform_notetweets_richtext_consumption_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Afalse%7D" \
| jq
You have to replace the userID between the two "%22" tags. Here the user id (of elonmusk) is : 44196397
Which returns All his Tweets and Replies (too long):
https://bin.socialspill.com/ixomusor.json
To access detail of a specific tweet the url is :
https://api.twitter.com/graphql/XjlydVWHFIDaAUny86oh2g/TweetDetail?variables=%7B%22focalTweetId%22%3A% + statusId + %22,%22with_rux_injections%22%3Atrue,%22includePromotedContent%22%3Afalse,%22withCommunity%22%3Afalse,%22withQuickPromoteEligibilityTweetFields%22%3Afalse,%22withBirdwatchNotes%22%3Afalse,%22withSuperFollowsUserFields%22%3Afalse,%22withDownvotePerspective%22%3Afalse,%22withReactionsMetadata%22%3Afalse,%22withReactionsPerspective%22%3Afalse,%22withSuperFollowsTweetFields%22%3Afalse,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22vibe_api_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Afalse,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse,%22interactive_text_enabled%22%3Atrue,%22responsive_web_text_conversations_enabled%22%3Afalse,%22longform_notetweets_richtext_consumption_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Atrue%7D
Wow, this is a great find! So, can we just implement a Twitter API client and re-implement Nitter's fetching layer?
Updated my post, :) (btw, get rekt elon)
I guess the next step would be finding all the endpoints we can access to. I know the folks at Libreddit were able to do something similar by rummaging through a decompiled version of the Reddit Android client.
@Write https://github.com/zedeus/nitter/issues/919#issuecomment-1619067142 Is this not limited?
@Write #919 (comment) Is this not limited?
Well guessing that there is, hence why birds makeup has 40% failure rate and rotate between keys. Gonna let you try :)
@Write #919 (comment) Is this not limited?
It seems the Bearer Token is the same for all, so the first step can be skipped. But the guest_token
is different for all.
So probably only this one can get limited. Maybe it is simple to just recreate a new one each few calls.
One limitation, according to tests with curl, is that each second or third curl answer is slow (~ 2 to 3 seconds).
@Write #919 (comment) Is this not limited?
It seems the Bearer Token is the same for all, so the first step can be skipped. But the
guest_token
is different for all. So probably only this one can get limited. Maybe it is simple to just recreate a new one each few calls. One limitation, according to tests with curl, is that each 2 or 3 curl answers are slow (~ 2 to 3 seconds).
You gonna have to make your own tests. I just showed an example as how bird.makeup works. I don't know anything more, sorry. For me the Bearer returned is definitely different based on which credential I use.
@Write #919 (comment) Is this not limited?
It seems the Bearer Token is the same for all, so the first step can be skipped. But the
guest_token
is different for all. So probably only this one can get limited. Maybe it is simple to just recreate a new one each few calls. One limitation, according to tests with curl, is that each 2 or 3 curl answers are slow (~ 2 to 3 seconds).You gonna have to make your own tests. I just showed an example as how bird.makeup works. I don't know anything more, sorry. For me the Bearer returned is definitely different based on which credential I use.
Yes, it is based on the credentials. But since they are constant, I got the same Bearer as you have shown above. All others probably, too.
@Write #919 (comment) Is this not limited?
It seems the Bearer Token is the same for all, so the first step can be skipped. But the
guest_token
is different for all. So probably only this one can get limited. Maybe it is simple to just recreate a new one each few calls. One limitation, according to tests with curl, is that each 2 or 3 curl answers are slow (~ 2 to 3 seconds).You gonna have to make your own tests. I just showed an example as how bird.makeup works. I don't know anything more, sorry. For me the Bearer returned is definitely different based on which credential I use.
Yes, it is based on the credentials. But since they are constant, I got the same Bearer as you have shown above. All others probably, too.
There's a possibility they change about some times. I don't know.
I don't know why everyone is so positive about the makeup bird. It has failed for me ever since I tried it first a couple of days ago. No matter which IP or browser (agent) I use, I always get:
Error.
An error occurred while processing your request.
I don't know why everyone is so positive about the makeup bird. It has failed for me ever since I tried it first a couple of days ago. No matter which IP or browser (agent) I use, I always get:
Error. An error occurred while processing your request.
Yeah, this app does not work. But you can try the curl codes above and it works. There is also not much room for twitter to prevent this without blocking their own official apps or force users to install new versions. The problem with the code above is that it is less legal than the previous approach that was easier to get from the browser, I think. But not sure.
My goal to find all possible endpoints is starting to go well; just found my first API endpoint:
curl -s -X GET \
-H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \
-H "x-guest-token: 1675958319943393280" -H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com" \
"https://api.twitter.com/graphql/0c8QtPl8cRsze0fPzS6_zg/TwitterBlueMarketingPage?features=%7B%22subscriptions_annual_subscription_signup_enabled%22%3A%20true%7D"
{"data":{"blue_marketing_page_config":{"card":{"badge":{"text":""},"imageUrl":"https://abs.twimg.com/responsive-web/client-web/verification-card-v2@3x.8ebee01a.png","description":"","title":"Blue subscribers with a verified phone number will get a blue checkmark once approved."},"products":[{"buckets":{"title":"","buckets":[{"imageUrl":"https://abs.twimg.com/responsive-web/client-web/purple-present@3x.5f4d564a.png","description":"Edit Tweet, 1080p video uploads, Reader, custom navigation, Bookmark Folders, Top Articles and more.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"All the existing Blue features","clientEventInfo":{"component":"feature_bucket","element":"existing_blue_features"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/upranked-replies-feature@3x.68f97c89.png","description":"Tweets from Twitter Blue subscribers will be prioritized in replies, mentions, and search — helping to fight scams and spam.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"Prioritized ranking in conversations and search","clientEventInfo":{"component":"feature_bucket","element":"reply_upranking"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/less-ads-feature@3x.98d5a999.png","description":"See approximately twice as many Tweets between ads in your For You and Following timelines.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"See half the ads","clientEventInfo":{"component":"feature_bucket","element":"less_ads"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/longer-video-feature-v3@3x.6c6c531a.png","description":"You’ll finally be able to post longer videos to Twitter.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"Post longer videos","clientEventInfo":{"component":"feature_bucket","element":"longer_video"},"features":[]},{"imageUrl":"https://abs.twimg.com/responsive-web/client-web/early-access-feature@3x.9d1ba0a9.png","description":"Get early access to select new features with Twitter Blue Labs.","learnMoreText":"Learn more","learnMoreTitle":"Get early access","learnMoreDescription":"Twitter Blue subscribers get early access to new features like these through Twitter Blue Labs.","title":"Get early access","clientEventInfo":{"component":"feature_bucket","element":"early_access"},"features":[{"icon":"WriteStroke","description":"Edit a Tweet up to 5 times within 30 minutes.","title":"Edit Tweet"},{"icon":"AccountNft","description":"Show your personal flair and set your profile picture to an NFT you own.","title":"NFT Profile Pictures"},{"icon":"CameraVideoStroke","description":"Share your favorite moments with 1080p (Full HD) video.","title":"1080p video uploads"}]}]},"productCategory":"BlueVerified","title":"Blue"}],"feature_buckets":{"title":"","buckets":[{"imageUrl":"https://abs.twimg.com/responsive-web/client-web/purple-present@3x.5f4d564a.png","description":"Edit Tweet, 1080p video uploads, Reader, custom navigation, Bookmark Folders, Top Articles and more.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"All the existing Blue features","clientEventInfo":{"component":"feature_bucket","element":"existing_blue_features"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/upranked-replies-feature@3x.68f97c89.png","description":"Tweets from Twitter Blue subscribers will be prioritized in replies, mentions, and search — helping to fight scams and spam.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"Prioritized ranking in conversations and search","clientEventInfo":{"component":"feature_bucket","element":"reply_upranking"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/less-ads-feature@3x.98d5a999.png","description":"See approximately twice as many Tweets between ads in your For You and Following timelines.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"See half the ads","clientEventInfo":{"component":"feature_bucket","element":"less_ads"},"features":[]},{"badge":"NEW","imageUrl":"https://abs.twimg.com/responsive-web/client-web/longer-video-feature-v3@3x.6c6c531a.png","description":"You’ll finally be able to post longer videos to Twitter.","learnMoreText":"","learnMoreTitle":"","learnMoreDescription":"","title":"Post longer videos","clientEventInfo":{"component":"feature_bucket","element":"longer_video"},"features":[]},{"imageUrl":"https://abs.twimg.com/responsive-web/client-web/early-access-feature@3x.9d1ba0a9.png","description":"Get early access to select new features with Twitter Blue Labs.","learnMoreText":"Learn more","learnMoreTitle":"Get early access","learnMoreDescription":"Twitter Blue subscribers get early access to new features like these through Twitter Blue Labs.","title":"Get early access","clientEventInfo":{"component":"feature_bucket","element":"early_access"},"features":[{"icon":"WriteStroke","description":"Edit a Tweet up to 5 times within 30 minutes.","title":"Edit Tweet"},{"icon":"AccountNft","description":"Show your personal flair and set your profile picture to an NFT you own.","title":"NFT Profile Pictures"},{"icon":"CameraVideoStroke","description":"Share your favorite moments with 1080p (Full HD) video.","title":"1080p video uploads"}]}]},"button":{"detailText":"Limited time offer: {{PRICE}}/{{PERIOD}}","disclaimerText":"By clicking Subscribe, you agree to our @#!. Subscriptions auto-renew until canceled, as described in the Terms. Cancel anytime. A verified phone number is required to subscribe. If you've subscribed on another platform, manage your subscription through that platform.","disclaimerUrl":"https://legal.twitter.com/purchaser-terms","disclaimerUrlText":"Purchaser Terms of Service"}}}}
Here's all the operations I was able to extract from a decomp (using JADX) of the Twitter APK (version 9.95.0-release.0
): operations.txt
It would seem that the Android and web clients use different queries, as I was unable to find any @Write mentioned.
It looks like Twitter's Android app makes use of Apollo, so there's a chance we may be able to fully extract all info about the schema from it; it will just require some reverse engineering.
Does anyone know how bird.makeup still manage to work correctly ? According to it's author it has 40% failure rate but that's still more than enough. https://sr.ht/~cloutier/bird.makeup/ His Twoot : https://social.librem.one/@vincent/110645408061048325
probably he uses twitter's paid api and makes a local cache to avoid repeating requests even so he seems to be suffering with the platform's instability
confirmated.
But that doesn't make any sense, you can selfhost it, there's no way he just share publicly his paid apikey .. ?
In his documentation you don't have to fill any apikey https://git.sr.ht/~cloutier/bird.makeup/tree/master/item/VARIABLES.md
He seems to use some sort of_ API 1.1 and / GraphQL _combo : https://git.sr.ht/~cloutier/bird.makeup/commit/612637fdc7f667d2e491117ea84cd2e60b5c85e6
EDIT : Nevermind he seems to use some sort of_ API Key indeed, as you can see in the function `private async _Task<_string_> GenerateBearerToken()
in
src/BirdsiteLive.Twitter/Tools/TwitterAuthenticationInitializer.cs`
this is a hard coded persistent graphql query:
Here's how you can get Elon Musk's profile info using the Android API:
$ curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" -H "x-guest-token: 1675958319943393280" -H "x-twitter-active-user: yes" -H "Referer: https://twitter.com" "https://api.twitter.com/graphql/oPppcargziU1uDQHAUmH-A/UserResultByIdQuery?features=%7B%22blue_business_profile_image_shape_enabled%22%3A%20false%2C%20%22verified_phone_label_enabled%22%3A%20false%2C%20%22super_follow_user_api_enabled%22%3A%20false%2C%20%22super_follow_badge_privacy_enabled%22%3A%20false%2C%20%22subscriptions_verification_info_enabled%22%3A%20false%2C%20%22creator_subscriptions_subscription_count_enabled%22%3A%20false%2C%20%22super_follow_exclusive_tweet_notifications_enabled%22%3A%20false%7D&variables=%7B%22user_id%22%3A%20%2244196397%22%2C%20%22rest_id%22%3A%20%2244196397%22%7D"
{"data":{"user_result":{"result":{"__typename":"User","rest_id":"44196397","has_nft_avatar":false,"is_blue_verified":true,"affiliates_highlighted_label":{"label":{"url":{"urlType":"DeepLink","url":"https://twitter.com/Twitter"},"badge":{"url":"https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF_bigger.jpg"},"userLabelType":"BusinessLabel","userLabelDisplayType":"Badge","description":"Twitter"}},"legacy":{"advertiser_account_service_levels":["analytics"],"advertiser_account_type":"promotable_user","analytics_type":"enabled","created_at":"Tue Jun 02 20:12:29 +0000 2009","description":"","entities":{"description":{"hashtags":[],"symbols":[],"urls":[],"user_mentions":[]}},"fast_followers_count":0,"favourites_count":26922,"followers_count":146412716,"friends_count":342,"geo_enabled":false,"has_custom_timelines":true,"has_extended_profile":true,"id_str":"44196397","is_translator":false,"location":"","media_count":1605,"name":"Elon Musk","normal_followers_count":146412716,"pinned_tweet_ids_str":[],"profile_background_color":"C0DEED","profile_banner_url":"https://pbs.twimg.com/profile_banners/44196397/1576183471","profile_image_url_https":"https://pbs.twimg.com/profile_images/1590968738358079488/IY9Gx6Ok_normal.jpg","profile_interstitial_type":"","profile_link_color":"0084B4","protected":false,"screen_name":"elonmusk","statuses_count":27575,"translator_type_enum":"None","verified":false,"withheld_in_countries":[]},"business_account":{}}}}}
Here's how you can get Elon Musk's profile info using the Android API:
$ curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" -H "x-guest-token: 1675958319943393280" -H "x-twitter-active-user: yes" -H "Referer: https://twitter.com" "https://api.twitter.com/graphql/oPppcargziU1uDQHAUmH-A/UserResultByIdQuery?features=%7B%22blue_business_profile_image_shape_enabled%22%3A%20false%2C%20%22verified_phone_label_enabled%22%3A%20false%2C%20%22super_follow_user_api_enabled%22%3A%20false%2C%20%22super_follow_badge_privacy_enabled%22%3A%20false%2C%20%22subscriptions_verification_info_enabled%22%3A%20false%2C%20%22creator_subscriptions_subscription_count_enabled%22%3A%20false%2C%20%22super_follow_exclusive_tweet_notifications_enabled%22%3A%20false%7D&variables=%7B%22user_id%22%3A%20%2244196397%22%2C%20%22rest_id%22%3A%20%2244196397%22%7D"
{"data":{"user_result":{"result":{"__typename":"User","rest_id":"44196397","has_nft_avatar":false,"is_blue_verified":true,"affiliates_highlighted_label":{"label":{"url":{"urlType":"DeepLink","url":"https://twitter.com/Twitter"},"badge":{"url":"https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF_bigger.jpg"},"userLabelType":"BusinessLabel","userLabelDisplayType":"Badge","description":"Twitter"}},"legacy":{"advertiser_account_service_levels":["analytics"],"advertiser_account_type":"promotable_user","analytics_type":"enabled","created_at":"Tue Jun 02 20:12:29 +0000 2009","description":"","entities":{"description":{"hashtags":[],"symbols":[],"urls":[],"user_mentions":[]}},"fast_followers_count":0,"favourites_count":26922,"followers_count":146412716,"friends_count":342,"geo_enabled":false,"has_custom_timelines":true,"has_extended_profile":true,"id_str":"44196397","is_translator":false,"location":"","media_count":1605,"name":"Elon Musk","normal_followers_count":146412716,"pinned_tweet_ids_str":[],"profile_background_color":"C0DEED","profile_banner_url":"https://pbs.twimg.com/profile_banners/44196397/1576183471","profile_image_url_https":"https://pbs.twimg.com/profile_images/1590968738358079488/IY9Gx6Ok_normal.jpg","profile_interstitial_type":"","profile_link_color":"0084B4","protected":false,"screen_name":"elonmusk","statuses_count":27575,"translator_type_enum":"None","verified":false,"withheld_in_countries":[]},"business_account":{}}}}}
It stops working fast.
Here's how you can get Elon Musk's profile info using the Android API:
$ curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" -H "x-guest-token: 1675958319943393280" -H "x-twitter-active-user: yes" -H "Referer: https://twitter.com" "https://api.twitter.com/graphql/oPppcargziU1uDQHAUmH-A/UserResultByIdQuery?features=%7B%22blue_business_profile_image_shape_enabled%22%3A%20false%2C%20%22verified_phone_label_enabled%22%3A%20false%2C%20%22super_follow_user_api_enabled%22%3A%20false%2C%20%22super_follow_badge_privacy_enabled%22%3A%20false%2C%20%22subscriptions_verification_info_enabled%22%3A%20false%2C%20%22creator_subscriptions_subscription_count_enabled%22%3A%20false%2C%20%22super_follow_exclusive_tweet_notifications_enabled%22%3A%20false%7D&variables=%7B%22user_id%22%3A%20%2244196397%22%2C%20%22rest_id%22%3A%20%2244196397%22%7D"
{"data":{"user_result":{"result":{"__typename":"User","rest_id":"44196397","has_nft_avatar":false,"is_blue_verified":true,"affiliates_highlighted_label":{"label":{"url":{"urlType":"DeepLink","url":"https://twitter.com/Twitter"},"badge":{"url":"https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF_bigger.jpg"},"userLabelType":"BusinessLabel","userLabelDisplayType":"Badge","description":"Twitter"}},"legacy":{"advertiser_account_service_levels":["analytics"],"advertiser_account_type":"promotable_user","analytics_type":"enabled","created_at":"Tue Jun 02 20:12:29 +0000 2009","description":"","entities":{"description":{"hashtags":[],"symbols":[],"urls":[],"user_mentions":[]}},"fast_followers_count":0,"favourites_count":26922,"followers_count":146412716,"friends_count":342,"geo_enabled":false,"has_custom_timelines":true,"has_extended_profile":true,"id_str":"44196397","is_translator":false,"location":"","media_count":1605,"name":"Elon Musk","normal_followers_count":146412716,"pinned_tweet_ids_str":[],"profile_background_color":"C0DEED","profile_banner_url":"https://pbs.twimg.com/profile_banners/44196397/1576183471","profile_image_url_https":"https://pbs.twimg.com/profile_images/1590968738358079488/IY9Gx6Ok_normal.jpg","profile_interstitial_type":"","profile_link_color":"0084B4","protected":false,"screen_name":"elonmusk","statuses_count":27575,"translator_type_enum":"None","verified":false,"withheld_in_countries":[]},"business_account":{}}}}}
It stops working fast.
That's expected and normal you just have to refresh your guest_token.
Ok this seems interesting, he seems he is able to get a Bearer tokens using built-in tokens from APPS as seens here
He then picks one randomly and send a post request in GenerateBearerToken() which correctly returns a Bearer Tokens
You can try yourself with curl :
curl -X POST "https://api.twitter.com/oauth2/token?grant_type=client_credentials" -u "CjulERsDeqhhjSme66ECg:IQWdVyqFxghAtURHGeGiWAsmCAGmdW3WmbEx6Hck"
You can then activate your fresh access_token :
curl -X POST -H "Authorization: Bearer your_access_token" "https://api.twitter.com/1.1/guest/activate.json"
This will return you a guest_token.
You can then access graphql endpoints :
curl -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \ -H "x-guest-token: 1675958319943393280" \ -H "x-twitter-active-user: yes" \ -H "Referer: https://twitter.com/" \ "https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName?variables=%7B%22screen_name%22%3A%22elonmusk%22%2C%22withSafetyModeUserFields%22%3Atrue%7D&features=%7B%22hidden_profile_likes_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D"
where x-guest-token is the token returned when you activated your Bearer.
You can get the user numeric ID from previous request, from twitter name : for elonmusk :
curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \ -H "x-guest-token: 1675958319943393280" \ -H "x-twitter-active-user: yes" \ -H "Referer: https://twitter.com/" \ "https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName?variables=%7B%22screen_name%22%3A%22elonmusk%22%2C%22withSafetyModeUserFields%22%3Atrue%7D&features=%7B%22hidden_profile_likes_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D" \ | jq -r '.data.user.result.rest_id'
Then get all his timeline :
curl -s -X GET -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR" \ -H "x-guest-token: 1675958319943393280" \ -H "x-twitter-active-user: yes" \ -H "Referer: https://twitter.com" "https://api.twitter.com/graphql/pNl8WjKAvaegIoVH--FuoQ/UserTweetsAndReplies?variables=%7B%22userId%22%3A%2244196397%22,%22count%22%3A40,%22includePromotedContent%22%3Atrue,%22withCommunity%22%3Atrue,%22withSuperFollowsUserFields%22%3Atrue,%22withDownvotePerspective%22%3Afalse,%22withReactionsMetadata%22%3Afalse,%22withReactionsPerspective%22%3Afalse,%22withSuperFollowsTweetFields%22%3Atrue,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22vibe_api_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse,%22interactive_text_enabled%22%3Atrue,%22responsive_web_text_conversations_enabled%22%3Afalse,%22longform_notetweets_richtext_consumption_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Afalse%7D" \ | jq
You have to replace the userID between the two "%22" tags. Here the user id (of elonmusk) is : 44196397
Which returns All his Tweets and Replies (too long):
https://bin.socialspill.com/ixomusor.json
To access detail of a specific tweet the url is :
https://api.twitter.com/graphql/XjlydVWHFIDaAUny86oh2g/TweetDetail?variables=%7B%22focalTweetId%22%3A% + statusId + %22,%22with_rux_injections%22%3Atrue,%22includePromotedContent%22%3Afalse,%22withCommunity%22%3Afalse,%22withQuickPromoteEligibilityTweetFields%22%3Afalse,%22withBirdwatchNotes%22%3Afalse,%22withSuperFollowsUserFields%22%3Afalse,%22withDownvotePerspective%22%3Afalse,%22withReactionsMetadata%22%3Afalse,%22withReactionsPerspective%22%3Afalse,%22withSuperFollowsTweetFields%22%3Afalse,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22vibe_api_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Afalse,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse,%22interactive_text_enabled%22%3Atrue,%22responsive_web_text_conversations_enabled%22%3Afalse,%22longform_notetweets_richtext_consumption_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Atrue%7D
Poor man's nitter:
#!/bin/sh
[ -z "$1" ] && echo "USAGE: $(basename $0) username_to_search" && exit
USERNAME=$1
echo "Searching..."
ACCESS_TOKEN=$(curl -s -X POST "https://api.twitter.com/oauth2/token?grant_type=client_credentials" -u "CjulERsDeqhhjSme66ECg:IQWdVyqFxghAtURHGeGiWAsmCAGmdW3WmbEx6Hck" | jq .access_token | cut -d '"' -f 2)
GUEST_TOKEN=$(curl -s -X POST -H "Authorization: Bearer $ACCESS_TOKEN" "https://api.twitter.com/1.1/guest/activate.json" | jq .guest_token | cut -d '"' -f 2)
USER_ID=$(curl -s -X GET -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-guest-token: $GUEST_TOKEN" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com/" \
"https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName?variables=%7B%22screen_name%22%3A%22$USERNAME%22%2C%22withSafetyModeUserFields%22%3Atrue%7D&features=%7B%22hidden_profile_likes_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D" \
| jq -r '.data.user.result.rest_id')
curl -s -X GET -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-guest-token: $GUEST_TOKEN" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com" "https://api.twitter.com/graphql/pNl8WjKAvaegIoVH--FuoQ/UserTweetsAndReplies?variables=%7B%22userId%22%3A%22$USER_ID%22,%22count%22%3A40,%22includePromotedContent%22%3Atrue,%22withCommunity%22%3Atrue,%22withSuperFollowsUserFields%22%3Atrue,%22withDownvotePerspective%22%3Afalse,%22withReactionsMetadata%22%3Afalse,%22withReactionsPerspective%22%3Afalse,%22withSuperFollowsTweetFields%22%3Atrue,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22vibe_api_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse,%22interactive_text_enabled%22%3Atrue,%22responsive_web_text_conversations_enabled%22%3Afalse,%22longform_notetweets_richtext_consumption_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Afalse%7D" \
| jq '..|.full_text?|select(length>0)'
Poor man's nitter:
🤣
poor_man_nitter.sh
Poor man's nitter:
rofl
muskie shows up
Love your poor_man_nitter.sh
Damn . SO syndication isn't working- there's no working way for the plebs to read a TL is there (other than these shell scripts you wizards are toying with now) .. correct? (no rss-bridge either)
poor_man_nitter.sh
hahaha what if @ElonMusk monetizes scrapping for ChatGPT+ providing an improved API instead of chasing users?
UPDATE: apologizes for the off topic - won't bother you again.
Damn . SO syndication isn't working- there's no working way for the plebs to read a TL is there (other than these shell scripts you wizards are toying with now) .. correct? (no rss-bridge either)
Not that I found
@polkaulfield have you find a way to get only tweets without replies ? or at least that it returns more tweets, because for example, for elon we can't see any of his tweet because of all his replies
Idk, ill take a look tomorrow. I just automated your commands and parsed the output for the laughs. Amazing work finding this API :D
I tried the rIIwMe1ObkGh_ByBtTCtRQ/UserTweets to no avail.
It always returns The following features cannot be null: [then it list all features].
Not sure what / if I did something wrong.
curl -s -X GET -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-guest-token: $GUEST_TOKEN" \
-H "x-twitter-active-user: yes" \
-H "Referer: https://twitter.com" "https://twitter.com/i/api/graphql/rIIwMe1ObkGh_ByBtTCtRQ/UserTweets?variables=%7B%22userId%22%3A%22$USER_ID%22,%22count%22%3A20,%22includePromotedContent%22%3Atrue,%22withQuickPromoteEligibilityTweetFields%22%3Atrue,%22withVoice%22%3Atrue,%22withV2Timeline%22%3Atrue%7D%26features%3D%7B%22rweb_lists_timeline_redesign_enabled%22%3Atrue,%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue,%22verified_phone_label_enabled%22%3Afalse,%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue,%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue,%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse,%22tweetypie_unmention_optimization_enabled%22%3Atrue,%22responsive_web_edit_tweet_api_enabled%22%3Atrue,%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue,%22view_counts_everywhere_api_enabled%22%3Atrue,%22longform_notetweets_consumption_enabled%22%3Atrue,%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Afalse,%22tweet_awards_web_tipping_enabled%22%3Afalse,%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue,%22standardized_nudges_misinfo%22%3Atrue,%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue,%22longform_notetweets_rich_text_read_enabled%22%3Atrue,%22longform_notetweets_inline_media_enabled%22%3Atrue,%22responsive_web_media_download_video_enabled%22%3Afalse,%22responsive_web_enhance_cards_enabled%22%3Afalse%7D%26fieldToggles%3D%7B%22withArticleRichContentState%22%3Afalse%7D"
Eliminating x-guest-token header has been working for me
Edit: Added authenticate(), TweetDetail()
import requests, json
from pprint import pprint as pp
headers = {
"Authorization": "Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR",
"x-guest-token": "",
"x-twitter-active-user": "yes",
"Referer": "https://twitter.com",
"user-agent": "",
}
def authenticate():
resp1 = requests.post("https://api.twitter.com/oauth2/token", params={"grant_type": "client_credentials"}, auth=("CjulERsDeqhhjSme66ECg", "IQWdVyqFxghAtURHGeGiWAsmCAGmdW3WmbEx6Hck")).json()
headers["Authorization"] = f"Bearer {resp1['access_token']}"
resp2 = requests.post("https://api.twitter.com/1.1/guest/activate.json", headers=headers).json()
headers["x-guest-token"] = resp2["guest_token"]
return {"access_token": resp1['access_token'], "guest_token": resp2["guest_token"]}
def UserByScreenName(username):
return requests.get("https://api.twitter.com/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName", headers=headers, params={
"features": json.dumps({
"hidden_profile_likes_enabled": False,
"responsive_web_graphql_exclude_directive_enabled": True,
"verified_phone_label_enabled": False,
"subscriptions_verification_info_verified_since_enabled": True,
"highlights_tweets_tab_ui_enabled": 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
}),
"variables": json.dumps({
"screen_name": username,
"withSafetyModeUserFields": True
})
}).json()
def UserResultByIdQuery(userId):
return requests.get("https://api.twitter.com/graphql/oPppcargziU1uDQHAUmH-A/UserResultByIdQuery", headers=headers, params={
"features": json.dumps({
"blue_business_profile_image_shape_enabled": False,
"verified_phone_label_enabled": False,
"super_follow_user_api_enabled": False,
"super_follow_badge_privacy_enabled": False,
"subscriptions_verification_info_enabled": False,
"creator_subscriptions_subscription_count_enabled": False,
"super_follow_exclusive_tweet_notifications_enabled": False
}),
"variables": json.dumps({
"user_id": f"{userId}",
"rest_id": f"{userId}"
})
}).json()
def UserTweetsAndReplies(userId):
return requests.get("https://api.twitter.com/graphql/pNl8WjKAvaegIoVH--FuoQ/UserTweetsAndReplies", headers=headers, params={
"features": json.dumps({
"responsive_web_twitter_blue_verified_badge_is_enabled": True,
"responsive_web_graphql_exclude_directive_enabled": True,
"verified_phone_label_enabled": False,
"responsive_web_graphql_timeline_navigation_enabled": True,
"responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
"tweetypie_unmention_optimization_enabled": True,
"vibe_api_enabled": True,
"responsive_web_edit_tweet_api_enabled": True,
"graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
"view_counts_everywhere_api_enabled": True,
"longform_notetweets_consumption_enabled": True,
"tweet_awards_web_tipping_enabled": False,
"freedom_of_speech_not_reach_fetch_enabled": False,
"standardized_nudges_misinfo": True,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": False,
"interactive_text_enabled": True,
"responsive_web_text_conversations_enabled": False,
"longform_notetweets_richtext_consumption_enabled": False,
"responsive_web_enhance_cards_enabled":False
}),
"variables": json.dumps({
"userId": f"{user_id}",
"count": 40,
"includePromotedContent": True,
"withCommunity": True,
"withSuperFollowsUserFields": True,
"withDownvotePerspective": False,
"withReactionsMetadata": False,
"withReactionsPerspective": False,
"withSuperFollowsTweetFields": True,
"withVoice": True,
"withV2Timeline":True
})
}).json()
def TweetDetail(tweetId):
return requests.get("https://api.twitter.com/graphql/XjlydVWHFIDaAUny86oh2g/TweetDetail", headers=headers, params={
"features": json.dumps({
"rweb_lists_timeline_redesign_enabled": True,
"creator_subscriptions_tweet_preview_api_enabled": True,
"responsive_web_twitter_article_tweet_consumption_enabled": False,
"longform_notetweets_rich_text_read_enabled": True,
"longform_notetweets_inline_media_enabled": True,
"responsive_web_media_download_video_enabled": True,
"responsive_web_enhance_cards_enabled": True,
"responsive_web_twitter_blue_verified_badge_is_enabled":True,
"responsive_web_graphql_exclude_directive_enabled":True,
"verified_phone_label_enabled":False,
"responsive_web_graphql_timeline_navigation_enabled":True,
"responsive_web_graphql_skip_user_profile_image_extensions_enabled":False,
"tweetypie_unmention_optimization_enabled":True,
"vibe_api_enabled":True,
"responsive_web_edit_tweet_api_enabled":True,
"graphql_is_translatable_rweb_tweet_is_translatable_enabled":False,
"view_counts_everywhere_api_enabled":True,
"longform_notetweets_consumption_enabled":True,
"tweet_awards_web_tipping_enabled":False,
"freedom_of_speech_not_reach_fetch_enabled":False,
"standardized_nudges_misinfo":True,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":False,
"interactive_text_enabled":True,
"responsive_web_text_conversations_enabled":False,
"longform_notetweets_richtext_consumption_enabled":False,
"responsive_web_enhance_cards_enabled":True
}),
"variables": json.dumps({
"focalTweetId":tweetId,
"with_rux_injections":True,
"includePromotedContent":False,
"withCommunity":False,
"withQuickPromoteEligibilityTweetFields":False,
"withBirdwatchNotes":False,
"withSuperFollowsUserFields":False,
"withDownvotePerspective":False,
"withReactionsMetadata":False,
"withReactionsPerspective":False,
"withSuperFollowsTweetFields":False,
"withVoice":True,
"withV2Timeline":True
}),
"fieldToggles": json.dumps({
"withArticleRichContentState": False
})
}).json()
test_username = 'elonmusk'
test_tweet_id = 1675390796718014464
authenticate()
user_id = UserByScreenName(test_username)['data']['user']['result']['rest_id']
tweets = UserTweetsAndReplies(user_id)['data']['user']['result']['timeline_v2']['timeline']['instructions'][1]['entries']
tweet_thread = TweetDetail(test_tweet_id)['data']['threaded_conversation_with_injections_v2']['instructions'][0]['entries']
pp(tweet_thread)
Has anyone figured out any graphql endpoint for retrieving tweets by id? I have a list of like several thousand tweets I'd like to get asap with as little manual work as possible.
https://techcrunch.com/2023/06/30/twitter-now-requires-an-account-to-view-tweets/
the nitter crawler will need to be recreated...