# X Twitter Scraper - Date Filter That Works, Replies & Quotes (`ntriqpro/x-twitter-deep-scraper`) Actor

X (Twitter) scraper with a date filter that holds: exact since/until windows, tweets outside the window dropped and counted. Full reply threads, quote tweets, followers, author profiles, topic, influencer, sentiment, trend and bot-signal rows. No-login profile mode. JSON, CSV, Excel.

- **URL**: https://apify.com/ntriqpro/x-twitter-deep-scraper.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 tweet, reply or quote rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper - Date Filter That Works, Replies & Quotes

**Every tweet is checked against your date window, and the ones outside it are dropped and counted, never billed. A search that finds nothing costs one query event and no tweet rows. Reply threads and quote tweets come back as one row each.**

**Zero results cost $0.01, nothing more.** A search term, profile or tweet URL that returns no tweets is charged only its `query-completed` event ($0.01). Tweet-row charges for it: 0.

This Actor is not affiliated with, endorsed by, or sponsored by X Corp. "X" and "Twitter" only describe what it works with.

### Free plan vs paid plans

| | Free Apify plan (a preview) | Any paid Apify plan |
|---|---|---|
| Tweets per run | Up to 25 | Up to your maximums (20,000 per search term, 3,200 per profile) |
| Search (needs your own X cookies) | Locked: search with your own X session is a paid feature. Pasted cookies are ignored, so your X account is never used | Every page of every search term, exact date window, Latest or Top |
| Profiles and tweet URLs | Yes, within the 25 tweets | Full timelines |
| Reply threads, quote tweets, followers | Locked | Yes, one row each |
| Analysis rows | One preview row per type (5 types), marked `preview: true` with `total` | Every row of every type |
| Author profile rows | Up to 5 | One per unique author |

Every free run ends as SUCCEEDED with a `free-plan-preview` notice row and status message that count what a paid run would have added for the same input, for example: *"Free preview: 25 tweets and 5 analysis rows (5 of 62). Paid plans get ~2,400 more tweets, 316 replies, 74 quotes and 57 more analysis rows for this input."* The tweet estimate comes from X's own numbers: the profile's tweet count, or whether the timeline offered another page. If you entered search terms, the notice also says *"Search with your own X session is a paid feature"* and how many terms were locked. A free run with only search terms ends right away with that notice and is not charged. Preview analysis rows and notice rows are never charged.

### What you get

| Input | Without login | With your own X session cookies |
|---|---|---|
| Profiles (`NASA`, `@NASA`, profile URL) | Profile row + timeline tweets | + reply threads, quote tweets, followers |
| Tweet URLs | The tweet | + its reply thread and quote tweets |
| Search terms (any X search syntax) | Not available: X only answers search to signed-in sessions | Search with an exact date window, Latest or Top (paid plans) |

Every run also adds one **profile row per unique author** (followers, following, account age, verification, bio) taken from tweets already fetched, so no extra requests. Each query also gets **analysis rows** (free plan: one preview row per type):

- **Topic clusters**: the hashtags, words and link domains that group the conversation, with size, engagement and top tweet.
- **Influencer ranking**: authors ranked by engagement in your sample, with engagement per follower.
- **Complaint and sentiment themes**: refunds, bugs, outages, support, price, cancellations, scams and praise, each with sample tweets (English word lists).
- **Time trend**: hourly or daily buckets with engagement and spike flags.
- **Bot-suspicion signals**: new accounts, extreme posting rates, default avatars, copy-paste text. A signal, not a verdict.

### How the date window works

1. `since` (inclusive) and `until` (exclusive) are turned into exact UTC timestamps and sent to X as `since_time:` / `until_time:`. X's plain `since:` / `until:` date operators are read in a time zone you do not control; in our test only 4.3% of the results they returned fell inside the UTC window.
2. Every top-level tweet is checked again on its own `createdAt`. Tweets outside the window are dropped before delivery.
3. The **query-summary** row says what happened: window, `receivedFromX`, `droppedOutsideWindow`, `droppedByFilters`, `duplicatesSkipped`, delivered counts, and `stoppedBy`.

Quoted or retweeted originals inside a tweet are kept as fields (`quotedTweet`, `retweetedTweet`) and never used for the window check. Reply and quote rows belong to in-window tweets; their own dates can be later, and `insideWindow` shows it on every row.

### Common complaints about tweet scrapers, and what this Actor does

| Complaint pattern in public reviews (paraphrased) | Here |
|---|---|
| Date filter ignored, results outside the requested range | Exact epoch window + a second check per tweet; drops are published on the summary row |
| Charged for "noResults" or demo rows | A zero-result query is one `query-completed` event. No placeholder rows. Notice rows are never billed |
| The free-plan price is about 100 times the listed price | One price on every plan. The free plan is a capped taste: same price, fewer rows |
| Asked for 1,000 results, got 100 | Pages are walked until your maximum, the end of results, or X's limit; `stoppedBy` tells you which |

### Pricing (pay per event, same price on every plan)

