vercel / react-tweet

Embed tweets in your React application.

Home Page:https://react-tweet.vercel.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How do you learn about the twitter "API"?

kentcdodds opened this issue · comments

The twitter API used here seems unofficial. Where do you learn about the possible options?

these apis can break at anytime

You can discover the unofficial apis by looking at blogs/repos from people that have done it before eg: the fa0311/TwitterInternalAPIDocument repo or checking the devtools network tab, specifically the Fetch/XHR tab.

some examples

cdn.syndication.twimg.com/tweet-result?

Used at publish.twitter.com for the official embed but not documented.

Usage:

Make GET request to cdn.syndication.twimg.com/tweet-result?id=20&lang=en

  • No special headers requred, just a valid tweet id. (You can visit the url from a browser)
  • Doesn't include all the tweet data eg: spaces, longform tweets, rt count

Example using javascript

async function getTweet(id) {
    const url = `https://cdn.syndication.twimg.com/tweet-result?id=${id}`
    const response = await fetch(url)
    const json = await response.json()
    return json
}

api.twitter.com/*

Used by the official twitter web client so it's very rich. It's always changing and fields can be deleted without warning.
eg: the co-tweet feature was removed from all api responses.

Usage:

Get a (public) bearer token from devtools in an incognito twitter.com tab. The current one is
AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA
but it might be changed.

Make POST Request to api.twitter.com/1.1/guest/activate.json with the bearer token in the Authorization header.

The response will be something like this.

{"guest_token": "123"}

The guest_token value is used to set a valid X-Guest-Token header. It can be reused but it expires after some time (not sure how long it lasts), so you need to renew it.

An example of getting a tweet from the statuses/lookup.json endpoint using curl

#!/bin/bash
bearer="AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
# get guest token & parse json with jq
GT=$(curl -X 'POST' "https://api.twitter.com/1.1/guest/activate.json" -H "Authorization: Bearer $bearer" | jq -r  .guest_token )
# Get tweet with hardcoded id of 20, & pretty print output with jq .
curl "https://api.twitter.com/1.1/statuses/lookup.json?include_entities=1&id=20" -H "X-Guest-Token: $GT" -H "Authorization: Bearer $bearer" | jq .

With these headers, you can do most read-only operations. ie: the same experience as a logged out twitter user.

It's also very verbose and has lots of flags.

Here's an example using the TweetResultByRestId graphql endpoint.

// main.ts
export async function TweetResultByRestID(tweetId: string, headers: HeadersInit): Promise<any> {
    let url = "https://api.twitter.com/graphql/ncDeACNGIApPMaqGVuF_rw/TweetResultByRestId"
    const variables = {
        tweetId: tweetId,
        includePromotedContent: true,
        withBirdwatchNotes: true,
        withCommunity: true,
        withDownvotePerspective: true,
        withReactionsMetadata: true,
        withReactionsPerspective: true,
        withSuperFollowsTweetFields: true,
        withSuperFollowsUserFields: true,
        withVoice: true,
    }

    const features = {
        freedom_of_speech_not_reach_fetch_enabled: true,
        graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
        interactive_text_enabled: true,
        longform_notetweets_consumption_enabled: true,
        longform_notetweets_richtext_consumption_enabled: true,
        responsive_web_edit_tweet_api_enabled: true,
        responsive_web_enhance_cards_enabled: true,
        responsive_web_graphql_exclude_directive_enabled: true,
        responsive_web_graphql_skip_user_profile_image_extensions_enabled: true,
        responsive_web_graphql_timeline_navigation_enabled: true,
        responsive_web_text_conversations_enabled: true,
        responsive_web_twitter_blue_verified_badge_is_enabled: true,
        standardized_nudges_misinfo: true,
        tweet_awards_web_tipping_enabled: true,
        tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
        tweetypie_unmention_optimization_enabled: true,
        verified_phone_label_enabled: true,
        vibe_api_enabled: true,
        view_counts_everywhere_api_enabled: true,
    }

    url = `${url}?variables=${encodeURI(JSON.stringify(variables))}&features=${encodeURI(JSON.stringify(features))}`
    const response = await fetch(url, { method: 'GET', headers: headers })
    const json = await response.json();
    return json
}

async function genHeaders() {
    const url = "https://api.twitter.com/1.1/guest/activate.json";
    const bearer =
        `AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA`;
    const response = await fetch(url, {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${bearer}`
        },
    });
    const json = await response.json();

    return {
        "Authorization": `Bearer ${bearer}`,
        "X-Guest-Token": json.guest_token,
    };
}

console.log(JSON.stringify(await TweetResultByRestID("20", await genHeaders()), null, " "))

You can run it by copying the code to a .ts file then running it with deno (it will ask for permissons)

deno run main.ts

I found a useful repo automatically documenting all the graphql endpoints at https://github.com/fa0311/TwitterInternalAPIDocument.

You can also use the genereated headers in some offical & documented REST endpoints that don't requre authentication eg: https://api.twitter.com/1.1/statuses/lookup.json?include_entities=1&id=20 which is
documented at https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/get-statuses-lookup

Links

Thanks a lot!

Are you sure that token you shared isn't your personal token?

Yeah, I'm sure it's not anyone's personal token.

This blog post from 2020 includes it, the only difference is that the = is encoded to %3D (not sure why)

https://www.mufaddal.dev/scraping/2020/11/23/Scraping-Twitter.html

How?
The authorization : Bearer
seems to be invariant, incase if it ever changes, just inspect one of the API requests headers in your (incognito) browser to get it, make sure to include it in every request. The next important bit is the x-guest-token header, that’s what permits you to make any tweet fetching API requests. Acquire it by making a POST the /1.1/guest/activate.json endpoint, don’t forget to include the authorization header.

You can also find the exact string in some code search tools

Sourcegraph

  • 251 results, including projects like yt-dlp & nitter

https://sourcegraph.com/search?q=context%3Aglobal+AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%253D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA&patternType=standard&sm=1&groupBy=repo

Without encoding the = to %3D it's just 16 results

https://sourcegraph.com/search?q=context%3Aglobal+AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA&patternType=standard&case=yes&sm=1&groupBy=repo

Github Search

https://github.com/search?q=AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%253D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA&type=code

  • 500+ files
  • 10 issues (oldest one from jan 2022)

Without encoding the = to %3D , it's 72 results
https://github.com/search?q=AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA&type=code

The token with & without encoding compared

-AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA
+AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA

@kentcdodds Twitter's embed makes a request for the data it uses. You can see that data for any tweet under the network tab in https://publish.twitter.com/. What I basically did was get some tweets with different types of content and build all the types for it manually (https://github.com/vercel-labs/react-tweet/tree/main/packages/react-tweet/src/api/types).

As it's an unofficial API it can change at any moment and break something.