X (Twitter) Media Scraper | $0.40/1K | Photos and Videos avatar

X (Twitter) Media Scraper | $0.40/1K | Photos and Videos

Pricing

from $0.32 / 1,000 media returneds

Go to Apify Store
X (Twitter) Media Scraper | $0.40/1K | Photos and Videos

X (Twitter) Media Scraper | $0.40/1K | Photos and Videos

Export photos, videos and gifs from X (Twitter) accounts or posts. One row per attachment with every video encoding, bitrate, dimensions, duration, alt text, preview image, and the parent post with its author and engagement. Direct links, ready to download, as JSON, CSV or Excel. By FeedMiner.

Pricing

from $0.32 / 1,000 media returneds

Rating

0.0

(0)

Developer

FeedMiner

FeedMiner

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

8 hours ago

Last modified

Share

🎞️ Every encoding, not just one. A video comes back with its full variant list, best first: the highest bitrate for archiving, the smallest for a preview. Tools that return a single link decide for you.

💸 $0.40 per 1,000 files, down to $0.32 on the larger Apify plans. The visible alternatives charge $2.99 and $5.00 per 1,000 for the same links, plus a start fee on every run. No start fee here.

🖼️ The post travels with the file. Each row carries the parent post, its date, its author and its engagement, so a media export doubles as a content performance table.

Alt text included. The accessibility description creators write is on the row, which is the field content teams and dataset builders ask for first.

FeedMiner · X (Twitter) suite • Posts, profiles, audiences, media, trends
X Media Scraper
➤ You are here
X Mentions Monitor
Brand watch, only what is new · $0.12/1K mentions
X Engagement Scraper
Quote authors and repliers · $0.15/1K accounts

Copy to your AI assistant

feedminer/x-media-scraper on Apify. Exports photos, videos and gifs from X (Twitter) accounts or individual posts. One row per attachment with id, type, url (best encoding), previewUrl, contentType, bitrate, width, height, durationMs, altText, variants[] and the parent post (tweetId, tweetUrl, text, createdAt, likeCount, viewCount, author). Call ApifyClient("TOKEN").actor("feedminer/x-media-scraper").call(run_input={...}), then client.dataset(run["defaultDatasetId"]).list_items().items. Input: handles (string[]), startUrls (string[] of post URLs or ids), mediaTypes (string[] of photo|video|gif), maxItems (int), sessions (array, optional). Full spec: GET https://api.apify.com/v2/acts/feedminer~x-media-scraper/builds/default (Bearer TOKEN) → inputSchema, actorDefinition.storages.dataset, readme. Token: https://console.apify.com/account/integrations

How to scrape media from X (Twitter)

Basic: everything an account posted

{ "handles": ["nasa"], "maxItems": 200 }

Videos only, for a highlight reel

{ "handles": ["nasa"], "mediaTypes": ["video"], "maxItems": 50 }

A single post you already have

{ "startUrls": ["https://x.com/NASA/status/2089425053599289546"] }

A batch of posts from another run

{
"startUrls": [
"https://x.com/NASA/status/2089425053599289546",
"https://x.com/NASA/status/2089802824678879526"
],
"maxItems": 100
}

Pasted posts are read together, so a hundred URLs cost one or two requests rather than a hundred.

Photos from several accounts, for a brand asset sweep

{ "handles": ["stripe", "adyen"], "mediaTypes": ["photo"], "maxItems": 400 }

Input parameters

ParameterTypeDefaultDescription
handlesarrayAccounts whose media you want, as handles or profile URLs
startUrlsarrayIndividual posts to read, as post URLs or ids
mediaTypesarrayLeave empty for everything
maxItemsinteger1000Stop once this many attachments have been collected

Advanced

ParameterTypeDefaultDescription
sessionsarrayOptional. The Actor works without one. X accounts of your own, for anyone who would rather the run used accounts they control
debugModebooleanfalseVerbose logs for support

Coming from another media downloader

tweetUrls, links, urls and postUrls are all read as posts, and onlyVideos or onlyImages select the kind. The input you already send elsewhere works unchanged.

What an account changes here

Nothing about what you get, only how fast. An account makes an account's media tab answer straight away; without one the same rows still come back, they just take longer.


What data can you extract from X media?

FieldTypeDescription
idstringMedia id
typestringphoto, video or animated_gif
urlstringDirect link to the file. For a video, the best encoding
previewUrlstringStill image, the poster frame for a video
contentTypestringMIME type of url, for example video/mp4
bitrateintegerBitrate of url, in bits per second
width heightintegerOriginal dimensions
durationMsintegerVideo duration in milliseconds
altTextstringAccessibility description written by the author
variantsarrayEvery encoding, best first, each with url, bitrate, contentType
tweetIdstringParent post id
tweetUrlstringParent post link
textstringParent post text
createdAtstringParent post date, RFC 3339
likeCount viewCountintegerParent post engagement
authorobjectHandle, name, bio, followers, verification, avatar

Example output

