🎬 Download Videos from X (Twitter) — Free, No App, No Watermark
Download any video from X/Twitter straight to your device with one script — free, no sign-up, no third-party app, no watermark. Picks the highest quality automatically.
Works on: 🌐 Browser Console · 💻 CLI · 🤖 MCP (AI Agents)
Difficulty: 🟢 Beginner
Time: ⏱️ Under 1 minute
Requirements: A web browser (Chrome, Firefox, Edge, Safari)
📖 For the quick-reference version, see video-downloader.md
🎯 Real-World Scenario
You're scrolling X and hit a viral 2-minute clip — a perfect demo of a coding technique, a hilarious skit, a breaking news moment. You want to save it locally so you can share it on Slack, embed it in a presentation, or just watch it offline. But X doesn't have a "Download" button. Third-party sites want you to paste URLs, watch ads, and download mystery files with watermarks.
XActions finds every quality variant of the video embedded in the tweet's page data, picks the highest resolution, and downloads it as a clean MP4 — directly from Twitter's own CDN. No middleman, no watermark, no ads.
Before XActions:
┌─────────────────────────────────────────────────────┐
│ You: "How do I download this video?" │
├─────────────────────────────────────────────────────┤
│ Option 1: Third-party sites │
│ → Paste URL → Wait → Ads → Captcha → Low quality│
│ → Watermark: "Downloaded with SketchySite.io" │
│ │
│ Option 2: Screen recording │
│ → Lose quality → Capture UI → Bad audio sync │
│ │
│ Option 3: Twitter API ($100/month) │
│ → Apply for developer access → Wait 3 days │
│ → Parse JSON responses → Still no direct link │
└─────────────────────────────────────────────────────┘
After XActions:
┌─────────────────────────────────────────────────────┐
│ You: F12 → Paste → Enter │
├─────────────────────────────────────────────────────┤
│ 📹 FOUND 3 VIDEO(S) │
│ │
│ 1. [1280x720] MP4 ← highest │
│ 2. [640x360] MP4 │
│ 3. [480x270] MP4 │
│ │
│ ✅ Download started! → nichxbt_1893847265.mp4 │
│ 📋 URL copied to clipboard │
│ ⏱️ Total time: 3 seconds │
│ 💰 Cost: $0 │
└─────────────────────────────────────────────────────┘
📋 What This Does (Step by Step)
- 🔍 Reads the tweet page's DOM and source data — scans
<video>elements, page HTML, and framework data - 🎣 Extracts all
video.twimg.comURLs — finds every quality variant Twitter has for this video - 📡 Sets up a network interceptor — catches any video URLs loaded via
fetchorXMLHttpRequest(for second-run reliability) - 📊 Sorts by resolution — parses
1280x720,640x360, etc. from the URL paths - 🏆 Picks the best quality — auto-selects the highest resolution MP4
- ⬇️ Downloads the file — fetches the blob and triggers a browser download (or opens in new tab if CORS-blocked)
- 📋 Copies URL to clipboard — so you can paste it elsewhere
┌───────────────────────────────────────────────────────────────┐
│ │
│ [Open tweet with video] │
│ │ │
│ ▼ │
│ [Click PLAY on the video] │
│ │ │
│ ▼ │
│ [Paste script in console] │
│ │ │
│ ▼ │
│ [Scan page for video.twimg.com URLs] │
│ │ │
│ ┌─────┼─────────┬──────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ <video> Page Network Framework │
│ elements source intercepts data │
│ │ │ │ │ │
│ └─────┴─────────┴──────────────┘ │
│ │ │
│ ▼ │
│ [Deduplicate & sort by resolution] │
│ │ │
│ ▼ │
│ [Display all qualities + auto-download best] │
│ │ │
│ ▼ │
│ [Copy URL to clipboard + save as filename.mp4] │
│ │
└───────────────────────────────────────────────────────────────┘
🌐 Method 1: Browser Console (Copy-Paste)
Best for: Everyone — no installs, no sign-ups, works in 3 seconds.
Prerequisites
- Any modern web browser (Chrome, Firefox, Edge, Safari)
- The tweet must contain a native X/Twitter video (not a YouTube embed)
Step 1: Open the tweet with the video
Go to the tweet containing the video you want to download. The URL should look like
x.com/username/status/1234567890
┌──────────────────────────────────────────────────────────┐
│ 🔍 x.com/nichxbt/status/1893847265192837465 │
├──────────────────────────────────────────────────────────┤
│ │
│ 👤 nich @nichxbt · 2h │
│ Just shipped the new XActions dashboard 🚀 │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ │ │
│ │ ▶ [VIDEO — 1:42] │ │
│ │ Click play first! │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ♡ 247 🔁 89 💬 34 │
│ │
└──────────────────────────────────────────────────────────┘
Step 2: Click PLAY on the video
Important: The video must start playing at least once — this loads the video URL into the page data so the script can find it.
Step 3: Open Developer Console
| OS | Shortcut |
|---|---|
| Windows / Linux | F12 then Console tab, or Ctrl + Shift + J |
| Mac | Cmd + Option + J |
Step 4: Paste and Run
// ============================================
// XActions - X/Twitter Video Downloader
// by nichxbt — https://xactions.app
// Go to a tweet with a video, click play, then paste this
// ============================================
(() => {
const CONFIG = {
QUALITY: 'highest', // 'highest', 'lowest', 'all'
AUTO_DOWNLOAD: true, // Auto-download best quality
SHOW_ALL_QUALITIES: true // Show all available qualities
};
const getTweetId = () => {
const match = window.location.href.match(/status\/(\d+)/);
return match ? match[1] : null;
};
const getAuthor = () => {
const match = window.location.href.match(/x\.com\/(\w+)/);
return match ? match[1] : 'unknown';
};
// Method 1: Direct <video> elements
const findVideoInReactState = () => {
const videos = [];
document.querySelectorAll('video').forEach(video => {
if (video.src && !video.src.startsWith('blob:')) {
videos.push({ url: video.src, quality: 'direct', type: 'mp4' });
}
video.querySelectorAll('source').forEach(source => {
if (source.src && !source.src.startsWith('blob:')) {
videos.push({ url: source.src, quality: 'source', type: 'mp4' });
}
});
});
return videos;
};
// Method 2: Scan page HTML for video.twimg.com URLs
const findVideoInPageData = () => {
const videos = [];
const pageContent = document.documentElement.innerHTML;
const patterns = [
/https:\/\/video\.twimg\.com\/[^"'\s]+\.mp4[^"'\s]*/g,
/https:\/\/video\.twimg\.com\/[^"'\s]+\.m3u8[^"'\s]*/g,
/https:\/\/pbs\.twimg\.com\/[^"'\s]+\.mp4[^"'\s]*/g,
/https:\/\/[^"'\s]*\/amplify_video[^"'\s]*\.mp4[^"'\s]*/g,
/https:\/\/[^"'\s]*\/ext_tw_video[^"'\s]*\.mp4[^"'\s]*/g,
];
patterns.forEach(pattern => {
const matches = pageContent.match(pattern) || [];
matches.forEach(url => {
let cleanUrl = url.replace(/\\u002F/g, '/').replace(/\\/g, '');
cleanUrl = cleanUrl.split('"')[0].split("'")[0].split(' ')[0];
if (cleanUrl.includes('.mp4')) {
const qualityMatch = cleanUrl.match(/\/(\d+x\d+)\//);
const quality = qualityMatch ? qualityMatch[1] : 'unknown';
videos.push({ url: cleanUrl, quality, type: 'mp4' });
}
});
});
// Deduplicate
const unique = [];
const seen = new Set();
videos.forEach(v => {
const key = v.url.split('?')[0];
if (!seen.has(key)) { seen.add(key); unique.push(v); }
});
return unique;
};
// Method 3: Network intercepts
const findVideoInNetwork = () => window.__XACTIONS_VIDEO_URLS || [];
// Method 4: Framework data
const findVideoInNextData = () => {
const videos = [];
if (window.__NEXT_DATA__) {
const dataStr = JSON.stringify(window.__NEXT_DATA__);
const mp4Matches = dataStr.match(/https:[^"]*\.mp4[^"]*/g) || [];
mp4Matches.forEach(url => {
videos.push({ url: url.replace(/\\/g, ''), quality: 'next_data', type: 'mp4' });
});
}
return videos;
};
// Set up network interceptor for future runs
const setupInterceptor = () => {
if (window.__XACTIONS_INTERCEPTOR_ACTIVE) return;
window.__XACTIONS_VIDEO_URLS = window.__XACTIONS_VIDEO_URLS || [];
const origFetch = window.fetch;
window.fetch = async (...args) => {
const url = args[0]?.toString?.() || args[0]?.url || args[0];
if (url && (url.includes('.mp4') || url.includes('.m3u8') || url.includes('video.twimg'))) {
window.__XACTIONS_VIDEO_URLS.push({ url, quality: 'intercepted', type: url.includes('.m3u8') ? 'm3u8' : 'mp4', time: Date.now() });
}
return origFetch(...args);
};
const origXHR = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function(method, url, ...rest) {
if (url && (url.includes('.mp4') || url.includes('.m3u8') || url.includes('video.twimg'))) {
window.__XACTIONS_VIDEO_URLS.push({ url, quality: 'xhr', type: url.includes('.m3u8') ? 'm3u8' : 'mp4', time: Date.now() });
}
return origXHR.call(this, method, url, ...rest);
};
window.__XACTIONS_INTERCEPTOR_ACTIVE = true;
};
const sortByQuality = (videos) =>
videos.sort((a, b) => {
const getRes = (v) => { const m = v.quality?.match(/(\d+)x(\d+)/); return m ? parseInt(m[1]) * parseInt(m[2]) : 0; };
return getRes(b) - getRes(a);
});
const downloadVideo = async (url, filename) => {
try {
const response = await fetch(url, { mode: 'cors' });
if (response.ok) {
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
console.log('✅ Download started!');
return true;
}
} catch (e) {
console.log('⚠️ CORS blocked — opening in new tab...');
}
window.open(url, '_blank');
console.log('📺 Opened in new tab — right-click → Save Video As');
return true;
};
// Main
const run = async () => {
console.clear();
console.log('🎬 XActions — X/Twitter Video Downloader');
console.log('════════════════════════════════════════════════');
const tweetId = getTweetId();
const author = getAuthor();
if (!tweetId) {
console.error('❌ Navigate to a tweet page first!');
console.log(' Example: x.com/user/status/123456789');
return;
}
console.log(`📍 Tweet: @${author}/status/${tweetId}`);
console.log('');
setupInterceptor();
console.log('🔍 Searching for video URLs...');
let allVideos = [];
const reactVids = findVideoInReactState();
if (reactVids.length) { console.log(` ✓ ${reactVids.length} in video elements`); allVideos.push(...reactVids); }
const pageVids = findVideoInPageData();
if (pageVids.length) { console.log(` ✓ ${pageVids.length} in page data`); allVideos.push(...pageVids); }
const netVids = findVideoInNetwork();
if (netVids.length) { console.log(` ✓ ${netVids.length} from network`); allVideos.push(...netVids); }
const nextVids = findVideoInNextData();
if (nextVids.length) { console.log(` ✓ ${nextVids.length} in framework data`); allVideos.push(...nextVids); }
// Deduplicate
const seen = new Set();
allVideos = allVideos.filter(v => { const k = v.url.split('?')[0]; if (seen.has(k)) return false; seen.add(k); return true; });
allVideos = sortByQuality(allVideos);
if (allVideos.length === 0) {
console.log('');
console.log('❌ No video URLs found.');
console.log('');
console.log('💡 Fix:');
console.log(' 1. Make sure the tweet has a native video (not YouTube)');
console.log(' 2. Click PLAY on the video');
console.log(' 3. Run this script again — the interceptor is now active');
return;
}
console.log('');
console.log('════════════════════════════════════════════════');
console.log(`📹 FOUND ${allVideos.length} VIDEO(S)`);
console.log('════════════════════════════════════════════════');
if (CONFIG.SHOW_ALL_QUALITIES) {
console.log('');
allVideos.forEach((v, i) => {
console.log(`${i + 1}. [${v.quality}] ${v.type.toUpperCase()} ${v.url.slice(0, 60)}...`);
});
}
const best = allVideos[0];
console.log('');
console.log('🏆 BEST QUALITY:');
console.log(` Resolution: ${best.quality}`);
console.log(` Type: ${best.type.toUpperCase()}`);
try { await navigator.clipboard.writeText(best.url); console.log(' 📋 URL copied to clipboard!'); } catch {}
window.xVideo = { best: best.url, all: allVideos, author, tweetId, tweetUrl: window.location.href };
console.log('');
console.log('🔗 Direct URL:');
console.log(best.url);
if (CONFIG.AUTO_DOWNLOAD) {
console.log('');
await downloadVideo(best.url, `${author}_${tweetId}.mp4`);
}
console.log('');
console.log('════════════════════════════════════════════════');
console.log('💾 All data: window.xVideo');
console.log(' window.xVideo.best → Best quality URL');
console.log(' window.xVideo.all → All qualities array');
console.log('════════════════════════════════════════════════');
};
run();
})();
✅ Expected Console Output
🎬 XActions — X/Twitter Video Downloader
════════════════════════════════════════════════
📍 Tweet: @nichxbt/status/1893847265192837465
🔍 Searching for video URLs...
✓ 1 in video elements
✓ 3 in page data
════════════════════════════════════════════════
📹 FOUND 3 VIDEO(S)
════════════════════════════════════════════════
1. [1280x720] MP4 https://video.twimg.com/ext_tw_video/189384...
2. [640x360] MP4 https://video.twimg.com/ext_tw_video/189384...
3. [480x270] MP4 https://video.twimg.com/ext_tw_video/189384...
🏆 BEST QUALITY:
Resolution: 1280x720
Type: MP4
📋 URL copied to clipboard!
🔗 Direct URL:
https://video.twimg.com/ext_tw_video/1893847265/pu/vid/1280x720/abcDEF123.mp4?tag=14
✅ Download started!
════════════════════════════════════════════════
💾 All data: window.xVideo
window.xVideo.best → Best quality URL
window.xVideo.all → All qualities array
════════════════════════════════════════════════
The file nichxbt_1893847265192837465.mp4 drops into your downloads folder.
💻 Method 2: CLI (Command Line)
# Install XActions globally
npm install -g xactions
# Download a video by tweet URL
npx xactions download-video https://x.com/nichxbt/status/1893847265192837465
# Specify output path and quality
npx xactions download-video https://x.com/nichxbt/status/1893847265192837465 \
--quality highest \
--output ./videos/my-video.mp4
Example with all options:
npx xactions download-video "https://x.com/nichxbt/status/1893847265192837465" \
--quality highest \
--output ./downloads/ \
--filename "demo-video.mp4" \
--show-all-qualities \
--copy-url
✅ CLI Output Preview
⚡ XActions v2.4.0
🎬 VIDEO DOWNLOADER
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📍 Tweet: @nichxbt/status/1893847265192837465
🔍 Finding video URLs...
📹 Available qualities:
1. 1280x720 MP4 2.4 MB
2. 640x360 MP4 1.1 MB
3. 480x270 MP4 0.6 MB
⬇️ Downloading best quality (1280x720)...
████████████████████████████ 100% | 2.4 MB
✅ Saved → ./downloads/demo-video.mp4
📋 URL copied to clipboard
CLI Configuration Table
| Flag | Type | Default | Description |
|---|---|---|---|
--quality |
string | highest |
highest, lowest, or all |
--output |
string | ./ |
Output directory or file path |
--filename |
string | auto | Custom filename (author_tweetId.mp4 by default) |
--show-all-qualities |
boolean | false |
Display all available resolutions |
--copy-url |
boolean | false |
Copy direct video URL to clipboard |
--json |
boolean | false |
Output video info as JSON |
🤖 Method 3: MCP Server (AI Agents)
Use with Claude Desktop, GPT, Cursor, or any MCP-compatible AI agent.
Setup
{
"mcpServers": {
"xactions": {
"command": "npx",
"args": ["-y", "xactions", "mcp"]
}
}
}
MCP Tool Call
{
"tool": "x_download_video",
"arguments": {
"tweet_url": "https://x.com/nichxbt/status/1893847265192837465",
"quality": "highest"
}
}
Claude Desktop example prompt:
"Download the video from this tweet: x.com/nichxbt/status/1893847265192837465 and save it in the highest quality."
Expected MCP response:
{
"status": "complete",
"tweet": "@nichxbt/status/1893847265192837465",
"qualities_found": 3,
"best_quality": "1280x720",
"file_size": "2.4 MB",
"download_url": "https://video.twimg.com/ext_tw_video/...",
"saved_to": "nichxbt_1893847265192837465.mp4"
}
📊 Method Comparison
| Feature | 🌐 Browser Console | 💻 CLI | 🤖 MCP |
|---|---|---|---|
| Setup | None | npm install |
Config JSON |
| Speed | Instant | Instant | Via AI agent |
| Best for | Grab one video fast | Batch downloads | AI workflows |
| Show all qualities | ✅ | ✅ | ✅ |
| Auto-download | ✅ | ✅ | ✅ |
| Clipboard copy | ✅ | ✅ | ❌ |
| Custom filename | ❌ | ✅ | ✅ |
| Batch mode | ❌ | ✅ | ✅ |
| Works on GIFs | ✅ (they're videos) | ✅ | ✅ |
⚙️ Configuration Reference
| Option | Type | Default | Description |
|---|---|---|---|
QUALITY |
string | 'highest' |
Which quality to auto-download: 'highest', 'lowest', 'all' |
AUTO_DOWNLOAD |
boolean | true |
Automatically trigger download of the best quality |
SHOW_ALL_QUALITIES |
boolean | true |
List all available resolutions in console |
📊 Sample Output / Results
window.xVideo object (available after running the script):
// Access in console after running the script
window.xVideo
// =>
{
best: "https://video.twimg.com/ext_tw_video/189384.../pu/vid/1280x720/abcDEF.mp4?tag=14",
all: [
{ url: "https://video.twimg.com/.../1280x720/...", quality: "1280x720", type: "mp4" },
{ url: "https://video.twimg.com/.../640x360/...", quality: "640x360", type: "mp4" },
{ url: "https://video.twimg.com/.../480x270/...", quality: "480x270", type: "mp4" }
],
author: "nichxbt",
tweetId: "1893847265192837465",
tweetUrl: "https://x.com/nichxbt/status/1893847265192837465"
}
Downloaded file:
nichxbt_1893847265192837465.mp4
├── Format: MP4 (H.264 + AAC)
├── Resolution: 1280×720
├── Duration: 1:42
└── Size: 2.4 MB
💡 Pro Tips
Click PLAY first, then paste — X lazy-loads video URLs. If the video hasn't played yet, the URL might not be in the page data. Just tap play (even for one second), then paste the script. On second run, the network interceptor catches URLs automatically.
GIFs are actually videos — X stores animated GIFs as MP4 files internally. This script downloads them as MP4, which is smaller and higher quality than the original GIF format.
Use
window.xVideo.allto grab a specific quality — After running the script, open the console and typewindow.xVideo.allto see every available variant. You can manually open any URL in a new tab and right-click → Save.Bookmark the script — Create a browser bookmark with
javascript:followed by the minified script for one-click video downloading on any tweet page (search "bookmarklet" for how-to on your browser).For CORS-blocked videos, use the new tab workaround — Some videos open in a new tab instead of auto-downloading. Just right-click the video → "Save Video As…" from the new tab. The URL is also copied to your clipboard.
⚠️ Important Notes
- Native X videos only — Works on videos uploaded directly to X/Twitter. Does not work on embedded YouTube, Vimeo, or other external players.
- No watermark — Downloads the original file directly from Twitter's CDN (
video.twimg.com). No third-party branding. - Respect copyright — You're downloading content created by others. Don't re-upload or monetize without permission.
- CORS may block direct download — Some browsers block cross-origin downloads. In that case, the script opens the video in a new tab where you can right-click → Save As.
- Large videos — Very long videos (10+ minutes) may take a moment to fetch. The download happens entirely in-browser.
🔧 Troubleshooting
| Problem | Fix |
|---|---|
| "No video URLs found" | Click PLAY on the video, then run the script again. The interceptor is now active. |
| Opens in new tab instead of downloading | CORS restriction — right-click the video → "Save Video As…" |
| Only finds low-quality versions | The video may have been uploaded in low quality. Check window.xVideo.all for all options. |
| Script doesn't work on a GIF | It should — X GIFs are MP4s. Make sure you're on the tweet page, not the media viewer overlay. |
| "Not a tweet page" error | Navigate to x.com/user/status/123456. The script needs /status/ in the URL. |
🔗 Related Features
| Feature | Use Case | Link |
|---|---|---|
| Media Scraping | Download ALL images/videos from a profile's media tab | → Guide |
| Tweet Scraping | Archive tweets and their media together | → Guide |
| Bookmark Exporter | Export bookmarked tweets (including video links) | → Guide |
| Thread Scraping | Save a full thread including embedded videos | → Guide |
❓ FAQ
Q: How do I download a video from Twitter / X for free?
A: Go to the tweet with the video, click play on it, open your browser console (F12 → Console), paste the XActions video downloader script, and press Enter. The video downloads as an MP4 in the highest quality available — no app, no API key, no watermark, completely free.
Q: Can I download Twitter GIFs?
A: Yes — X/Twitter stores all "GIFs" as MP4 video files internally. The XActions downloader works on them identically and downloads the MP4 version, which is actually higher quality and smaller in file size than the original GIF.
Q: What video quality can I get?
A: X typically stores videos in 3 qualities: 480p (480×270), 360p (640×360), and 720p (1280×720). The script finds all variants and auto-downloads the highest. Some videos uploaded in 1080p will have a 1920×1080 variant available.
Q: Does this work on protected/private accounts?
A: Only if you can see the video in your browser (i.e., you follow the private account). The script reads what's already loaded on the page — it doesn't bypass any access restrictions.
Q: Why does the video open in a new tab instead of downloading?
A: This is a browser CORS (Cross-Origin Resource Sharing) restriction — some browsers block downloading files from video.twimg.com directly. When this happens, the video opens in a new tab. Right-click it → "Save Video As…" to save it to your device.
⚡ Ready to try Download Videos from X (Twitter) — Free, No App, No Watermark?
XActions is 100% free and open-source. No API keys, no fees, no signup.
Browse All Scripts