By Sorsa Editorial

Updated July 2026: restated Sorsa media pricing on the per-1,000-tweets batch base, added the 100 free requests starting allowance, reconfirmed the official X API per-resource rates after the April 2026 update, and reverified yt-dlp's native X extractor.

Key Takeaway: To download Twitter (X) media via API, request the tweet, then read its media objects for the direct file URL. The official X API returns video links inside the variants array through expansions, while third-party REST APIs return them in one call. Images come from pbs.twimg.com, video from video.twimg.com.

The hard part of pulling Twitter media in code is rarely the download. It is getting a clean, direct media URL at scale without OAuth flows, brittle scraping, or per-resource billing. Sorsa API, an alternative Twitter/X API, returns every tweet's media links next to the full author profile in a single request, accepts up to 100 tweet IDs per batch call, and runs on a flat 20 requests per second on every plan. Batched, that puts media extraction at from $0.02 per 1,000 tweets, media and author data included, so pulling across thousands of posts costs cents rather than the official API's stacked per-resource reads.

This guide covers the three approaches developers actually use in 2026: the official X API, the open-source yt-dlp, and a managed Twitter/X data API. You get the working request for each, the real cost, and a clear read on which one fits which job.

How media is attached to a tweet {#how-media-is-attached-to-a-tweet}

A tweet does not contain a media file. It contains references to media objects, and those objects point to files hosted on X's content servers. Images live on pbs.twimg.com and video on video.twimg.com, usually under a path like amplify_video. To download anything, you fetch the tweet, find its media object, and read the direct URL.

Two details trip people up. Video is served as one or more MP4 variants at different bitrates, so "the video URL" is really a list, and you pick the highest-bitrate file. GIFs are not GIFs at all on X; the platform stores them as silent looped MP4s, so a GIF download returns an MP4. Photos are simpler: each media object carries a single image URL, and the original upload is larger and sharper than the compressed copy shown in the timeline.

Every method below is a different way to reach that same media object. The differences are in authentication, cost, how much parsing you do, and whether the approach holds up when you run it across thousands of posts.

Method 1: the official X API (expansions and variants) {#method-1-the-official-x-api}

The official X API can return media URLs, but only when you ask for them explicitly. By default a tweet response carries just the id and text. To get media you add an expansion for the attached media keys plus the media fields you want, then read the result out of the includes.media section of the response.

bash
curl "https://api.twitter.com/2/tweets/TWEET_ID?expansions=attachments.media_keys&media.fields=variants,url,preview_image_url&tweet.fields=attachments" \
  -H "Authorization: Bearer $BEARER_TOKEN"

For video, the media object returns a variants array, and each entry carries a bit_rate and a url. You sort by bit_rate and take the top MP4. For photos, the object returns a url directly. The video URL was not available at the v2 launch and arrived later through this expansion, which is why many developers get an empty response on their first attempt.

The method is authoritative, and it is the only path when you also need official write actions or compliance feeds. The cost and the friction are the problem. Under the pay-per-use model in effect after the April 2026 pricing update, the official API has no free tier and bills per resource: roughly $0.005 per post read and $0.010 per user read, with media counted as its own resource on top (rates verified July 2026). Pay-per-use accounts are capped at 2,000,000 post reads per month, and authentication is OAuth 2.0 with a Bearer token. For a single tweet that is fine. For a pipeline that reads tweets, their media, and their authors at volume, the per-resource meter climbs fast and the parsing stays on you.

Method 2: yt-dlp for one-off and local archiving {#method-2-yt-dlp}

yt-dlp is the open-source command-line downloader most developers reach for when they just need a file on disk. It is a maintained fork of youtube-dl, ships frequent updates, covers more than 1,800 sites, and supports Twitter/X natively. For a single public video it is one line.

bash
yt-dlp "https://x.com/USER/status/TWEET_ID"

It selects the best quality by default and uses FFmpeg to merge streams where needed, so you install FFmpeg alongside it. For your own posts or anything behind a login you pass cookies from a signed-in browser session:

bash
yt-dlp --cookies-from-browser chrome "https://x.com/USER/status/TWEET_ID"

yt-dlp is excellent for personal archiving, transformative use of your own content, and quick scripts. It is a weaker fit for production media pipelines for three reasons. It hands you a downloaded file rather than structured URLs and metadata, so building a database of media links means parsing its output yourself. Its extractor breaks whenever X changes its page structure, which means pinning versions and rebuilding regularly. And running it across a large batch from a single machine invites IP rate-limiting and blocks, because you are scraping the front end rather than calling a managed endpoint. Private accounts, paywalled Creator content, and direct messages sit behind authentication and are off-limits without permission.

Method 3: a managed Twitter/X data API {#method-3-a-managed-twitter-x-data-api}

A managed Twitter/X data API removes both the OAuth setup and the variant parsing. With Sorsa, one call to the Tweet Data endpoint returns the full tweet, its media, and the complete author profile, authenticated with a single ApiKey header.

bash
curl -X POST "https://api.sorsa.io/v3/tweet-info" \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tweet_link": "https://x.com/USER/status/TWEET_ID"}'

The response carries an entities array, where each media item lists its type (photo, video, or url), a direct link to the file, and a preview thumbnail:

json
{
  "id": "1782368585664626774",
  "full_text": "Example tweet text",
  "entities": [
    {
      "type": "video",
      "link": "https://video.twimg.com/amplify_video/.../vid.mp4",
      "preview": "https://pbs.twimg.com/.../thumb.jpg"
    }
  ],
  "user": { "username": "user", "followers_count": 100000 }
}

No expansions to request, no variants array to sort, no token refresh. For known sets of posts, the Tweet Data (Batch) endpoint takes up to 100 tweet IDs and returns them all as a single billable request:

bash
curl -X POST "https://api.sorsa.io/v3/tweet-info-bulk" \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tweet_links": ["TWEET_ID_1", "TWEET_ID_2", "TWEET_ID_3"]}'

Because Sorsa bills one request per call rather than per resource, the author profile attached to every tweet is included at no extra cost, and there is no separate media charge. Batched through the bulk endpoint, media extraction runs from $0.02 per 1,000 tweets: $0.049 per 1,000 on Starter, $0.02 on Pro, and $0.018 on Enterprise, with every media link and author profile returned. Standalone profile pulls run from $0.01 per 1,000. The same 1,000 posts on the official X API start at $5.00 in post reads alone, before separate media and per-author reads (verified July 2026). Full plan details sit on the flat-rate pricing plans.

If you only need a file now and again, or you are not a developer, the same engine powers a no-code Twitter media downloader: paste a tweet link, get the MP4 or full-resolution image, no key required.

How the three methods compare {#comparison}

Each method wins a different job. The picks below assign each its strongest use case, then the cost table shows where a flat-rate model pulls ahead of the official API.

One-off saves and local archiving: yt-dlp, a free command-line tool when you just need a file on disk and can absorb the upkeep.

Official write actions or compliance feeds: the official X API, the required path for posting, direct messages, and licensed data.

Read-heavy media extraction across many posts: Sorsa, flat per-request billing with batch calls, structured URLs, and author data included.

If you need write access or licensed data, that is the official API's territory. For read-heavy media access at a flat, predictable price, a managed alternative wins, running up to 50x cheaper than the official X API on that work, with batched pulls widening the gap further. Since yt-dlp is free to run but self-hosted, the cost comparison that decides real pipelines is against the official API:

Cost to pull 1,000 tweets with media and authorOfficial X APISorsa API (batched)
Billing unitper resource fetchedper request
Post reads$5.00included
Author reads$10.00included
Cost per 1,000 tweets$15.00 before mediafrom $0.02

One Sorsa request returns up to 100 tweets or 200 profiles, so a thousand tweets is ten batched calls, and the author profile on every tweet is free (verified July 2026).

Most real jobs are not one tweet. You want every video a profile posted, or all images matching a query. The pattern is to list the tweets, collect the media links from each, then fetch the files.

Pull a profile's posts with the User Tweets endpoint, which returns about 20 tweets per page along with their entities. Follow the next_cursor to page through the rest:

bash
curl -X POST "https://api.sorsa.io/v3/user-tweets" \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "USERNAME"}'

For a keyword or hashtag, swap in the Search Tweets endpoint and read media from the same entities field. Once you have a set of tweet IDs, the batch endpoint collapses up to 100 of them into one request, which keeps both your request count and your bill low. The whole flow is a few endpoints and a loop, with no OAuth and no front-end scraping to maintain. Developers moving from a scripted setup often pair this with the guide to pulling X data in Python.

Image quality: getting full-resolution photos {#image-quality}

The photos you see in the timeline are compressed previews tuned to load quickly, not the originals. Saving with a right-click, or grabbing the first URL you see, often leaves you with a downscaled copy. The media object's image URL points to the original upload, which can be several times larger in pixel dimensions and noticeably sharper.

This matters most when the pixels carry information: infographics, screenshots of text, charts, or any image headed for OCR or a training set. A managed API returns the full-resolution link directly, so you are not stitching together size parameters or guessing at the original. When images are the whole job, the dedicated walkthrough on downloading Twitter images via API covers the photo path end to end, and teams assembling labeled corpora can follow the steps for building a Twitter dataset for machine learning. For datasets and archives, pulling the original once is cleaner than re-fetching a sharper copy later.

Downloading publicly posted media for personal reference or internal analysis is generally acceptable. Reposting, redistributing, or using it commercially without the creator's permission can breach copyright and X's Terms of Service, and that risk sits with what you do with the file, not with the tool that fetched it. Private and protected accounts are off-limits without authorization, and you should respect rate limits rather than hammering endpoints. When the use is public-facing, the safe move is to credit or clear it with the creator.

In practice {#in-practice}

A roughly 15-person media-monitoring startup moved to a flat-rate API after its brand-safety product kept overrunning budget. The team was reading video URLs from the official API, where the per-resource meter charged separately for each post read, each media resource, and each author lookup. During news spikes the volume pushed them toward the 2,000,000 post-read cap, and the OAuth plus variants parsing was a standing maintenance cost. Routing the read path through one batched request per 100 tweets took the media-read line from dollars per thousand tweets to about two cents, a cut of more than 90 percent, and collapsing the call to a single ApiKey request removed a class of token and parsing bugs. For teams running brand monitoring workflows, matching the billing model to a read-heavy load is the whole win.

FAQ

Can you download a Twitter video with the official X API?

Yes. The official X API returns video links when you request the media expansion attachments.media_keys together with media.fields=variants. The video comes back as an array of MP4 variants at different bitrates, and you select the highest one. Photos return a single url. It works but bills per resource and requires OAuth 2.0.

How do you get the direct MP4 URL from a tweet?

Fetch the tweet's media object and read the highest-bitrate entry in its video variants, which is the direct MP4 on video.twimg.com. Images return the original file URL on pbs.twimg.com. A managed Twitter/X API returns these links in the response without the expansion and parsing steps the official API requires.

Is there an API that returns Twitter media URLs without OAuth?

Yes. Sorsa API, an alternative Twitter/X API, returns each tweet's media links and the full author profile in one request authenticated with a single ApiKey header, with no OAuth flow. It batches up to 100 tweets per call and runs on a flat 20 requests per second on every plan, which suits pulling media at scale.

Can yt-dlp download Twitter/X videos in 2026?

Yes. yt-dlp has a native Twitter/X extractor, is actively maintained, and downloads public tweet videos with one command, using FFmpeg to merge streams. Content behind a login needs cookies from a signed-in session. It is best for one-off and local archiving rather than managed pipelines, because its extractor breaks when X changes its markup.

How much does it cost to download Twitter media at scale?

On the official X API, reading media bills per resource, roughly $0.005 per post read plus separate media and $0.010 user reads, capped at 2,000,000 post reads a month. A flat-rate API like Sorsa charges per request instead, so batching a thousand tweets costs from $0.02 per 1,000, about two cents on the Pro plan, media and author data included.

Can you download images in full resolution via API?

Yes. The media object's image URL points to the original uploaded file, not the compressed preview shown in the timeline, and the original is often several times larger in pixel dimensions. Pulling the full-resolution link matters for infographics, text screenshots, OCR, and datasets where image detail is part of the data.

Getting started {#getting-started}

If your job is structured media access across many posts, setup takes minutes rather than an approval queue. Every new account starts with 100 free requests: a one-time allowance, no credit card, that never expire and cover all 40 endpoints, enough for up to 10,000 tweets or 20,000 profiles. Create a key, then make your first call with a single ApiKey header, no OAuth and no developer-account review. The Sorsa quickstart gets you to a first response fast, and the flat 20 requests per second applies on every plan. For one-off saves, the no-code media downloader needs no key at all.


Reviewed by Keksich, founder of Sorsa, marketer and X API researcher.

This guide draws on our day-to-day work operating Sorsa's read-only Twitter/X API, with every endpoint and code sample checked against the live v3 API while writing. The official-API path was verified against X's developer documentation for media expansions and the variants field; the cost figures use X's pay-per-use rates as of the April 2026 update alongside Sorsa's published plans. yt-dlp's current X extractor was confirmed against the maintained project. Three retrieval methods were compared: the official X API, yt-dlp, and Sorsa. Verified July 2026. More about the team is on the Sorsa About page.