# X (Twitter) Scraper: Tweets & Profiles by URL, No Login (`humble-echidna/x-scraper`) Actor

Get tweets and X profiles by URL, tweet id or @handle, with no login or cookies: full text, likes, replies, retweets, quotes, bookmarks, views, photos, every video MP4, polls and quoted tweets; profiles with followers, bio and website. Single tweets welcome. Optional latest tweets and top replies.

- **URL**: https://apify.com/humble-echidna/x-scraper.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Categories:** Social media, Marketing
- **Stats:** 4 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 tweet or profiles

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

### What does X (Twitter) Scraper do?

**X (Twitter) Scraper** looks up **tweets and X profiles by URL, tweet id or @handle** and returns each one with its
full text, **likes, replies, retweets, quotes, bookmarks and views**, photos and **every video MP4**, polls, link
cards and quoted tweets; profiles come with followers, bio, location and website. **One tweet is fine**: there's no
minimum per run. No login, no cookies, no X account and no API key.

It is **not** a search tool: X shows search, hashtags and full timelines only to logged-in accounts, and this actor
never logs in. Give it the tweets and profiles you already have.

[Input fields](https://apify.com/humble-echidna/x-scraper/input-schema) ·
[API](https://apify.com/humble-echidna/x-scraper/api) ·
[Use it from Claude, ChatGPT or Cursor (MCP)](#can-i-use-x-twitter-scraper-from-an-ai-agent-mcp)

**Jump to:** [Fields](#what-data-does-x-twitter-scraper-return) ·
[Price](#how-much-does-it-cost-to-scrape-tweets-and-x-profiles) ·
[How to use](#how-to-scrape-tweets-and-x-profiles) ·
[Input](#input) · [Output](#output) · [Switching](#switching-from-another-x-twitter-actor) ·
[AI agents (MCP)](#can-i-use-x-twitter-scraper-from-an-ai-agent-mcp) · [Limits](#limits) · [FAQ](#faq)

**Try it in one click:** the input comes pre-filled with NASA's profile and the first tweet ever posted
(`jack/status/20`). That's 2 results, or about $0.0006 (2 × $0.30/1,000, plus $0.00005 for the run start). **Then
replace them with what you actually want.**

### What data does X (Twitter) Scraper return?

One result per **tweet** (`"type": "tweet"`):

| Field | Example | Notes |
|---|---|---|
| `text` | `Our biggest event of the year lands soon! ✈️ ...` | Full text, long posts included, t.co links expanded |
| `createdAt` | `2026-10-13T19:46:36Z` | ISO 8601 UTC |
| `likeCount`, `replyCount`, `retweetCount`, `quoteCount`, `bookmarkCount` | `1175`, `113`, `180`, `11`, `50` | As X shows them now |
| `viewCount` | `1331757` | `null` for tweets from before December 2022 (X didn't count views then) |
| `media` | `[{"type": "video", "videoUrl": "https://video.twimg.com/.../1920x1080/....mp4", "videoVariants": [...], "durationMs": 8475, ...}]` | Photos, videos and GIFs; every MP4 X offers, best first |
| `isReply`, `inReplyToId`, `inReplyToUserName` | `true`, `2110094874024219133`, `NASA` | |
| `isQuote`, `quotedTweet` | `true`, `{"id": ..., "text": ..., "author": ...}` | The quoted tweet with its own counts |
| `hashtags`, `mentions`, `links` | `["Artemis"]`, `["NASA_Kennedy"]`, `["https://go.nasa.gov/4iiNPN3"]` | |
| `poll` | `{"choices": [{"label": "Yes", "votes": 10063044}, ...], "endsAt": ..., "isFinal": true}` | `null` when there's no poll |
| `card`, `article` | link preview; X Article title and cover | `null` when there's none |
| `author` | `{"userName": "NASA", "followers": 92323053, "isBlueVerified": true, ...}` | Who posted it |

One result per **profile** (`"type": "profile"`): `userName`, `name`, `description` (bio), `location`, `website`,
`createdAt` (when the account was made), `followers`, `following`, `tweets`, `isBlueVerified`, `verifiedType`
(`Business`, `Government`), `protected`, `profilePicture` (400 px) and `banner`.

Every result also has its `id`, `url`, the `input` line it came from, `foundVia` (`input`, `latestTweets` or
`topReplies`) and `scrapedAt`. The full list is under [Output](#output).

### How much does it cost to scrape tweets and X profiles?

You pay per result: **$0.30 per 1,000 results**, plus $0.00005 each time a run starts. A result is one tweet or one
profile.

- **The example below:** 2 results × $0.30/1,000 = $0.0006, plus the start fee.
- **A month, for example:** re-checking the counts on 500 campaign tweets every day is 15,000 results ≈ **$4.50**.
  Vetting 2,000 creator profiles once ≈ **$0.60**.
- **Caps:** **Max results per run** in the input, and **Maximum cost per run** in the run options. The run stops
  cleanly at whichever comes first.

**Never charged:** lines that fail (a deleted tweet, a suspended or unknown account, a search URL), and the same tweet
or profile given twice.

### How to scrape tweets and X profiles

1. Open X (Twitter) Scraper and click **Try for free** (or **Start** if you're signed in).
2. Paste your tweet URLs, tweet ids, profile URLs or @handles into **Tweets and profiles**, one per line. Mixed lists
   are fine.
3. Optionally turn on **Also get each profile's latest tweets** (up to 5 per profile) or **Also get each tweet's top
   replies** (up to 3 per tweet).
4. Click **Start**, then open the **Output** tab and export as JSON, CSV or Excel.

### Example: NASA and the first tweet

The pre-filled input:

```json
{"targets": ["https://x.com/NASA", "https://x.com/jack/status/20"]}
```

It returned 2 results (real output from a local run on 2026-10-14; the tweet's empty lists left out):

```json
{
  "type": "profile",
  "id": "11348282",
  "url": "https://x.com/NASA",
  "userName": "NASA",
  "name": "NASA",
  "description": "Making the seemingly impossible, possible. ✨",
  "location": "Pale Blue Dot",
  "website": "http://www.nasa.gov/",
  "createdAt": "2007-12-19T20:20:32Z",
  "followers": 92323490,
  "following": 115,
  "tweets": 74427,
  "isBlueVerified": true,
  "verifiedType": "Government",
  "protected": false,
  "profilePicture": "https://pbs.twimg.com/profile_images/1321163587679784960/0ZxKlEKB_400x400.jpg",
  "banner": "https://pbs.twimg.com/profile_banners/11348282/1788992290",
  "possiblySensitive": false,
  "foundVia": "input",
  "input": "https://x.com/NASA",
  "scrapedAt": "2026-10-14T22:39:39.101185Z"
}
```

```json
{
  "type": "tweet",
  "id": "20",
  "url": "https://x.com/jack/status/20",
  "text": "just setting up my twttr",
  "createdAt": "2006-03-21T20:50:14Z",
  "lang": "en",
  "likeCount": 309958,
  "replyCount": 18089,
  "retweetCount": 124640,
  "quoteCount": 7308,
  "bookmarkCount": 21984,
  "viewCount": null,
  "isReply": false,
  "isQuote": false,
  "isEdited": false,
  "author": {
    "id": "12",
    "userName": "jack",
    "name": "jack",
    "url": "https://x.com/jack",
    "followers": 12548338,
    "following": 3,
    "isBlueVerified": true,
    "verifiedType": null,
    "protected": false,
    "profilePicture": "https://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_400x400.jpg"
  },
  "dataSource": "web",
  "foundVia": "input",
  "input": "https://x.com/jack/status/20",
  "scrapedAt": "2026-10-14T22:39:39.101185Z"
}
```

### Input

| Field | What it does |
|---|---|
| **Tweets and profiles** (`targets`) | One per line: a tweet URL (x.com, twitter.com, mobile and `/i/status/` links), a tweet id, a profile URL or a handle (`@NASA` or `NASA`). A bare number is read as a tweet id. |
| Also get each profile's latest tweets (`latestTweets`) | For profile lines, also the posts X shows a logged-out visitor on the profile: up to 5 latest originals. |
| Also get each tweet's top replies (`topReplies`) | For tweet lines, also the replies X shows a logged-out visitor under the tweet: up to 3. |
| Max results per run (`maxResults`) | Cap the total number of results, extras included. |

```json
{
  "targets": ["https://x.com/NASA/status/2110094874024219133", "@SpaceX", "20"],
  "latestTweets": true,
  "maxResults": 100
}
```

### Output

Tweets and profiles share one dataset; `type` says which a result is. Fields a result doesn't have are left out or
`null` (a profile has no `likeCount`; an old tweet has no `viewCount`).

```json
{
  "type": "tweet",
  "id": "2110094874024219133",
  "url": "https://x.com/NASA/status/2110094874024219133",
  "text": "Our biggest event of the year lands soon! ✈️\n\nMAX POWER, a new aerospace expo, is happening Nov. 7-8 @NASA_Kennedy. ... plan your visit: https://go.nasa.gov/4iiNPN3",
  "createdAt": "2026-10-13T19:46:36Z",
  "lang": "en",
  "likeCount": 1175, "replyCount": 113, "retweetCount": 180, "quoteCount": 11, "bookmarkCount": 50,
  "viewCount": 1331757,
  "isReply": false, "inReplyToId": null, "inReplyToUserName": null,
  "isQuote": false, "quotedTweetId": null, "quotedTweet": null,
  "isEdited": false, "possiblySensitive": false,
  "hashtags": [], "cashtags": [], "mentions": ["NASA_Kennedy"], "links": ["https://go.nasa.gov/4iiNPN3"],
  "media": [{
    "type": "video",
    "url": "https://pbs.twimg.com/amplify_video_thumb/2110094814733651969/img/mi2EnXdCG2jxU1NW.jpg",
    "videoUrl": "https://video.twimg.com/amplify_video/2110094814733651969/vid/avc1/1920x1080/Iw3FOQxVrNtU6UEJ.mp4?tag=16",
    "videoVariants": [{"bitrate": 10368000, "url": "...1920x1080..."}, {"bitrate": 2176000, "url": "...1280x720..."}, "... 4 in all"],
    "width": 3840, "height": 2160, "durationMs": 8475, "altText": null
  }],
  "card": null, "poll": null, "article": null,
  "author": {"id": "11348282", "userName": "NASA", "name": "NASA", "url": "https://x.com/NASA", "followers": 92323053, "following": 115, "isBlueVerified": true, "verifiedType": "Government", "protected": false, "profilePicture": "https://pbs.twimg.com/profile_images/1321163587679784960/0ZxKlEKB_400x400.jpg"},
  "dataSource": "web",
  "foundVia": "input",
  "input": "https://x.com/NASA/status/2110094874024219133",
  "scrapedAt": "2026-10-14T22:31:02Z"
}
```

`id` is X's own id, the same on every run, so you can use it to deduplicate or to join today's counts with
yesterday's. `dataSource` is `web` normally; if X refuses its web app's data for the rest of a run, tweets still come
back from X's embed service with `dataSource: "embed"`, and then retweet, quote, bookmark and view counts are `null`.
The run's `RUN_STATS` record lists every input line, what it returned and anything that failed, with the reason.

### Switching from another X (Twitter) actor?

Most X actors are built around search. If you used one to look up tweets or accounts you already had, here's where
your input goes:

| What you had | Put it here |
|---|---|
| Tweet links in `startUrls` (strings or `{"url": ...}` objects) | **Tweets and profiles** (`targets`), one URL per line |
| Profile links in `startUrls`, or handles in a handles field (`twitterHandles` and the like) | **Tweets and profiles**, one per line; `@` is optional |
| Tweet ids in an id list | **Tweets and profiles**, one id per line |
| A cap like `maxItems` | **Max results per run** (`maxResults`); `maxItems` is read too |
| "Get the profile's tweets" | **Also get each profile's latest tweets** (`latestTweets`): the 5 latest, all X shows logged out |
| Search terms, hashtags, date ranges, lists, followers | Not available without an X login; see [Limits](#limits) |

The output is flat JSON with the usual names (`likeCount`, `replyCount`, `retweetCount`, `quoteCount`,
`bookmarkCount`, `viewCount`, `createdAt`, `author.userName`, `author.followers`), so most existing pipelines need
few changes.

### Run it on a schedule, or from your own code

1. Save your input as a [task](https://docs.apify.com/platform/actors/running/tasks) (**Save as a new task**, top right
   of the actor page) and add it to a [schedule](https://docs.apify.com/platform/schedules) (Console → Schedules →
   **Create new**), for example daily to track the counts on a campaign's tweets.
2. Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the
   [API](https://apify.com/humble-echidna/x-scraper/api)
   (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with
   your API token); let a [webhook](https://docs.apify.com/platform/integrations/webhooks) tell your system when a
   run succeeds; or connect it to Make, Zapier or n8n through
   [Apify's integrations](https://docs.apify.com/platform/integrations).

#### Can I use X (Twitter) Scraper from an AI agent (MCP)?

Yes, through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp), from Claude, ChatGPT, Cursor or
any other MCP client. Add this to your client's MCP configuration (or let the agent find it with the server's actor
search); your client signs you in to Apify:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=humble-echidna/x-scraper"
    }
  }
}
```

To use an [Apify API token](https://console.apify.com/settings/integrations) instead of signing in, add
`"headers": {"Authorization": "Bearer <APIFY_TOKEN>"}` next to `url`.

An agent passes the links a user gave it, e.g. `{"targets": ["https://x.com/NASA/status/2110094874024219133"]}`, and
gets the tweet back in a few seconds.

### Who it's for

Social and influencer-marketing teams tracking likes, views and retweets on known posts; researchers and journalists
archiving specific tweets with their media; anyone vetting a list of accounts (followers, bio, website); and AI agents
that need to read an x.com link someone pasted.

### Why this one?

- **Single tweets and short lists.** No minimum per run or per query: one tweet costs $0.0003 plus the start fee.
- **The fields people ask for.** View and bookmark counts on every tweet, every MP4 of a video (not just one), long
  posts in full, polls with votes, quoted tweets with their own counts.
- **Honest failures.** A deleted tweet, a suspended account or a search URL is named in the log and `RUN_STATS` with
  the reason, costs nothing, and never stops the rest of the run.
- **Bills follow your list.** You get one result per line you give (plus the extras you turn on), so a run can't
  grow past what you asked for.
- **No login, no cookies, no proxy setup.** It reads what X shows anyone who isn't signed in.

### Limits

- **No search, hashtags, lists or full timelines.** X shows them only to logged-in accounts. Lines like
  `x.com/search?q=...`, `x.com/hashtag/...` or `x.com/NASA/followers` fail with that reason.
- **Latest tweets: 5 per profile** (original posts, no retweets or replies), and **top replies: 3 per tweet**. That's
  all X shows a logged-out visitor; there's no way to page further without an account.
- **Protected accounts:** the profile is public, their tweets aren't, so a protected account's tweet URL fails as not
  found.
- **Very large lists:** X limits how much one logged-out visitor can read. After about 20,000 tweets (or 6,000
  profiles) in one run, tweets come back from X's embed service with fewer counts (`dataSource: "embed"`) and
  profiles fail. Split bigger lists across runs.
- **Counts change.** Likes, views and followers are what X showed at `scrapedAt`.

### FAQ

#### Is it legal to scrape X (Twitter)?

This actor reads only what x.com shows a visitor who isn't logged in: public tweets and public profile details. It
never logs in, uses no account or cookies, and can't see protected accounts, private messages or anything behind a
login. Two things you should know before using the data:

- X's robots.txt (`https://x.com/robots.txt`) disallows automated access for all crawlers. This actor doesn't follow
  it for x.com; it does follow the private-network guard and keeps its request rate modest.
- X's Terms of Service (effective October 9, 2026, [x.com/tos](https://x.com/tos)) say: "crawling or scraping the
  Services in any form, for any purpose without our prior written consent is expressly prohibited", and they set
  liquidated damages for accessing more than 1,000,000 posts in any 24-hour period.

Courts have treated scraping of public, logged-out pages differently from breaking into accounts (in the US, for
example, *X Corp. v. Bright Data*, N.D. Cal. 2024), but the law depends on where you are and what you do with the
data. Tweets and profiles are personal data in many places: collect only what you have a lawful reason for, and get
your own legal advice for anything beyond that.

#### Why did a line return nothing?

The run log and `RUN_STATS` say why, per line: the tweet was deleted or is from a protected account, the account is
suspended or doesn't exist, or the line needs a login (search, hashtags, followers). Every other line in the run is
unaffected, and failed lines aren't charged. If a line that should work fails with "X has probably changed its web
app", please open an issue with the input you used.

#### Can I search X or get someone's whole timeline?

Not with this actor, and not without an X login: X shows search results and timelines past the latest few posts only
to signed-in accounts. Give it tweet URLs you've collected elsewhere (a list, a spreadsheet, a monitoring tool) and it
fills in everything about them.

#### How is this different from X's official API?

X's API needs a developer account and a paid plan for any real volume, and returns its own format. This actor needs
neither: paste links, get flat JSON or CSV, pay per result. If you already have API access and need search or
streaming, use the API.

### Related actors

| Actor | Use it when |
|---|---|
| [Dataset Diff](https://apify.com/humble-echidna/dataset-diff) | You re-check the same tweets on a schedule and want what changed since the last run. |
| [Dataset Transform](https://apify.com/humble-echidna/dataset-transform) | You want a CSV with just the columns you need, deduplicated. |

### Feedback and support

Found a bug, or need a field that isn't here? Open an issue on the **Issues** tab with the input you used.

### Versions

Current version: **0.1**. See the Changelog tab for what changed in each version.

# Changelog

This Actor's version history is a separate document: https://apify.com/humble-echidna/x-scraper/changelog.md

# Actor input Schema

## `targets` (type: `array`):

One per line: a tweet URL (https://x.com/jack/status/20, twitter.com, mobile and /i/status/ links all work), a tweet id (20), a profile URL (https://x.com/NASA) or a handle (@NASA or NASA). A bare number is a tweet id. Each line returns one result. Search, hashtags, lists, followers and full timelines need a logged-in X account, which this actor never uses: those lines fail with the reason and the rest of the run goes on. Duplicates are returned once.

## `latestTweets` (type: `boolean`):

For every profile line, also return the latest tweets X shows a logged-out visitor on that profile: up to 5 original posts (no retweets or replies), each a separate tweet result with "foundVia": "latestTweets". Each tweet is charged like any result. Off by default.

## `topReplies` (type: `boolean`):

For every tweet line, also return the top replies X shows a logged-out visitor under it: up to 3, each a separate tweet result with "foundVia": "topReplies" and inReplyToId set to the tweet. Each reply is charged like any result. Off by default.

## `maxResults` (type: `integer`):

Stop after this many results in total (tweets and profiles together), e.g. 20. Minimum 1; leave empty (the default) for no limit. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first. Set it to keep a run small and quick.

## Actor input object example

```json
{
  "targets": [
    "https://x.com/NASA",
    "https://x.com/jack/status/20"
  ],
  "latestTweets": false,
  "topReplies": false
}
```

# Actor output Schema

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

No description

## `runStats` (type: `string`):

No description

# 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 = {
    "targets": [
        "https://x.com/NASA",
        "https://x.com/jack/status/20"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/x-scraper").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 = { "targets": [
        "https://x.com/NASA",
        "https://x.com/jack/status/20",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("humble-echidna/x-scraper").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 '{
  "targets": [
    "https://x.com/NASA",
    "https://x.com/jack/status/20"
  ]
}' |
apify call humble-echidna/x-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,humble-echidna/x-scraper"
        }
    }
}
```

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/9tYjc24TktNUjmN2T/builds/ZRSCTfiMo5QnYVSmA/openapi.json
