---
title: "Twitter Video Downloader API — X Videos & GIFs | FastSaverAPI"
url: https://fastsaverapi.com/twitter-video-downloader-api/
description: "X (Twitter) video downloader API: a public post link in, a playable MP4 out, with width, height and duration. No developer account, no OAuth, no app review."
updated: 2026-08-10
api_base: https://api.fastsaver.io/v1
site_index: https://fastsaverapi.com/llms.txt
openapi: https://fastsaverapi.com/openapi.json
---

# X (Twitter) video downloader API for MP4s, GIFs and images

The X (Twitter) video downloader API turns a public x.com or twitter.com post link into a
playable MP4 with one GET request. You get a direct download URL, dimensions, duration and the
post text back as JSON — no developer account, no OAuth, no app review. The same
/fetch call also resolves five other platforms .

- **Endpoint**: GET /fetch

- **Credits**: 1.5 per post

- **Auth**: X-Api-Key header

- **Returns**: direct MP4 URL + metadata

## what this endpoint resolves

X carries two kinds of moving image and treats them identically. A native video comes back as
the uploaded MP4. So does a GIF: X stopped storing real GIFs years ago, and what loops in the
timeline is a silent video.

- Native video — the uploaded file, not a re-encode.
- GIFs — a couple of seconds of MP4, no audio stream.
- Still images — the full-size attachment, not the thumbnail.
- Quoted and threaded posts — the link you pass decides whose media you get.

A quote-post, the post it quotes and every reply in a thread are separate URLs with separate
status IDs. Pass the outer link and you get the outer post's attachment — nothing at all, if the
quoter added none. Copy the permalink of the post you actually want.

## the link formats you can pass

Pass the URL exactly as you copied it — what matters is the status ID.

- https://x.com/<handle>/status/<id> — the canonical form.
- https://twitter.com/<handle>/status/<id> — still all over old databases.
- https://x.com/i/web/status/<id> — the handle-less permalink.
- Share links with ?t= and ?s=20 tracking parameters attached.

Since share links carry query strings, URL-encode the url parameter in code. An
unencoded &s=20 becomes a parameter of your request instead of part of
the link, and the API sees a truncated URL.

Normalise before you store anything. Rewrite the host to x.com , drop the query
string, and pull the digits after /status/ — that number is your cache key. The
id in the response is not: it is a verbatim echo of the URL you sent, so the same
post arriving in four spellings becomes four cache entries and four paid calls.

## sending a status link

The same shape as every other platform on /fetch . No POST body, no SDK, no
session to keep warm.

GET
https://api.fastsaver.io/v1/fetch
1.5 credits



```
curl -G "https://api.fastsaver.io/v1/fetch" \
  --data-urlencode "url=https://x.com/SpaceX/status/1749286413298475123" \
  -H "X-Api-Key: fs_sk_•••••••••••"
```



```
{
  "ok": true,
  "id": "https://x.com/SpaceX/status/1749286413298475123",
  "source": "x.com",
  "type": "video",
  "download_url": "https://video.twimg.com/ext_tw_video/...",
  "thumbnail_url": "https://pbs.twimg.com/ext_tw_video_thumb/...",
  "width": 1280,
  "height": 720,
  "duration": 34,
  "caption": "Static fire test complete."
}
```


Three fields shape your handler. type says whether you hold video or a still.
duration is the cheapest tell that a "video" is really a GIF. source is
an opaque platform label — do not compare it to a hardcoded hostname. One field shapes nothing:
id is the URL you sent, echoed back, so it is an identifier in name only. Still
images arrive without width , height or duration at all —
read those with a default rather than a bracket lookup. Failures arrive as ok: false
with a detail string, so branch on ok , not the HTTP status.

## gifs, and why yours is silent

If you are building a reposting bot or an archive, this is the detail that bites. A Twitter
GIF has no audio track, and pipelines that assume one exists — ffmpeg concat, some Telegram and
Discord upload paths — fail or emit a broken file. Branch on it. In Telegram, send the silent MP4
as an animation and it plays inline, muted and looping, like the original.

The API will not produce a real .gif for you:



```
ffmpeg -i clip.mp4 \
  -vf "fps=15,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" \
  -loop 0 out.gif
```

Expect the result to be several times larger than the MP4. That is the format, not your
settings.

## resolving and saving

Two steps, separate on purpose: the API call authenticates with your key, the download does
not. Never attach X-Api-Key to the CDN request — that host is not ours.



