Twitter Video Downloader
Pricing
from $5.00 / 1,000 results
Twitter Video Downloader
Download videos from Twitter/X posts. Supports direct post URLs and username-based scraping.
Pricing
from $5.00 / 1,000 results
Rating
5.0
(5)
Developer

Crawler Bros
Actor stats
2
Bookmarked
13
Total users
5
Monthly active users
8 days ago
Last modified
Categories
Share
Twitter/X Media Downloader
Download videos, photos, and GIFs from any public Twitter/X post — no login required.
Just paste the post link, hit run, and get your files with direct download links. You can also provide a username to automatically collect media from their recent posts.
What Can This Tool Do?
- Download videos from any public tweet (including quoted tweets and CMAF/fMP4 streaming videos)
- Download photos in original quality (PNG/JPG)
- Download GIFs as MP4 files
- Scrape by username — automatically find and download media from a user's recent posts
- Detect retweets and quote tweets — correctly attributes media to the original author
- Get media details — resolution, duration, file size, codec, bitrate, and more
- Cloud storage — every file is uploaded and given a direct download link that works anywhere
Getting Started
Option 1: Download from specific posts
Paste one or more tweet/post URLs:
{"postUrls": ["https://x.com/NASA/status/123456789","https://x.com/SpaceX/status/987654321"]}
Option 2: Download from a user's profile
Provide usernames (without the @) and the tool will find their recent posts with media:
{"usernames": ["NASA", "SpaceX"],"maxPostsPerUser": 10}
You can also combine both — provide post URLs and usernames at the same time.
Input Settings
| Setting | What it does | Default |
|---|---|---|
| Post URLs | List of tweet URLs to download media from | Empty |
| Usernames | List of Twitter usernames to scrape (without @) | Empty |
| Max Posts Per User | How many recent posts to check per username (1-100) | 10 |
| Browser Cookies | Your Twitter cookies in JSON format (optional — built-in defaults are used if not provided) | Not set |
What You Get Back
Every downloaded file produces a result like this:
Video example
{"tweet_url": "https://x.com/username/status/123456789","tweet_id": "123456789","scraped_from": "username","uploader": "username","is_retweet": false,"is_quote_tweet": false,"media_origin": "self","media_type": "video","filename": "123456789_video_1.mp4","filesize_bytes": 28640256,"storage_key": "123456789_video_1.mp4","download_url": "https://api.apify.com/v2/key-value-stores/{STORE_ID}/records/123456789_video_1.mp4","download_status": "finished","downloaded_at": "2026-03-06T08:15:44.708281","media_stats": {"width": 1920,"height": 1080,"fps": 30.0,"duration": 45.23,"video_codec": "h264","ext": "mp4","filesize_bytes": 28640256,"total_bitrate_kbps": 5064.12,"aspect_ratio": 1.78}}
Photo example
{"tweet_url": "https://x.com/username/status/123456789","tweet_id": "123456789","scraped_from": null,"uploader": "username","is_retweet": false,"is_quote_tweet": false,"media_origin": "self","media_type": "photo","filename": "123456789_photo_1.png","filesize_bytes": 29387,"storage_key": "123456789_photo_1.png","download_url": "https://api.apify.com/v2/key-value-stores/{STORE_ID}/records/123456789_photo_1.png","download_status": "finished","downloaded_at": "2026-03-06T08:15:44.708281","media_stats": {"width": 1200,"height": 675,"ext": "png","filesize_bytes": 29387,"aspect_ratio": 1.78}}
Output fields explained
| Field | What it means |
|---|---|
tweet_url | The original post URL you provided |
tweet_id | The numeric ID of the tweet |
scraped_from | The username whose profile this post was scraped from (null if provided as a direct URL) |
uploader | The Twitter username of whoever posted the media |
is_retweet | true if the post is a retweet of someone else's content |
is_quote_tweet | true if the post quotes another tweet |
media_origin | "self" = media from the tweet itself, "quoted" = media from a quoted tweet inside it |
media_type | "video", "photo", or "gif" |
filename | Name of the saved file |
filesize_bytes | File size in bytes |
storage_key | The key used to store the file (use this for API access) |
download_url | A direct link to download the file — works in any browser |
download_status | "finished" if successful, "failed" if something went wrong |
error | Error message (only present when download_status is "failed") |
downloaded_at | When the file was downloaded (ISO 8601 timestamp) |
media_stats | Technical details about the file (resolution, duration, codec, etc.) |
How It Works
The actor uses a multi-strategy approach to reliably fetch media from Twitter/X:
- Syndication API — First attempts to get media URLs via Twitter's public syndication endpoint (fastest, most reliable for direct video URLs)
- HLS/m3u8 interception — For videos delivered as CMAF/fMP4 streams (common for high-profile accounts), intercepts m3u8 manifests via Playwright network monitoring, selects the highest-quality variant, and downloads all segments (init + media segments) into a single file
- DOM scraping — Always extracts tweet metadata (retweet/quote detection, author attribution, image URLs) from the page DOM
- Network interception — Falls back to intercepting direct mp4 URLs from browser network traffic when other methods don't apply
For username-based scraping, the actor:
- Opens the user's profile with authenticated cookies
- Automatically switches to the "Posts" tab (avoiding Twitter's "Highlights" tab which shows old popular tweets)
- Scrolls and collects post URLs with deduplication
- Sorts results by tweet ID (newest first) as a safety net
Using the API
You can run this tool programmatically from your own code using the Apify API.
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_API_TOKEN")# Start a runrun = client.actor("YOUR_ACTOR_ID").call(run_input={"postUrls": ["https://x.com/NASA/status/123456789"]})# Get resultsitems = client.dataset(run["defaultDatasetId"]).list_items().itemsfor item in items:print(f"File: {item['filename']}")print(f"Download: {item['download_url']}")print(f"Type: {item['media_type']}")print()
JavaScript / Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });const run = await client.actor('YOUR_ACTOR_ID').call({postUrls: ['https://x.com/NASA/status/123456789'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const item of items) {console.log(`File: ${item.filename}`);console.log(`Download: ${item.download_url}`);}
cURL — download a file directly
Once you have the download_url from the results, you can download the file with any tool:
$curl -o video.mp4 "https://api.apify.com/v2/key-value-stores/{STORE_ID}/records/{STORAGE_KEY}"
Where do I find my API token?
Go to Apify Console > Settings > Integrations and copy your Personal API Token.
Frequently Asked Questions
Do I need a Twitter account to use this?
No. The tool downloads media from public tweets without requiring you to log in. The actor includes built-in cookies for profile scraping. You only need to provide your own cookies if the built-in ones expire or you hit rate limits.
What media types are supported?
Videos, photos, and GIFs. The tool detects the type automatically and downloads everything it finds in a tweet. If a tweet has 3 photos and 1 video, you'll get all 4 files.
What about streaming videos (CMAF/fMP4)?
Twitter delivers some videos as fragmented MP4 streams (CMAF) using HLS m3u8 playlists instead of single MP4 files. The actor automatically detects this and downloads all segments (init + media), concatenating them into a single playable MP4 file.
What does "media_origin" mean?
"self"— The media belongs to the tweet you linked. This is the most common case."quoted"— The tweet you linked quotes another tweet, and this media comes from that quoted tweet.
For example, if User A quotes User B's video tweet, you'll get User B's video with media_origin: "quoted" and uploader: "UserB".
What does "scraped_from" mean?
When you use username-based scraping, scraped_from tells you which profile the post was found on. This is useful when the post is a retweet — uploader shows the original author, while scraped_from shows whose profile it appeared on. For direct post URLs, this field is null.
Does it download the best quality available?
Yes. Videos are downloaded at the highest quality Twitter provides. For HLS streams, the actor selects the highest-resolution variant. Photos are fetched at original resolution (?name=orig).
Can I download from private/protected accounts?
Not directly. The tool only works with public tweets. If you provide authenticated browser cookies, you may be able to access some restricted content, but this depends on Twitter's access controls.
How do I download media from a user's profile?
Use the Usernames input field. Enter one or more usernames (without the @), and the tool will open their profile, scroll through their recent posts, and collect all the media it finds. You can control how many posts to check with the Max Posts Per User setting (up to 100).
Why does username scraping need cookies?
When scraping by username, the tool needs to browse the profile page like a real user. Twitter may block or rate-limit unauthenticated browsing. The tool includes built-in default cookies, but for heavy usage or if the defaults expire, provide your own.
How do I get my browser cookies?
- Log into Twitter/X in your browser
- Open DevTools (F12) > Application > Cookies >
https://x.com - Use a browser extension like "Cookie-Editor" to export cookies as JSON
- Paste the JSON string into the Browser Cookies input field
What if a download fails?
Failed downloads still appear in the results with download_status: "failed" and an error field explaining what went wrong. Common reasons:
- The tweet was deleted or made private
- Twitter temporarily rate-limited the request
- The media URL expired before the download completed
Is there a limit on how many posts I can process?
There's no hard limit from the tool side. However, Twitter has its own rate limits. For best results:
- Use cookies for runs with more than ~50 posts
- The tool automatically adds delays between requests to avoid blocks
How long are the download links valid?
The download_url links point to Apify's Key-Value Store and remain available as long as your Apify storage retention allows (depends on your plan — typically 7 days on the free tier, longer on paid plans).
Can I use this tool on a schedule?
Yes. On the Apify platform, you can set up scheduled runs to automatically download new media from specific users at regular intervals (daily, hourly, etc.). Go to your Actor's page and click "Schedules" to set it up.
What's the media_stats field?
It contains technical metadata extracted from the downloaded file:
| Stat | Applies to | Example |
|---|---|---|
width / height | All media | 1920 / 1080 |
fps | Videos, GIFs | 30.0 |
duration | Videos, GIFs | 45.23 (seconds) |
video_codec | Videos, GIFs | "h264" |
ext | All media | "mp4", "png", "jpg" |
filesize_bytes | All media | 28640256 |
total_bitrate_kbps | Videos | 5064.12 |
aspect_ratio | All media | 1.78 |
Local Development
Prerequisites
- Python 3.12+
- Apify CLI (
npm install -g apify-cli)
Setup
- Navigate to the project:
$cd Twitter/twitter-video-downloader-cli
- Install dependencies:
pip install -r requirements.txtplaywright install chromium firefox
- Create input file
storage/key_value_stores/default/INPUT.json:
{"postUrls": ["https://x.com/username/status/123456"]}
- Run locally:
$apify run
Local output
Results are saved in ./storage/:
datasets/default/— JSON files with metadata and download linkskey_value_stores/default/— The actual downloaded media files
Deployment
Deploy to Apify Platform
apify loginapify push
Deploy from GitHub
- Go to Apify Console
- Click "Link Git Repository"
- Connect your GitHub repository
- Set Dockerfile path to
./Dockerfile
Support
License
This project is for educational and personal use. Respect Twitter's Terms of Service. Always ensure you have the rights to download and use the content.
Built with love by Crawler Bros