Get a video URL from Twitter API v2

Andy Piper - Aug 15 '22 - - Dev Community

Twitter API v2 has been available for quite a while now, but it's an evolving process as we bring more endpoints and features to the new environment.

One data field that was not initially available at launch was the URL for videos attached to Tweets. This information was added to the data expansions earlier this year, so if you are making a request to the API today, you can now add query parameters to have multiple video variant URLs returned as part of an attached expansion.

If you're into the details, you can always check what functionality is available in the API by grabbing the latest OpenAPI specification document, which lives at https://api.twitter.com/2/openapi.json - you can look at the Changelog for a more high-level list of updates

Twitter API v2 has many improvements over the legacy version, one of them being the concepts of fields and expansions. These are query parameters that control which elements of data are returned on an API call. To get the video information, the data we want to return is part of the media object, contained in variants, which is an array of available display formats.

Building a query

I'll pick a recent Tweet with an attached video from my own timeline at random:

The Tweet ID here is 1557438090897137665 (I got this by taking it from the URL of the Tweet on Twitter, but I could have picked it out from an API query). If we request that Tweet via the API, by default only the the basic fields will be returned (for a Tweet, these are text and id). In this case, we want to get the video URLs that are attached. In oder to do this, we need the following combination of fields and expansions:

  • tweet.fields - I need to ask for any attachments (the concept here is that videos are attached to Tweets, and so are images, polls, etc)
    • expansions - the name of the expansion is attachments.media_keys

These two parameters will now tell the API to return any media object associated with the Tweet inside the includes section of the response, but by default, the default fields for the media object are media_key (the value that links the data to the Tweet itself), and type (video, image, etc). So we need to add third parameter:

  • media.fields - the video data is in the variants array. It may also be useful to request alt_text (if the user has set alternative text for accessibility), preview_image_url, duration_ms, and possibly also public_metrics so that we can see how many times the video has been viewed.

The final request we've built includes the URL parameters ?tweet.fields=attachments&expansions=attachments.media_keys&media.fields=variants,alt_text,preview_image_url,duration_ms,public_metrics. These parameters can be used on any API calls that are expected to return Tweet objects, and will expand the media to include appropriate video information when available.

Show us some code!

The Twitter team released some beta SDKs for the new API, starting with packages for JavaScript and TypeScript in Node, and also Java.

If you don't code in these languages (or even if you do, but you're familiar with a different SDK), the community has created a range of great libraries to help you code against the Twitter API!

Let's go ahead and use the TypeScript SDK to run our query. You'll need a Twitter Developer Account (if you don't have one yet, sign up here, it's free!), and to create a project containing an app to get a Bearer Token. You'll also need to install the Twitter SDK (add "twitter-api-sdk": "^1.1.0" to your package.json file)

Here's the code to get the same Tweet we looked at before, using the same set of parameters to the API.

import { Client } from "twitter-api-sdk";

if (!process.env.BEARER_TOKEN) throw new Error("Please add your BEARER_TOKEN as an environment variable in the Secrets section");

const client = new Client(process.env.BEARER_TOKEN as string);

async function main() {
  const { data, includes } = await client.tweets.findTweetsById({
    "ids": [
      "1557438090897137665"
    ],
    "tweet.fields": [
      "attachments"
    ],
    "expansions": [
      "attachments.media_keys"
    ],
    "media.fields": [
      "alt_text",
      "duration_ms",
      "preview_image_url",
      "public_metrics",
      "variants"
    ]
  });

  if (!data) throw new Error("Couldn't retrieve Tweet");

  console.log("\nTweet text:\t" + data[0].text);
  console.log("Media:\t\t" + includes.media[0].type);
  console.log("Alt text:\t" + includes.media[0].alt_text);
  console.log("Views:\t\t" + includes.media[0].public_metrics.view_count);

  console.table(includes.media[0].variants, ['bit_rate', 'url']);

  // dump whole JSON data objects
  // console.log("data", JSON.stringify(data, null, 1));
  // console.log("includes", JSON.stringify(includes, null, 1));

}

main();
Enter fullscreen mode Exit fullscreen mode

Let's go through this code. We start out by bringing in the Twitter SDK, and then we read the value of the bearer token in from the environment variables (you can do this via setting up secrets in a cloud runtime, or create a local shell environment variable e.g. export BEARER_TOKEN=your-bearer-token-value at the command line).

Inside our main function, we know the ID of the Tweet we want, so we use client.tweets.findTweetsById to call the GET /2/tweets/:id API, passing in our Tweet ID. We could easily choose to use a search API, or we could request multiple Tweets at once, if we wanted to do so. Note that when we make the call, we request both the data and the includes (since the video information is included under includes, where attachments are added in the response)

We know the parameters we need to get the values, so we add those to the request.

Once the data is returned, we can grab just the values we want to show. In this example we're using console.table to quickly throw the URLs and additional fields into a table, but we could choose the handle it differently. To debug, we can simply dump out the whole JSON format data and includes, but that can stay commented out if we are confident with the data format.

Tweet text: @biglesp @Jolly_Device It blinks! The module that is… not coded yet. https://t.co/ErJABKMFYL
Media:      video
Alt text:   undefined
Views:      13
┌─────────┬──────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ (index) │ bit_rate │                                                     url                                                      │
├─────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│    0    │  256000  │    'https://video.twimg.com/ext_tw_video/1557437992389722112/pu/vid/480x270/XRx8UwJoOLZS1AE4.mp4?tag=14'     │
│    1    │  832000  │    'https://video.twimg.com/ext_tw_video/1557437992389722112/pu/vid/640x360/kbjqEmwvIA1_3zFq.mp4?tag=14'     │
│    2    │ 2176000  │    'https://video.twimg.com/ext_tw_video/1557437992389722112/pu/vid/1280x720/1nqh8h8iFoHno66E.mp4?tag=14'    │
│    3    │          │ 'https://video.twimg.com/ext_tw_video/1557437992389722112/pu/pl/e2nunEhaXNarlcHp.m3u8?tag=14&container=fmp4' │
│    4    │ 10368000 │   'https://video.twimg.com/ext_tw_video/1557437992389722112/pu/vid/1920x1080/QXFoJWg4begKhDss.mp4?tag=14'    │
└─────────┴──────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

I didn't add alt text for this video, which was not very user-friendly of me - I'll do better!

That's it - that's the sample. We looked at:

  • where video information exists in the v2 API response
  • which API parameters are needed (the expansions and fields added to the request - there are others to experiment with)
  • quickly getting started with the beta Twitter SDK for TypeScript

I've created a template on Replit that can be forked and modified - add a valid BEARER_TOKEN value to the project secrets. You can also find more examples of using the Twitter SDKs on Replit.

Hope you found this helpful - let us know in the comments!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .