Use Twitter (X) Media Downloader with Python
Download videos, photos, GIFs, audio and poster images from public Twitter/X posts - no login, no cookies. Works with post links or usernames, supports every video quality, audio-only M4A/MP3, quoted-post media and public signed download links.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"audioFormat": "m4a",
"audioOnly": false,
"downloadAllQualities": false,
"imageQuality": "orig",
"includeAltText": true,
"includeMetadata": true,
"includePosterImage": false,
"includeQuotedMedia": true,
"maxItems": 1,
"maxPostsPerUser": 1,
"mediaTypes": [
"video",
"photo",
"gif"
],
"minLikes": 0,
"minRetweets": 0,
"onlyVerifiedAuthors": false,
"postUrls": [
"https://x.com/NASA/status/2107095768821576019"
],
"saveToFile": false,
"usernames": [],
"videoQuality": "best"
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
postUrls | array | optional | Paste links of posts to download media from. Accepts x.com and twitter.com links, i/web/status links, or bare post IDs. Optional if you provide usernames or profile links below. |
usernames | array | optional | Scan a profile's media posts and download their photos, videos and GIFs. Type handles with or without @, or paste full profile links. Optional if you provide post links above. |
maxPostsPerUser | integer | optional | How many of each profile's most recent media posts to look at. Posts without matching media or filtered-out posts are skipped. |
maxItems | integer | optional | Hard cap on dataset rows for the whole run. The actor stops collecting and downloading once this many files are emitted. |
mediaTypes | array | optional | Choose which kinds of media to download. All types are selected by default. |
imageQuality | string | optional | Quality to use for photos (and video poster images). Original is the full-resolution file posted on X. |
videoQuality | string | optional | Preferred video resolution. The actor picks the closest available quality when the exact one is not offered. |
downloadAllQualities | boolean | optional | Emit and download one separate file for every progressive MP4 quality X offers (for example 1080p, 720p, 480p) instead of only the preferred one. Every quality gets its own row, filename and download link. |
includePosterImage | boolean | optional | Save the thumbnail/poster image of each video or GIF as an additional photo file. |
includeAltText | boolean | optional | Attach the alt text (image description) X stores for each photo or video thumbnail to the media row. |
audioOnly | boolean | optional | Download only the audio track of videos. Photos and GIFs are skipped. Files are extracted with ffmpeg and file saving is enabled automatically. |
audioFormat | string | optional | Format used when Audio only is on. |
includeQuotedMedia | boolean | optional | Also download the photos, videos and GIFs that appear inside a quoted post (a post embedded in the linked post). Those rows are marked with mediaSource "quoted post". |
includeMetadata | boolean | optional | Attach the post text, date, conversation and reply/quote flags, hashtags, mentions, links, like/repost/reply/quote/bookmark/view counts, author name, avatar, verification and follower count, and a derived engagement rate to every downloaded file. |
minLikes | integer | optional | When scanning usernames, skip posts with fewer likes than this. Posts whose like count is unknown are kept. Leave at 0 to keep every post. |
minRetweets | integer | optional | When scanning usernames, skip posts with fewer reposts than this. Posts whose repost count is unknown are kept. Leave at 0 to keep every post. |
onlyVerifiedAuthors | boolean | optional | When scanning usernames, skip posts whose author is not verified. Posts where verification is unknown are kept. |
saveToFile | boolean | optional | Download each file into this run's key-value store and add a public signed download link. Turn off to collect metadata and direct CDN links only. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/fnHBLqMkGad5UusrY/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build b2lfr5bXEkHVsmX4j and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.