| Event | Price | When |
|---|---|---|
| `run-started` | $0.005 | Once per run, after the X web client started. Not charged if it could not start |
| `query-completed` | $0.01 | Once per search term, profile or tweet URL whose lookup completed, including zero results or a missing profile |
| `tweet-row` | $0.0004 | Per unique tweet, reply or quote row delivered |
| `profile-row` | $0.002 | Per profile or follower row delivered |
| `analysis-row` | $0.02 | Per analysis row delivered on paid plans. Free-plan preview rows are not charged |

Example: one search term, 500 tweets, 10 threads with 400 replies and quotes, 150 author profiles, 20 analysis rows costs about $0.005 + $0.01 + 900 × $0.0004 + 150 × $0.002 + 20 × $0.02 = **$1.08**. Set "Maximum cost per run" in Apify to cap any run; the Actor stops cleanly at that cap.

### Free plan

Up to **25 tweet rows** and **5 profile rows** per run from profiles and tweet URLs, which work without login. Search, reply threads, quote tweets and followers are locked, and pasted cookies are ignored, so a free run never acts as your X account. Each analysis type shows its first row as a preview (`preview: true`, `shown: 1`, `total`: rows a paid run gets), not charged. The run ends as SUCCEEDED with a `free-plan-preview` notice row and status message listing what a paid run would have added: estimated more tweets, the reply and quote counts on the tweets you got, locked analysis rows and, with *"Search with your own X session is a paid feature"*, the number of locked search terms.

### Your X session cookies

X answers search, reply threads, quotes and followers only to signed-in sessions. On a paid plan, paste **your own** x.com cookies into `cookies`: a cookie-editor JSON export, or a header like `auth_token=…; ct0=…` (both are required). The free plan ignores cookies.

- **The run acts as your X account.** You accept the risk of X limiting or locking that account, and you are responsible for complying with X's terms.
- Requests follow X's own per-account limits (search: 50 requests per 15 minutes). When a limit is reached the run waits for the reset (up to `maxRateLimitWaitSeconds`) or skips that step with a notice row.
- A query that hits the search limit keeps its partial results and a reason row (`stoppedBy: "rate-limit"` on its query-summary row), and the run still ends as SUCCEEDED.
- The value is stored encrypted by Apify (secret input) and is never written to the log, the dataset or the key-value store.

### Input example

```json
{
  "searchTerms": ["\"customer support\" (acme OR acmeapp) -filter:retweets"],
  "since": "2026-09-01",
  "until": "2026-09-08",
  "searchMode": "Latest",
  "maxTweetsPerQuery": 500,
  "threadsPerQuery": 10,
  "maxRepliesPerTweet": 100,
  "minLikes": 2,
  "cookies": "auth_token=…; ct0=…"
}
```

### Output example (tweet row)

```json
{
  "rowType": "tweet",
  "id": "2102882545427714266",
  "url": "https://x.com/NASA/status/2102882545427714266",
  "text": "Welcome to the Artemis Accords, Croatia 🇭🇷 …",
  "createdAt": "2026-09-23T22:07:23.000Z",
  "lang": "en",
  "conversationId": "2102882545427714266",
  "isReply": false,
  "isQuote": false,
  "isRetweet": false,
  "likeCount": 2219,
  "replyCount": 57,
  "retweetCount": 227,
  "quoteCount": 29,
  "bookmarkCount": 167,
  "viewCount": 291797,
  "totalEngagement": 2699,
  "urls": ["https://go.nasa.gov/3VcvAQn"],
  "media": [{ "type": "photo", "url": "https://pbs.twimg.com/media/HS7wh0rXMAA61bq.jpg", "width": 2000, "height": 1125 }],
  "author": { "userName": "NASA", "followers": 92373166, "isBlueVerified": true, "verifiedType": "Government" },
  "matchedQuery": "@NASA",
  "insideWindow": null,
  "collectedAt": "2026-09-24T01:20:00.000Z"
}
```

Reply rows add `parentTweetId`, `threadRootId` and `isDirectReply`; quote rows add `parentTweetId`. Row types: `tweet`, `reply`, `quote`, `profile`, `follower`, `topic-cluster`, `influencer-rank`, `sentiment-theme`, `time-trend`, `bot-signal`, `query-summary`, `notice`. The dataset has table views for tweets, profiles, summaries and analysis. Export as JSON, CSV or Excel.

### Responsible use

The data is public X content collected at your request. You are responsible for lawful use of it (GDPR, CCPA and other law), for the terms of the sites involved, and for any use of your own X session. Do not use it for harassment, stalking or spam.

### Leave a review

If this Actor saved you time, a short review on its Apify Store page helps other people find it. If something breaks, open an issue on the Actor's Issues tab and include the run ID; we fix scrapers quickly when X changes.

# Actor input Schema

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

