Download Twitter Videos in Python
You want the video out of a tweet from a Python script — to archive a thread, feed a
pipeline, or batch a folder of links. The official API turns that into a project:
developer application, OAuth tokens, a media endpoint with shifting terms. This does
it with requests and a URL.
The script below hits a plain JSON API, reads back the direct MP4 link, and streams
the file to disk. No API key needed to start, no SDK to install beyond requests.
Short answer: GET /api/extract?url=<TWEET_URL>, read
data.media[0].variants[0].url, then stream that link to a file.
Try it now
Paste a Twitter/X link and download in seconds — free, no login.
Extract the video URL
One call returns the author, text, thumbnail, and every media variant sorted best-quality-first.
import requests
API = "https://download-twitter-video.drummerduck.com"
def best_video_url(tweet_url: str) -> str:
r = requests.get(f"{API}/api/extract", params={"url": tweet_url}, timeout=30)
r.raise_for_status()
data = r.json()
if not data["ok"]:
raise RuntimeError(data["error"]["message"])
videos = [m for m in data["data"]["media"] if m["type"] == "video"]
if not videos:
raise RuntimeError("This tweet has no video.")
return videos[0]["variants"][0]["url"] # variants sorted best-first
Save the file to disk
You can stream the extracted CDN link yourself, or let /api/download name the file
and stream it for you. Streaming keeps memory flat on large clips:
def download(tweet_url: str, path: str) -> None:
url = best_video_url(tweet_url)
with requests.get(url, stream=True, timeout=60) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
download("https://x.com/SpaceX/status/1919172303709184350", "spacex.mp4")
Prefer a single call that also sets the filename? Hit /api/download directly:
r = requests.get(
f"{API}/api/download",
params={"url": "https://x.com/SpaceX/status/1919172303709184350", "quality": "1080p"},
timeout=60,
)
open("spacex.mp4", "wb").write(r.content)
Batch a list of links, with backoff
Reading rate-limit headers and pausing when you're low is the difference between a
script that finishes and one that trips a 429 halfway through:
import time
def download_many(tweet_urls: list[str], headers: dict | None = None) -> None:
for i, url in enumerate(tweet_urls):
r = requests.get(f"{API}/api/extract", params={"url": url},
headers=headers or {}, timeout=30)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", "60"))
print(f"Rate limited; sleeping {wait}s")
time.sleep(wait)
continue
remaining = int(r.headers.get("X-RateLimit-Remaining", "999"))
# ... extract + save as above ...
if remaining < 3:
time.sleep(2) # ease off before hitting the wall
Raise your limits with a free key
Anonymous callers get 30 requests an hour. For a bigger batch, mint a free API key — no signup — and pass it as a bearer token. That lifts you to 300 an hour:
key = requests.post(f"{API}/api/keys", json={"label": "py-script"}, timeout=30) \
.json()["data"]["apiKey"]
headers = {"Authorization": f"Bearer {key}"}
download_many(my_links, headers=headers)
Store the key and reuse it — key creation is rate-limited, so don't mint a new one per run.
Frequently asked questions
Do I need tweepy or the official API?
No. This is a plain HTTP call with requests. No developer account, no OAuth.
How do I get 1080p specifically?
data.media[0].variants[0] is already the highest quality. To force one, pass
quality=1080p to /api/download. See the
1080p guide.
What about GIFs and images?
Check media[].type: "gif" items are silent MP4s, "image" items are photos. Both
expose direct URLs the same way videos do.
Is there an async version?
Swap requests for httpx.AsyncClient and await the calls — the endpoints and
JSON shapes are identical. The full reference is on the developers page.
Try it now
Paste a Twitter/X link and download in seconds — free, no login.
Only download public content you have the right to use, and respect copyright.