All guides
Developers5 min readBy DrummerduckJul 19, 2026

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.

Open the downloader

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.

Open the downloader

Only download public content you have the right to use, and respect copyright.