```
import re
import httpx

KEY = "fs_sk_•••••••••••"
api = httpx.Client(headers={"X-Api-Key": KEY}, timeout=60)

STATUS_ID = re.compile(r"/status/(\d+)")

def cache_key(post_url: str) -> str:
    # the response's "id" is just the URL you sent - parse the real one
    m = STATUS_ID.search(post_url)
    if not m:
        raise ValueError(f"no status id in {post_url}")
    return m.group(1)

def resolve(post_url: str) -> dict:
    r = api.get("https://api.fastsaver.io/v1/fetch", params={"url": post_url})
    if r.status_code == 429:
        raise RuntimeError("rate limited - back off, then retry")
    r.raise_for_status()
    data = r.json()
    if not data.get("ok"):
        raise RuntimeError(data.get("detail", "could not resolve post"))
    return data

def save(post_url: str, path: str) -> dict:
    media = resolve(post_url)
    # plain client: no API key header on the CDN request
    with httpx.stream("GET", media["download_url"], follow_redirects=True) as src:
        with open(path, "wb") as out:
            for chunk in src.iter_bytes():
                out.write(chunk)
    return media

info = save("https://x.com/SpaceX/status/1749286413298475123", "clip.mp4")
print(cache_key(info["id"]), info["type"], info.get("width"), info.get("duration"))
```

From a shell:



```
curl -sG "https://api.fastsaver.io/v1/fetch" \
  --data-urlencode "url=$1" \
  -H "X-Api-Key: $FASTSAVER_KEY" \
| jq -r 'select(.ok) | .download_url' \
| xargs -r curl -sL -o clip.mp4
```


## how this compares to the official X API

Not competing products. X sells access to the platform; this sells one narrow capability —
public post link in, playable file out.

### what you skip

- No developer account application, no use-case description, no approval wait.
- No OAuth flow, no app registration, no token refresh, no request signing.
- No monthly tier you pay for in a month you barely called it — 1.5 credits per post.

### what you do not get

- Posting, replying, liking, following. Nothing that writes.
- Timelines, follower graphs, search, filtered streams, engagement metrics.
- Anything behind an authenticated session, including your own protected content.

Building an analytics dashboard or a scheduler? Use X's own API and do the OAuth work. Need the
video out of a link a user pasted into your app? One GET here — and GET /balance
reports your credits and per-minute limit for free.

## the X limits to design around

- Public posts only. Protected accounts, sensitive-media interstitials and deleted posts return an error, not a file.
- Download URLs expire. The link points at X's short-lived CDN — fetch the bytes in the same job and cache the status ID you parsed, never the CDN URL and never the response's id .
- GIFs carry no audio. Handle zero-audio-stream video explicitly when you transcode or upload.
- Rate limits are per plan — 10 requests per minute on Free, up to 900 on Mega. A 429 means slow down; the link is fine.
- The platform keeps moving. X changes hosts and playback formats on its own schedule; we fix that behind the same endpoint.

What you download is someone else's work. Copyright, X's terms and the rights of whoever posted
it are yours to respect.

## questions about the X (Twitter) API

**Q: Do x.com and twitter.com links both work?**

A: Yes. Old twitter.com bookmarks, x.com shares and /i/web/status/… permalinks all resolve to the same media. One caveat if you are building a cache: the response's id is the URL you sent, echoed back verbatim — not the status ID. Three spellings of one post therefore give you three different id values. Parse the digits after /status/ yourself and key on those.

**Q: Do I need an X developer account, an app or an OAuth token?**

A: No. No application form, no app review, no token exchange — you send your X-Api-Key header and the post URL over HTTPS. What you get back is media resolution only: this endpoint cannot post, read a timeline or search.

**Q: Why does a Twitter GIF download as an MP4?**

A: Because it was never a GIF. X transcodes uploads to a short, silent MP4 and loops it in the client; the API hands you that file as stored. If you genuinely need a .gif , transcode it with ffmpeg after the download.

**Q: Can it download video from protected or age-restricted posts?**

A: No. Anything needing a logged-in session — protected accounts, sensitive-media interstitials, deleted posts — returns an error instead of a file. The endpoint resolves only what an anonymous visitor could already see.

**Q: Can I pick a resolution, like 720p or 480p?**

A: Not here. GET /fetch takes one parameter, url , and returns the file the platform serves — with width and height on video responses so you know what you got. Format selection exists only on the YouTube endpoints.

## Keep reading

- Facebook video downloader API — same endpoint, same shape
- TikTok downloader API — also 1.5 credits
- All supported platforms — nine references
- Frequently asked questions — keys, limits, legality

## Resolve a post link right now

Paste an x.com URL into the playground and read the JSON before committing to anything.

Run it in the playground
Create a free API key
