Twitter Media Scraper avatar

Twitter Media Scraper

Pricing

$0.10 / 1,000 media links

Go to Apify Store
Twitter Media Scraper

Twitter Media Scraper

Get public Twitter/X images, videos, and GIFs from tweet URLs, IDs, profiles, hashtags, or keywords. Export direct media links with post, author, profile, and engagement data.

Pricing

$0.10 / 1,000 media links

Rating

0.0

(0)

Developer

Maxime Dupré

Maxime Dupré

Maintained by Community

Actor stats

0

Bookmarked

78

Total users

12

Monthly active users

7 hours ago

Last modified

Share

Twitter Media Scraper helps researchers, marketers, and developers find public images, videos, and GIFs. Give it tweet URLs, IDs, profiles, hashtags, or keywords. It returns direct source-hosted media links with post, author, profile, and engagement data.

📊 Twitter/X media rows

Each dataset row is one image, video, or GIF. When the same source media appears through more than one target, the Actor saves the first eligible match and ignores later matches. The row shows the first target that found it. Data can include:

  • A direct media URL, thumbnail, media type, size, alt text, and source order
  • The source post URL, text, date, language, and public engagement counts
  • The author's username, name, profile URL, avatar, and verification status
  • Profile facts when media comes from a profile target
  • The direct media link and other video quality choices when the source provides them

🚀 Run the Twitter/X media scraper

  1. Choose one Target type.
  2. Fill in the matching tweet, profile, hashtag, or keyword field.
  3. Pick the media types you want.
  4. Set Media item limit, or leave it empty to get all available results until the source is exhausted.
  5. Start the Actor and open Media items in the dataset.

Use known tweet URLs and IDs to find media from those posts. Profiles, hashtags, and keyword searches find matching public media. The Actor returns source-hosted links. It does not download, change, or bundle the files.

⚙️ Input

Only fields for the chosen Target are used.

Input fields

FieldTypeWhat it does
targetstringChooses tweets, profiles, hashtags, or keyword search.
tweetTargetsstring[]Lists public tweet URLs or raw tweet IDs, one per line. Used for the Tweets target.
replyScopestringIncludes no replies, direct replies, or all reply levels for tweet targets.
profilesstring[]Lists public handles such as @NASA, one per line. Used for the Profiles target.
profileFiltersobjectHolds optional filters for posts found through profiles.
profileFilters.minLikesintegerKeeps posts with at least this many likes. Leave empty for no likes filter.
profileFilters.minRepostsintegerKeeps posts with at least this many reposts. Leave empty for no repost filter.
profileFilters.minRepliesintegerKeeps posts with at least this many replies. Leave empty for no reply filter.
profileFilters.authorVerificationstringKeeps any author, verified authors, or authors not marked as verified.
profileFilters.languagestringKeeps a language code such as en, es, or fr. Leave empty for any language.
hashtagsstring[]Lists hashtags such as #nasa, one per line. Used for the Hashtags target.
keywordsstring[]Lists search keywords or phrases, one search per line. Used for Keyword Search.
mediaTypesstring[]Chooses images, videos, GIFs, or any mix of them.
maxItemsintegerStops after returning this many media items across the run. Leave empty to get all available results until the source is exhausted.

Example input

This example is from a successful run with the prefilled tweet:

{
"target": "tweets",
"tweetTargets": [
"https://x.com/teslaownersSV/status/2004891561180758165"
],
"replyScope": "none",
"mediaTypes": [
"image",
"video",
"gif"
],
"maxItems": 5
}

🧾 Output

The output includes a link to the default dataset. Each saved media row uses the same shape. Fields may be null when Twitter/X does not give the value.

Output fields

FieldTypeWhat it does
resultsstringLinks to the saved media rows in the default dataset.
mediaUrlstringMain direct source-hosted image, video, or GIF URL.
mediaTypestringSays whether the row is an image, video, or GIF.
mediaIndexintegerGives the media position in the post, starting at 1.
attachmentCountinteger or nullGives the number of media attachments in the source post.
mediaIdstring or nullGives the Twitter/X media ID when available.
thumbnailUrlstring or nullGives a source-hosted preview image URL.
altTextstring or nullGives accessibility text added to the media.
widthinteger or nullGives media width in pixels.
heightinteger or nullGives media height in pixels.
durationMsinteger or nullGives video or GIF length in milliseconds.
aspectRatiostring or nullGives a video or GIF aspect ratio.
contentTypestring or nullGives the media MIME type.
videoVariantsobject[]Lists other source-hosted video choices from highest to lowest bitrate.
videoVariants.urlstringGives the direct URL for a video choice.
videoVariants.bitrateinteger or nullGives bitrate in bits per second.
videoVariants.widthinteger or nullGives variant width in pixels.
videoVariants.heightinteger or nullGives variant height in pixels.
videoVariants.contentTypestring or nullGives the variant MIME type.
targetstringGives the submitted value that first found the media.
targetTypestringGives the target kind that first found the media.
tweetIdstringGives the source post ID.
tweetUrlstringGives the public source post URL.
conversationIdstring or nullGives the Twitter/X conversation ID.
textstring or nullGives readable source post text while preserving its meaning and entity-like literals, or null when unavailable.
postedAtstring or nullGives the date and time the post was published.
languagestring or nullGives Twitter/X's language code for the post.
postingSourcestring or nullGives the app or service used to publish the post.
authorUsernamestring or nullGives the author's username without @.
authorNamestring or nullGives the author's display name.
authorProfileUrlstring or nullGives the author's public profile URL.
authorAvatarUrlstring or nullGives the author's public avatar URL.
authorVerifiedboolean or nullSays whether Twitter/X marks the author as verified.
engagementobjectHolds public counts for the source post.
engagement.likesinteger or nullGives the number of likes.
engagement.retweetsinteger or nullGives the number of reposts.
engagement.repliesinteger or nullGives the number of replies.
engagement.viewsinteger or nullGives the number of views.
engagement.quotesinteger or nullGives the number of quote posts.
engagement.bookmarksinteger or nullGives the number of bookmarks.
profileobject or nullHolds more profile facts for media found through a profile target.
profile.biostring or nullGives the public profile bio.
profile.locationstring or nullGives the public profile location.
profile.websiteUrlstring or nullGives the public website linked from the profile.
profile.followersinteger or nullGives the follower count.
profile.followinginteger or nullGives the number of followed accounts.
profile.createdAtstring or nullGives the account creation date and time.
profile.postsinteger or nullGives the total profile post count.
profile.mediainteger or nullGives the total profile media post count.