X search queries, one per line. Any X search syntax works (from:, to:, filter:links, -filter:replies, lang:, #hashtag, "exact phrase"). Date operators are replaced by the exact window below. Needs your session cookies. Paid plans only: search with your own X session is a paid feature, and a free run counts the locked terms in its notice row.

## `profiles` (type: `array`):

X handles or profile URLs (NASA, @NASA, https://x.com/NASA). Returns the profile row and its timeline. Works without cookies.

## `tweetUrls` (type: `array`):

Tweet URLs or ids. Returns the tweet; with your cookies also its reply thread and quote tweets.

## `since` (type: `string`):

Start of the date window: YYYY-MM-DD, an ISO date-time, or relative like 7d / 24h. Tweets created before it are dropped and counted on the query-summary row.

## `until` (type: `string`):

End of the date window, exclusive (same as X's own until:). YYYY-MM-DD, ISO date-time or relative. Tweets created at or after it are dropped and counted.

## `evenlyAcrossDays` (type: `boolean`):

Split a multi-day window into day slices and collect an equal share from each day, instead of only the newest tweets.

## `searchMode` (type: `string`):

Latest walks the window newest to oldest. Top follows X's ranking.

## `maxTweetsPerQuery` (type: `integer`):

Top-level tweets per search term inside the window (replies and quotes are counted separately).

## `maxTweetsPerProfile` (type: `integer`):

Timeline tweets per profile (including the profile's retweets, marked isRetweet).

## `includeReplies` (type: `boolean`):

For the most-replied tweets of each query, collect the reply thread (one row per reply). Needs cookies.

## `includeQuotes` (type: `boolean`):

For the most-quoted tweets of each query, collect the quote tweets (one row per quote). Needs cookies; uses the search limit.

## `threadsPerQuery` (type: `integer`):

How many of the most-discussed tweets per query get their replies and quotes collected.

## `maxRepliesPerTweet` (type: `integer`):

Reply rows per expanded tweet.

## `maxQuotesPerTweet` (type: `integer`):

Quote rows per expanded tweet.

## `includeAuthorProfiles` (type: `boolean`):

One profile row per unique author (followers, following, account age, verification, bio). Taken from the tweets already fetched: no extra requests.

## `maxFollowersPerProfile` (type: `integer`):

Follower rows for each input profile (0 = off). Needs cookies.

## `includeAnalysis` (type: `boolean`):

Per query: topic clusters, influencer ranking, complaint and sentiment themes, time trend with spikes, bot-suspicion signals. Free plan: the first row of each type as an uncharged preview.

## `analysisLayers` (type: `array`):

Leave empty for all five.

## `minLikes` (type: `integer`):

Keep tweets with at least this many likes (sent to X as min\_faves: and checked again).

## `minRetweets` (type: `integer`):

Keep tweets with at least this many retweets.

## `minReplies` (type: `integer`):

Keep tweets with at least this many replies.

## `language` (type: `string`):

Two-letter language code (en, es, ja). Sent to X as lang: and checked again.

## `cookies` (type: `string`):

Search, reply threads, quote tweets and followers only answer signed-in sessions. Paste YOUR OWN x.com cookies (a cookie-editor JSON export, or an 'auth\_token=…; ct0=…' header; auth\_token and ct0 are required). The run acts as your X account: you accept the risk of X limiting or locking that account and you are responsible for complying with X's terms. We pace requests to X's own limits (search: 50 per 15 minutes per account). The value is stored encrypted and never written to the log, dataset or key-value store. Paid plans only: the free plan ignores cookies, so a free run never acts as your X account.

## `maxRateLimitWaitSeconds` (type: `integer`):

When X's per-account limit is reached, wait up to this long for the 15-minute window to reset. Beyond it the run skips the limited step and says so in a notice row.

## `proxyConfiguration` (type: `object`):

Optional. Without a proxy the run connects directly; with your cookies, a residential proxy close to your usual location looks most like your own browser.

## Actor input object example

```json
{
  "searchTerms": [],
  "profiles": [
    "NASA"
  ],
  "tweetUrls": [],
  "evenlyAcrossDays": false,
  "searchMode": "Latest",
  "maxTweetsPerQuery": 200,
  "maxTweetsPerProfile": 100,
  "includeReplies": true,
  "includeQuotes": true,
  "threadsPerQuery": 10,
  "maxRepliesPerTweet": 100,
  "maxQuotesPerTweet": 40,
  "includeAuthorProfiles": true,
  "maxFollowersPerProfile": 0,
  "includeAnalysis": true,
  "minLikes": 0,
  "minRetweets": 0,
  "minReplies": 0,
  "maxRateLimitWaitSeconds": 960,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (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": [],
    "profiles": [
        "NASA"
    ],
    "tweetUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/x-twitter-deep-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": [],
    "profiles": ["NASA"],
    "tweetUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/x-twitter-deep-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": [],
  "profiles": [
    "NASA"
  ],
  "tweetUrls": []
}' |
apify call ntriqpro/x-twitter-deep-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ntriqpro/x-twitter-deep-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/zKsDZGXfgRZMKvmoY/builds/DI4Z7ElKQ3s6o2Nua/openapi.json
