# X / Twitter Media Downloader & Post Details (`arjun_code/x-twitter-media-downloader`) Actor

Download X / Twitter videos, photos, GIFs, and X Article images in your preferred quality. Extract post and Article text, engagement metrics, X translations, author details, direct CDN links, and files saved in Apify Storage. No X login, cookies, or API key required.

- **URL**: https://apify.com/arjun\_code/x-twitter-media-downloader.md
- **Developed by:** [Arjun AI](https://apify.com/arjun_code) (community)
- **Categories:** Social media, Videos, Developer tools
- **Stats:** 1 total users, 0 monthly users, 95.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 media files

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## X / Twitter Media Downloader & Post Details

Download videos, photos, GIFs, and X Article images at your selected quality from public X posts. Every result also includes the complete post or Article content, engagement metrics, X-provided translation, and the author's public profile.

Paste tweet IDs or `x.com` / `twitter.com` status URLs. X Articles are supported through the public status post that contains the Article. The Actor saves one dataset row per requested post and, by default, stores every attached media file in the run's Key-Value Store.

### What you get

- Selectable MP4 quality for videos and animated GIFs
- Selectable `original`, `large`, `medium`, `small`, or `thumbnail` photo quality
- Media files stored in Apify with a directly accessible `stored_url`
- X CDN source URL, preview image, dimensions, duration, aspect ratio, and MP4 variants
- Full post text, including long-form Note Tweet text when available
- X Article detection, title, preview, summary, full plain text, publication dates, and outbound links
- X Article cover and body images included in the same media and storage workflow
- Replies, reposts, quotes, likes, bookmarks, and view counts
- Hashtags, cashtags, mentions, expanded links, conversation, and reply references
- X-provided translation text, source language, target language, and translated entities
- Author bio, location, account age, audience counts, verification, professional category, affiliation, avatar, banner, and profile URL

Only the requested focal post is saved. Replies returned alongside it by X are not emitted as separate results.

### How to download X media

1. Paste one or more X post URLs or numeric post IDs into **X post IDs or URLs**.
2. Keep **Store media files** enabled to copy the selected files into Apify Storage, or disable it for metadata and X CDN links only.
3. Choose the preferred video and image quality.
4. Run the Actor.
5. Open **Media Downloads** for one row per file or **Post Details** for one row per post.

### Input

Use `tweets` for one or more IDs or links. Equivalent URLs and IDs are deduplicated. For an X Article, provide its outer `/status/{tweet_id}` URL or numeric post ID. Direct `/i/article/{article_id}` URLs are not currently accepted as input. An invalid value returns an `invalid_input` result without stopping valid posts in the same run.

There is no fixed limit on the number of input posts. Unique posts are processed sequentially and normally produce one Dataset item each. When `storeMediaFiles` is enabled, one post can also create several media records in the Key-Value Store, so split large download jobs across runs to control runtime, traffic, and storage use.

```json
{
  "tweets": [
    "https://x.com/NASA/status/2095890073031966734"
  ],
  "storeMediaFiles": true,
  "videoQuality": "720p",
  "imageQuality": "original"
}
```

`storeMediaFiles` defaults to `true`:

- `true` downloads media into the run's default Key-Value Store and returns `stored_url`, `storage_key`, file size, and download status.
- `false` skips file transfer and returns the same post details plus X CDN `source_url` values.

`videoQuality` accepts `highest`, `1080p`, `720p`, `480p`, `360p`, or `lowest`. X exposes discrete MP4 variants, so the Actor selects the nearest available resolution that does not exceed the requested p value. `imageQuality` accepts `original`, `large`, `medium`, `small`, or `thumbnail`.

### Reliability

No X login, cookies, API key, or proxy setup is required. Apify Proxy routing and temporary network, server, access, and rate-limit failures are handled automatically. Each post-detail request is attempted up to five times. If one post still cannot be retrieved, the Actor writes an uncharged `request_failed` item and continues with the remaining inputs.

Media downloads are handled separately from post extraction. If a photo or video transfer fails, the post details remain available and that media item reports `download_status: "failed"` with a `download_error`.

### Output example

```json
{
  "status": "success",
  "requested_input": "https://x.com/NASA/status/2095890073031966734",
  "tweet_id": "2095890073031966734",
  "tweet_url": "https://x.com/NASA/status/2095890073031966734",
  "created_at": "2026-09-04T15:01:47Z",
  "text": "AvGeeks, assemble! MAX POWER lands in Florida on Nov. 7–8, with an F‑104 flyover by the Starfighters…",
  "is_longform": false,
  "is_article": false,
  "language": "en",
  "source_name": "Twitter Web App",
  "reply_count": 159,
  "retweet_count": 323,
  "quote_count": 27,
  "like_count": 1898,
  "bookmark_count": 124,
  "view_count": 989011,
  "urls": [
    {
      "url": "https://t.co/1DVwblNmua",
      "expanded_url": "https://www.nasa.gov/maxpower",
      "display_url": "nasa.gov/maxpower"
    }
  ],
  "media_count": 1,
  "stored_media_count": 1,
  "store_media_files": true,
  "media_types": ["video"],
  "media_source_urls": ["https://video.twimg.com/.../1280x720/...mp4"],
  "stored_media_urls": ["https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/...mp4"],
  "media": [
    {
      "origin": "tweet",
      "media_id": "2095889698845556737",
      "type": "video",
      "preview_image_url": "https://pbs.twimg.com/...jpg",
      "source_url": "https://video.twimg.com/.../1280x720/...mp4",
      "content_type": "video/mp4",
      "requested_quality": "720p",
      "selected_quality": "720p",
      "selected_resolution": "1280x720",
      "available_resolutions": ["1920x1080", "1280x720", "640x360", "480x270"],
      "filename": "2095890073031966734_video_1_2095889698845556737.mp4",
      "size_bytes": 1399813,
      "storage_key": "2095890073031966734_video_1_2095889698845556737.mp4",
      "stored_url": "https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/...mp4",
      "download_status": "stored",
      "download_error": null,
      "availability_status": "Available",
      "width": 1280,
      "height": 720,
      "duration_millis": 15548,
      "aspect_ratio": [16, 9],
      "hls_url": "https://video.twimg.com/...m3u8",
      "video_variants": [
        {
          "bitrate": 10368000,
          "content_type": "video/mp4",
          "width": 1920,
          "height": 1080,
          "url": "https://video.twimg.com/.../1920x1080/...mp4"
        },
        {
          "bitrate": 2176000,
          "content_type": "video/mp4",
          "width": 1280,
          "height": 720,
          "url": "https://video.twimg.com/.../1280x720/...mp4"
        }
      ]
    }
  ],
  "translation_available": false,
  "translated_text": null,
  "author_id": "11348282",
  "author_username": "NASA",
  "author_name": "NASA",
  "author_description": "Making the seemingly impossible, possible. ✨",
  "author_location": "Pale Blue Dot",
  "author_followers_count": 92377419,
  "author_verified_type": "Government",
  "author_blue_verified": true,
  "author_profile_url": "https://x.com/NASA"
}
```

For an X Article, the same result shape includes populated `article_*` fields and labels Article images through `media[].origin`:

```json
{
  "status": "success",
  "requested_input": "https://x.com/qinbafrank/status/2099834905891049885",
  "tweet_id": "2099834905891049885",
  "tweet_url": "https://x.com/qinbafrank/status/2099834905891049885",
  "text": "https://t.co/IfgOV2kZ1t",
  "is_longform": false,
  "is_article": true,
  "article_id": "2099832298527248384",
  "article_url": "https://x.com/i/article/2099832298527248384",
  "article_title": "我不得不把才华埋葬在昨天",
  "article_preview_text": "一篇署名…的微信公号文章于2026年9月14日在全球 AI 社区引发大范围传播。…",
  "article_summary_text": "- 前几天 DeepSeek v4.1 发布…",
  "article_text": "一篇署名…的微信公号文章于2026年9月14日在全球 AI 社区引发大范围传播。…",
  "article_published_at": "2026-09-15T12:17:09Z",
  "article_modified_at": "2026-09-15T12:17:09Z",
  "article_block_count": 32,
  "article_links": ["https://mp.weixin.qq.com/…"],
  "article_cover_image_url": "https://pbs.twimg.com/media/HSQbKXka4AALDSw.jpg",
  "article_media_count": 1,
  "media_count": 1,
  "media": [
    {
      "origin": "article_cover",
      "media_id": "2099833191200907264",
      "type": "photo",
      "source_url": "https://pbs.twimg.com/media/HSQbKXka4AALDSw.jpg?format=jpg&name=orig",
      "selected_quality": "original",
      "selected_resolution": "970x388",
      "download_status": "stored",
      "stored_url": "https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/...jpg"
    }
  ]
}
```

These are abridged snapshots from real runs. Post and Article text plus long CDN/storage URLs are shortened for readability, and engagement and follower counts change over time. `text` remains the outer post text; `article_text` contains the full X Article body. `source_url` points to the selected media version on X's CDN. `stored_url` points to the file copied into Apify Storage. Optional values that X does not return are represented by `null`.

### Dataset views

- **Media Downloads** is the default view. It expands attached media so every post photo, video, GIF, Article cover, or Article body image appears as its own row with preview, selected resolution, size, status, and download link. The `origin` value is `tweet`, `article_cover`, or `article_body`.
- **Post Details** keeps one row per requested post and focuses on post or Article text, Article title and link, engagement, translation, media totals, and a concise author summary.
- **Author Details** exposes the author's public bio, location, account age, audience counts, verification, professional category, affiliation, website, avatar, and banner. It remains one row per requested post, so the same author can appear more than once.

### Result and download statuses

Post `status` values:

- `success` — the post was found and parsed
- `invalid_input` — the supplied value was not a valid numeric post ID or status URL
- `not_found` — the post is deleted, private, withheld, or unavailable
- `request_failed` — post details were still unavailable after five automatic attempts

Per-media `download_status` values:

- `stored` — the file was downloaded and saved in Apify Storage
- `failed` — the file transfer or storage operation failed; see `download_error`
- `not_requested` — `storeMediaFiles` was disabled
- `unavailable` — X did not provide a downloadable MP4 or original image URL

Dataset results can be exported as JSON, JSONL, CSV, Excel, XML, or RSS. Stored media remains available according to the Key-Value Store retention of the run.

### Pricing

This Actor uses transparent pay-per-event pricing. Platform usage generated by the Actor run—including compute, proxy, storage operations, and data transfer—is included and is not passed through as a separate charge.

| Event | When it is charged | Price |
| --- | --- | ---: |
| Actor start (`apify-actor-start`) | Automatically at run start; one event at up to 1 GB of memory | $0.00005 |
| Post detail (`tweet-detail-result`) | Once for each successfully retrieved post | $0.0005 |
| Media file (`media-file-stored`) | Once for each photo, video, or GIF successfully copied to Apify Storage | $0.0015 |

`invalid_input`, `not_found`, and `request_failed` post records are not charged as post-detail results. An X Article and its outer post together count as one successful post-detail result. Each successfully stored Article cover or body image counts as one media-file event, just like post media. Media with `failed`, `not_requested`, or `unavailable` download status is not charged as a stored file. Set `storeMediaFiles` to `false` when you only need post metadata and X CDN links; no `media-file-stored` events are then generated.

Example totals at the Actor's current 512 MB default memory, including its $0.00005 start event:

- One successful post without stored files: **$0.00055**
- One successful post with one stored file: **$0.00205**
- One successful post with four stored files: **$0.00655**

The Actor page's **Pricing** tab is the final source for the currently active rates.

### Run with the Apify API

Replace `YOUR_APIFY_TOKEN` with your Apify API token:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/arjun_code~x-twitter-media-downloader/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tweets":["https://x.com/NASA/status/2095890073031966734"],"storeMediaFiles":true,"videoQuality":"720p","imageQuality":"original"}'
```

The run response contains `defaultDatasetId` and `defaultKeyValueStoreId`. Use them to retrieve structured results and stored media through the Apify API. You can also schedule runs, trigger them from webhooks, or connect results to other Apify integrations.

### Common use cases

- Download reusable copies of public X videos, photos, and GIFs.
- Extract and archive X Article titles, summaries, full text, links, and images.
- Archive post text, engagement metrics, translations, and author metadata together.
- Collect media source variants for editorial, research, or content-analysis workflows.
- Feed post and author records into data pipelines through the Apify API.

### Continue your X research workflow

| Goal | Actor |
| --- | --- |
| Discover public accounts by keyword, profession, niche, or location | [X (Twitter) People Search Scraper](https://apify.com/arjun_code/x-twitter-people-search-scraper) |
| Expand from a known profile through X's native Similar to recommendations | [X (Twitter) Similar Accounts Finder — No Login](https://apify.com/arjun_code/x-twitter-similar-accounts-finder) |
| Export followers or accounts followed by one or more profiles | [X Followers & Following Scraper — No Login](https://apify.com/arjun_code/x-twitter-followers-followings-scraper) |
| Inspect account origin, signup source, and username-change history | [X Twitter Account Origin Intelligence](https://apify.com/arjun_code/x-twitter-account-origin-scraper) |
| Monitor profile, username, verification, and follower changes over time | [X (Twitter) Profile & Username Change Monitor](https://apify.com/arjun_code/x-twitter-profile-username-history-monitor) |

### FAQ

#### Do I need an X account or API key?

No. X authentication and Apify Proxy routing are handled automatically.

#### What is the difference between a regular post, a Note Tweet, and an X Article?

A regular post returns its content in `text`. A long-form Note Tweet also uses `text` and has `is_longform: true`. An X Article has `is_article: true`; its outer post remains in `text`, while its title, summary, full body, links, dates, and image totals are returned in the `article_*` fields.

#### Can I enter a direct `x.com/i/article/...` URL?

Not currently. Enter the public `/status/{tweet_id}` link that contains the Article, or enter that numeric post ID. The result includes the direct Article URL in `article_url`.

#### Will the selected video resolution always be exact?

Not always. X exposes a fixed set of MP4 variants. For a requested p value, the Actor selects the nearest available resolution that does not exceed it and reports both the requested and selected quality.

#### What happens if one media download fails?

The post record is still saved. The affected media item contains `download_status: "failed"` and a readable `download_error`, while other media files continue downloading.

### Limitations and responsible use

- Deleted, private, withheld, or otherwise unavailable posts may return `not_found`.
- Direct `/i/article/{article_id}` URLs are not accepted as input; use the Article's outer status URL or post ID.
- Download quality is limited to the image sizes and MP4 variants supplied by X.
- Media URLs and public post fields can change or expire after collection.
- This Actor is independent and is not affiliated with or endorsed by X Corp.

Use public content lawfully. Respect copyright, privacy, X's terms, Apify's terms, and any rights attached to the downloaded media.

### Support

If a run fails, a post field is missing, or a media file cannot be downloaded, open an issue on the Actor page and include the Apify run ID, input, and expected behavior. Do not include passwords, cookies, tokens, or other secrets.

# Actor input Schema

## `tweets` (type: `array`):

Enter one X post per line. Each value may be a numeric tweet ID or a full x.com/twitter.com status URL. X Articles are supported through their outer status URL or post ID; direct x.com/i/article URLs are not accepted. Duplicate IDs are queried only once. Invalid values are reported without stopping valid posts in the same run. There is no fixed item limit; posts are processed sequentially, and storing media increases runtime, traffic, and storage use.

## `storeMediaFiles` (type: `boolean`):

Download the selected video, photo, and GIF versions into this run's Apify Key-Value Store. Disable it to return post details and selected X CDN source URLs only.

## `videoQuality` (type: `string`):

Select which X-provided MP4 variant to use. If the requested p value is unavailable, the Actor selects the nearest available resolution that does not exceed it and reports the actual result.

## `imageQuality` (type: `string`):

Select the X image size used for every photo in the requested posts.

## Actor input object example

```json
{
  "tweets": [
    "https://x.com/i/web/status/2099397318294278372"
  ],
  "storeMediaFiles": true,
  "videoQuality": "highest",
  "imageQuality": "original"
}
```

# Actor output Schema

## `results` (type: `string`):

Structured post and X Article details with media storage links in the default dataset. Downloaded files are stored in the run's default Key-Value Store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "tweets": [
        "https://x.com/i/web/status/2099397318294278372"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arjun_code/x-twitter-media-downloader").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "tweets": ["https://x.com/i/web/status/2099397318294278372"] }

# Run the Actor and wait for it to finish
run = client.actor("arjun_code/x-twitter-media-downloader").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "tweets": [
    "https://x.com/i/web/status/2099397318294278372"
  ]
}' |
apify call arjun_code/x-twitter-media-downloader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arjun_code/x-twitter-media-downloader"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8PgbaxoSehwPhrrOi/builds/psmaJnb5LaYkQOjVd/openapi.json