This complete row is from a successful current-beta run for a public tweet:

{
"target": "2099538990923751694",
"targetType": "tweet",
"tweetId": "2099538990923751694",
"tweetUrl": "https://x.com/willianmatiola/status/2099538990923751694",
"authorUsername": "willianmatiola",
"authorName": "Willian Matiola",
"text": "Ainda não superei esse & que o @ptrzzni e eu desenhamos juntos. Construindo cada próxima versão em cima da ideia um do outro. Vale o ♥️ ou não? https://t.co/HdOQspvfDA",
"postedAt": "2026-09-14T16:41:17.000Z",
"mediaType": "video",
"mediaUrl": "https://video.twimg.com/amplify_video/2099538724182695936/vid/avc1/1080x1350/xVBQ8oUKe-xSPINl.mp4?tag=29",
"mediaIndex": 1,
"attachmentCount": 1,
"thumbnailUrl": "https://pbs.twimg.com/amplify_video_thumb/2099538724182695936/img/ooEUaLW3NVsINh2X.jpg",
"altText": null,
"engagement": {
"likes": 68,
"retweets": 0,
"replies": 6,
"views": 1360,
"quotes": 0,
"bookmarks": 3
},
"conversationId": "2099538990923751694",
"authorProfileUrl": "https://x.com/willianmatiola",
"authorAvatarUrl": "https://pbs.twimg.com/profile_images/1905566107437551616/X7zkWb04_normal.jpg",
"authorVerified": false,
"language": null,
"postingSource": null,
"mediaId": "2099538724182695936",
"width": 1080,
"height": 1350,
"durationMs": 5033,
"aspectRatio": null,
"contentType": "video/mp4",
"videoVariants": [
{
"url": "https://video.twimg.com/amplify_video/2099538724182695936/vid/avc1/1080x1350/xVBQ8oUKe-xSPINl.mp4?tag=29",
"bitrate": 10368000,
"contentType": "video/mp4",
"width": 1080,
"height": 1350
},
{
"url": "https://video.twimg.com/amplify_video/2099538724182695936/vid/avc1/720x900/iVN0XJgpxgcKCpoB.mp4?tag=29",
"bitrate": 2176000,
"contentType": "video/mp4",
"width": 720,
"height": 900
},
{
"url": "https://video.twimg.com/amplify_video/2099538724182695936/vid/avc1/480x600/Hjzpil7HVo9RTMWz.mp4?tag=29",
"bitrate": 950000,
"contentType": "video/mp4",
"width": 480,
"height": 600
},
{
"url": "https://video.twimg.com/amplify_video/2099538724182695936/vid/avc1/320x400/BVWkMMjyOaU9_jhq.mp4?tag=29",
"bitrate": 632000,
"contentType": "video/mp4",
"width": 320,
"height": 400
}
],
"profile": null
}

💳 Pricing

Charges

This Actor uses pay-per-event pricing. You are charged for each direct link for a public image, video, or GIF that is saved to the dataset.

Use Media item limit to control the run size. Leaving it empty gets all available results until the source is exhausted.

🔌 Integrations

Ways to connect

Use the Apify API, schedules, webhooks, Make, Zapier, Google Sheets, or cloud storage. You can also export the dataset as JSON, CSV, Excel, XML, RSS, or HTML.

❓ FAQ

Can it get private or deleted media?

No. It only works with public Twitter/X content. It does not get private, protected, deleted, or age-gated content.

Do I need a Twitter/X login or API key?

No. You do not need to give the Actor a Twitter/X login, cookies, or API key.

Can I mix tweets, profiles, hashtags, and keywords in one run?

No. Choose one Target type for each run and fill in its matching field. Use another run for a different target type.

Can I choose images, videos, or GIFs?

Yes. Pick one type or any mix in Media types.

Can it include media from replies?

Yes, for tweet targets. Choose direct replies or all reply levels in Replies.

Does it save the media files?

No. It returns direct source-hosted links and data. It does not copy or change the files.

What happens when two targets find the same media?

The first eligible match is saved right away. Later matches for that same source media are ignored, so the saved row keeps only the first target that found it.

Is this a general Twitter scraper?

No. It saves rows only for the media types you ask for. It is not meant to collect posts with no requested media.

📝 Changelog

v0.3 (15-09-2026)

  • Added attachment counts and richer details to saved media rows.
  • Improved recovery from temporary Twitter/X access problems and clear reporting when a target still cannot be read.

v0.2

  • Added media discovery from public profiles, hashtags, and keywords, with richer author, engagement, and video data.

v0.1

  • Initial release.

🆘 Support

For issues, questions, or feature requests, file a ticket and I'll fix or implement it in less than 24h 🫡

Made with ❤️ by Maxime Dupré