{
"id": "2089424963400818688",
"type": "video",
"url": "https://video.twimg.com/amplify_video/2089424963400818688/vid/avc1/1920x1080/xyz.mp4",
"previewUrl": "https://pbs.twimg.com/media/HP8hAu4WEAAJLH7.png",
"contentType": "video/mp4",
"bitrate": 10368000,
"width": 1920,
"height": 1080,
"durationMs": 45120,
"altText": "Artemis II crew waves before boarding",
"variants": [
{ "url": "...1920x1080...", "bitrate": 10368000, "contentType": "video/mp4" },
{ "url": "...1280x720...", "bitrate": 2176000, "contentType": "video/mp4" }
],
"tweetId": "2089425053599289546",
"tweetUrl": "https://x.com/NASA/status/2089425053599289546",
"author": { "userName": "NASA", "followers": 79000000 }
}

Column mapping, if you are switching

Hereigview
urldownloadUrl
previewUrlthumbnail
typemediaType
durationMsvideo.duration
variantsvideo.quality, one value there, the whole ladder here
tweetUrltweetUrl
texttext
author.userNameusername
author.namename

The difference that matters is variants: one encoding there, every encoding X offers here, best first, so archiving and previewing come from the same row.

Pricing: Pay Per Event (PPE)

EventPrice
Media returned$0.0004

That is $0.40 per 1,000 files, with no start fee and no charge for a run that returns nothing. The price steps down on its own for the larger Apify plans, to $0.36 and then $0.32 per 1,000.


What a run costs

An account's media tab, 600 files$0.24
A 5,000 file archive$2.00

Free tier

The Apify free plan includes $5 of usage every month, which is roughly 12,000 files here. No card required.

Advanced usage

Pick your own quality. url is the best encoding, and variants holds the rest. Archiving takes the first, a preview grid takes the last, and a bandwidth budget takes the one under your bitrate ceiling.

Build a training set with captions. altText plus previewUrl is an image and description pair, straight out of the export.

Rank creative by performance. Every row carries the parent post's likes and views, so sorting media by viewCount tells you which visual actually worked.

Four photos in one post stay four rows. A gallery is not collapsed to its first image, which is where several tools quietly lose three quarters of the files.

Links have a lifetime. X's file links are served from its own CDN and are stable for a long while, not forever. Download what you need close to the run rather than storing links for months.

A pasted post always answers. When X's usual route refuses a post, the run reads it through the same public endpoint that powers embedded posts on other websites. The files and their encodings come back identical; the parent post's view count is the only field that endpoint does not publish, and it stays at zero rather than being guessed.


Integrate X media into your stack

Python

from apify_client import ApifyClient
import requests
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("feedminer/x-media-scraper").call(run_input={
"handles": ["nasa"],
"mediaTypes": ["video"],
"maxItems": 25,
})
for row in client.dataset(run["defaultDatasetId"]).list_items().items:
with open(f"{row['id']}.mp4", "wb") as f:
f.write(requests.get(row["url"]).content)

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('feedminer/x-media-scraper').call({
handles: ['nasa'],
maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const light = items.map((m) => m.variants?.at(-1)?.url ?? m.url);

No code

Push rows to Google Sheets, Airtable, Slack, Zapier, Make or n8n from the Integrations tab of the run.


Performance

MeasureValue
Long lists of linksA hundred pasted post links cost barely more time than one
Depth626 files from one large account's media tab, measured
ParallelismUp to four accounts at a time
Partial resultsKept and delivered when a limit is reached

FAQ

Does it download the files? It returns direct links to them, which is what an integration, a downloader or a browser needs. Downloading is three lines in the Python example above.

Why does a video have several links? X encodes each video at several qualities. url is the best one, variants is the full list, best first.

Are the links permanent? They are served by X's CDN and stay valid for a long time, not forever. Fetch what you keep close to the run.

Do I get media from replies too? The media tab of an account is what X publishes as its media. Point startUrls at a specific post to get exactly that post's attachments.

Can I filter by date? Not in this actor. Use X Advanced Search Scraper with since and until to get the post URLs, then paste them here.

What about a protected account? Its media is not public, and the run reports it instead of failing.

Is alt text always there? Only when the author wrote one. The field is empty otherwise, never invented.

Can I get a thumbnail for a photo? previewUrl is the image itself for a photo, and the poster frame for a video.


Support

A file you cannot reach, a variant that looks wrong, a format you need: write from the actor page.


This actor reads media that X publishes publicly. It does not access private accounts or direct messages. Media is copyrighted by its authors: exporting links for analysis, monitoring or archiving is not a licence to republish. Personal data in the output is subject to GDPR, CCPA and similar regimes.


FeedMiner · Fast, honestly priced scrapers, kept alive when platforms change.

X Tweet Scraper · X Profile Scraper · X Follower Scraper · X Advanced Search Scraper · X Media Scraper · X Mentions Monitor · X Engagement Scraper · X Likes and Reposts Scraper · X Trends Scraper · All FeedMiner Actors