# Tweet Scraper: X (Twitter) Search, Profiles, Lists & Replies (`winterlock/x-tweet-scraper`) Actor

Scrape tweets from X (Twitter) by search term, advanced search filters, handle, list, tweet URL or conversation (replies). Clean JSON with full author profiles, media and engagement counts. $0.34 per 1,000 tweets, no minimum per query.

- **URL**: https://apify.com/winterlock/x-tweet-scraper.md
- **Developed by:** [Alex Burton](https://apify.com/winterlock) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.34 / 1,000 tweets

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

## Tweet Scraper: X (Twitter) Search, Profiles, Lists & Replies

Scrape tweets from X (formerly Twitter) without an X account, API key or proxy. Give it search terms, X advanced-search filters, handles, list URLs, tweet URLs or conversation IDs, and get clean JSON with each tweet's text, engagement counts, media, quoted and retweeted tweets, and the author's full public profile.

**$0.34 per 1,000 tweets.** Platform usage is included. There is no start fee and no minimum per query, so a 5-tweet lookup costs a fraction of a cent.

### What you can scrape

| Input | What you get |
|---|---|
| **Search terms** | Tweets matching anything you can type into X search, including operators like `from:`, `#hashtag`, `lang:`, `min_faves:`, `filter:links`. |
| **X handles** | Tweets posted by those accounts. Filters and dates apply. |
| **Profile URLs** | An account's timeline (`x.com/name`), its tweets and replies (`/with_replies`) or its media (`/media`). |
| **Search URLs** | Paste a search from your browser, for example `x.com/search?q=apify&f=live`. |
| **List URLs** | Tweets from an X list (`x.com/i/lists/123`). |
| **Tweet URLs** | That single tweet, with full details. |
| **Conversation IDs** | A whole conversation: the tweet, the tweets above it, and all its replies. |

### Filters

These are added to every search term, handle and search URL:

- **Sort:** Latest, Top, or Latest + Top. With Latest + Top, duplicates are removed before you are charged.
- **Language:** ISO 639-1 code (`en`, `es`, `ja`, ...).
- **Only:** verified users, blue-check users, tweets with images, tweets with videos, quote tweets.
- **From, in reply to, mentioning:** a handle.
- **Location:** near a place, within a radius, by geocode or by X place ID. X's own location search only matches the few tweets that carry location data, so these filters often return little or nothing.
- **Engagement:** minimum retweets, likes or replies.
- **Dates:** since and until (`YYYY-MM-DD` or `YYYY-MM-DD_HH:MM:SS_UTC`).

### Example input

```json
{
  "searchTerms": ["web scraping", "#ai lang:en"],
  "twitterHandles": ["apify"],
  "sort": "Latest",
  "minimumFavorites": 5,
  "start": "2026-01-01",
  "maxItems": 500
}
```

### Example output

```json
{
  "type": "tweet",
  "id": "1980000000000000000",
  "url": "https://x.com/apify/status/1980000000000000000",
  "twitterUrl": "https://twitter.com/apify/status/1980000000000000000",
  "text": "New in Apify Store ...",
  "retweetCount": 12,
  "replyCount": 3,
  "likeCount": 87,
  "quoteCount": 1,
  "bookmarkCount": 9,
  "viewCount": 10432,
  "createdAt": "Wed Oct 22 14:01:48 +0000 2026",
  "createdAtIso": "2026-10-22T14:01:48.000Z",
  "lang": "en",
  "source": "Twitter Web App",
  "isReply": false,
  "inReplyToId": null,
  "conversationId": "1980000000000000000",
  "isRetweet": false,
  "isQuote": false,
  "quoteId": null,
  "isPinned": false,
  "author": {
    "type": "user",
    "userName": "apify",
    "name": "Apify",
    "id": "1234567",
    "url": "https://x.com/apify",
    "isBlueVerified": true,
    "verifiedType": "Business",
    "profilePicture": "https://pbs.twimg.com/...",
    "description": "...",
    "location": "Prague",
    "followers": 21000,
    "following": 300,
    "statusesCount": 5400,
    "createdAt": "Mon Jun 01 10:00:00 +0000 2015"
  },
  "media": [{ "type": "photo", "url": "https://pbs.twimg.com/...", "width": 1200, "height": 675 }],
  "entities": { "hashtags": ["webscraping"], "urls": ["https://apify.com"], "mentions": [] },
  "quote": null,
  "retweet": null,
  "article": null
}
```

Quoted and retweeted tweets are included in full under `quote` and `retweet`. `viewCount` is `null` when X hides it. The dataset has two views: **Tweets** and **Authors**.

### Pricing

You pay **$0.34 per 1,000 tweets** saved to the dataset. Nothing else is charged:

- Duplicates (from Latest + Top, or the same tweet found by two inputs) are removed before charging.
- Empty searches cost nothing.
- Set **Maximum cost per run** in the run options and the scraper stops exactly there.

| Tweets | Cost |
|---|---|
| 100 | $0.034 |
| 1,000 | $0.34 |
| 10,000 | $3.40 |
| 100,000 | $34 |

### Speed

Large "Latest" searches are split into time slices that are fetched in parallel, typically 20 to 30 tweets per second, and the results still arrive as one continuous newest-first stretch. A run capped with `maxItems` gets exactly the newest tweets, with no gaps.

### Tips

- **Few results?** Try sort **Top**, widen the dates, or check the query on x.com/search first. X search does not return every tweet ever posted. Some tweets are hidden by X's own filtering.
- **Large jobs:** one search term can return many thousands of tweets. Use `maxItems` or the maximum cost per run to cap it.
- **Only some fields?** Ask the dataset for them when you download: add `?fields=id,text,likeCount` (or `omit=`) to the dataset items URL, or pick the columns in the Console's export dialog. Nothing extra is charged.
- **API:** run it from your code with the Apify API or client, and read the results from the run's dataset.

### Limitations

- Only public tweets. Protected accounts and deleted tweets can't be scraped.
- Counts and search results are what X shows at the moment of the run.
- Use the data responsibly and in line with the laws that apply to you, especially for personal data.

# Actor input Schema

## `startUrls` (type: `array`):

Tweet, profile, search or list URLs, e.g. https://x.com/apify, https://x.com/search?q=apify\&f=live, https://x.com/i/lists/123, https://x.com/apify/status/123. Search URLs keep their own tab (f=live is Latest).

## `searchTerms` (type: `array`):

Anything you would type into X search, including advanced operators, e.g. "web scraping", "scraping from:apify", "#ai lang:en". The filters below are added to every term.

## `twitterHandles` (type: `array`):

Get tweets posted by these accounts (searched as from:handle, so the filters and dates below apply).

## `conversationIds` (type: `array`):

Tweet IDs or tweet URLs whose whole conversation you want: the tweet, the tweets above it and its replies.

## `maxItems` (type: `integer`):

Stop after this many tweets in total across all inputs. Leave empty for no limit (your maximum cost per run still applies).

## `sort` (type: `string`):

Which search tab to read. Use Top if Latest returns few tweets. Latest + Top reads both and removes duplicates.

## `tweetLanguage` (type: `string`):

Only tweets in this language (ISO 639-1 code, e.g. en, es, ja).

## `onlyVerifiedUsers` (type: `boolean`):

Only tweets from verified accounts.

## `onlyTwitterBlue` (type: `boolean`):

Only tweets from accounts with a blue check.

## `onlyImage` (type: `boolean`):

Only tweets that contain images.

## `onlyVideo` (type: `boolean`):

Only tweets that contain videos.

## `onlyQuote` (type: `boolean`):

Only tweets that quote another tweet.

## `author` (type: `string`):

Only tweets sent by this handle.

## `inReplyTo` (type: `string`):

Only tweets replying to this handle.

## `mentioning` (type: `string`):

Only tweets mentioning this handle.

## `geotaggedNear` (type: `string`):

Only tweets sent near this place, e.g. Los Angeles. X's location search only matches tweets that carry location data, so expect few results.

## `withinRadius` (type: `string`):

Radius around 'Geotagged near', e.g. 15km or 10mi.

## `geocode` (type: `string`):

Only tweets from users within a radius of a point: latitude,longitude,radius, e.g. 37.7764685,-122.4172004,10km. X's location search only matches tweets that carry location data, so expect few results.

## `placeObjectId` (type: `string`):

Only tweets tagged with this X place ID, e.g. 96683cc9126741d1.

## `minimumRetweets` (type: `integer`):

Only tweets with at least this many retweets.

## `minimumFavorites` (type: `integer`):

Only tweets with at least this many likes.

## `minimumReplies` (type: `integer`):

Only tweets with at least this many replies.

## `start` (type: `string`):

Only tweets on or after this date: YYYY-MM-DD, or YYYY-MM-DD_HH:MM:SS_UTC.

## `end` (type: `string`):

Only tweets before this date: YYYY-MM-DD, or YYYY-MM-DD_HH:MM:SS_UTC.

## `includeSearchTerms` (type: `boolean`):

Add a searchTerm field to each tweet saying which input found it.

## Actor input object example

```json
{
  "searchTerms": [
    "web scraping"
  ],
  "twitterHandles": [],
  "maxItems": 100,
  "sort": "Latest"
}
```

# Actor output Schema

## `tweets` (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 = {
    "searchTerms": [
        "web scraping"
    ],
    "twitterHandles": [],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("winterlock/x-tweet-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 = {
    "searchTerms": ["web scraping"],
    "twitterHandles": [],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("winterlock/x-tweet-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 '{
  "searchTerms": [
    "web scraping"
  ],
  "twitterHandles": [],
  "maxItems": 100
}' |
apify call winterlock/x-tweet-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,winterlock/x-tweet-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/GSzgBJtXDiCTgyrc1/builds/q09Iu6WFTPUlGyTuo/openapi.json
