# X Quotes & Reposts Scraper 💰 From $0.10/1K | Retweeters (`ahmed_jasarevic/x-quotes-reposts-scraper`) Actor

Scrape every quote tweet and reposting account for any X post: author profile, follower counts, engagement, text and media. Runs over plain HTTP with no browser, using your own burner-account session.

- **URL**: https://apify.com/ahmed_jasarevic/x-quotes-reposts-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 1 total users, 1 monthly users, 92.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.14 / 1,000 quotes

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) Quotes & Reposts Scraper — Retweeter & Quote-Tweet Data

For any X post, collect **every quote tweet** (with author profile, engagement counts, text and media) and **every account that reposted it** (handle, display name, bio, avatar, follower counts). Runs over plain HTTP against X's GraphQL endpoints — no browser, no proxy.

### Main Use Cases

- **Engagement & amplification analysis** — see exactly who quoted and who reposted a post, with follower counts and verification status.
- **Influencer & lead discovery** — turn a viral post into a list of engaged accounts with bios and profile URLs.
- **PR / crisis monitoring** — capture quote tweets (with full text) as they appear around a launch or announcement.
- **Competitive intelligence** — measure how far a competitor's post actually spread, beyond the vanity counter.
- **Sentiment research** — export quote-tweet text and author data for NLP on how a post was received.

### How It Works

![How the X Quotes & Reposts Scraper works: one post to every quote tweet and reposting account.](https://api.apify.com/v2/key-value-stores/oeuA0kLOyhbRM4fzt/records/how-it-works-x-quotes.webp)

Both lists are **login-only** on X, so a session cookie is required — the actor reads it from Apify secrets (`X_AUTH_TOKEN` / `X_CT0`), so it never appears in the input or run record. It mints X's public `x-client-transaction-id` header from the app shell (deterministic, not a secret), then calls `TweetResultByRestId` for the source post, `SearchTimeline` with `quoted_tweet_id:<id>` for quote tweets, and `Retweeters` for the accounts that reposted. Everything is ordinary HTTPS requests — no Chrome, no Playwright.

Give it one or more post URLs or IDs, choose quotes, reposts or both, and the actor returns structured rows with author profiles, engagement counts and source timestamps.

### Collect Quote Tweets And Reposts Without The Official X API

X's official API does not expose the account list for reposts, and gates quote tweets behind expensive tiers. This actor returns both as structured JSON with author profiles and engagement counts, and does it over plain HTTP.

### Input

| Field | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `tweetUrls` | array | yes | — | X post URLs (`https://x.com/user/status/123…`) or bare numeric post IDs. Multiple posts are fine. |
| `collect` | select | no | `quotes` | `quotes`, `reposts` or `both`. Every row is tagged `kind`. |
| `maxQuotesPerPost` | integer | no | `0` | Quote tweets per post. `0` = keep going until X stops serving new ones. |
| `maxRepostsPerPost` | integer | no | `0` | Reposting accounts per post. `0` = keep going until X stops serving new ones. |

There is no `proxy` field — the actor makes ordinary HTTPS requests and does not need one. There is no cookie field either; the session is read from secrets. You state **rows**, and the actor derives pagination depth from that.

### Output

Rows are split across **two datasets**, each with its own schema; both expose the same 24 columns so a merged export still parses (`kind` tells them apart). Views: **overview** and **all**.

| `collect` | default dataset | `reposts` dataset |
| --- | --- | --- |
| `quotes` | quote rows | *(not created)* |
| `reposts` | *(empty)* | repost rows |
| `both` | quote rows | repost rows |

Both datasets share these fields: `source_tweet_id, kind, quote_tweet_id, quoted_tweet_id, url, created_at, text, screen_name, user_id, user_name, avatar_url, bio, followers_count, following_count, verified, is_blue_verified, replies_count, retweets_count, likes_count, views_count, media_count, media_urls, source, collected_at`.

Notes: for **repost** rows the post-specific columns (`created_at`, `text`, engagement counts) are `null` — a native repost has no post of its own — and `url` points at the retweeter's profile. A run **summary** (collected counts vs X's reported counts, coverage %, stop condition, billed events, errors) is stored as the key-value store item `OUTPUT`.

### Coverage

Coverage is **not** 100%, and the reason is on X's side: X hides quote tweets and reposts from deleted, suspended and protected accounts. The `/quotes` tab is backed by X's search index, which only exposes a slice of very large threads (a post reporting 1,582 quotes typically yields a few hundred rows, ~15–40%). The repost list is a **direct query, not routed through search**, so it lands much closer to the reported count — the reference run collected **1,584 of 1,744 reposts (~91%)**, ending on X's own last page rather than on a cap. `OUTPUT.coverage_pct` and `OUTPUT.reposts_coverage_pct` always report the real ratio; both go to `null` when a configured limit or a spending limit ended the list, and `stopped_by` / `reposts_stopped_by` say which case occurred.

### Example Input

```json
{ "tweetUrls": ["https://x.com/OpenAI/status/2065225362544726371"], "collect": "both" }
```

Quote tweets only, capped:

```json
{ "tweetUrls": ["2065225362544726371"], "collect": "quotes", "maxQuotesPerPost": 200 }
```

### Example Output

`kind: "quote"` (default dataset):

```json
{
  "source_tweet_id": "2065225362544726371",
  "kind": "quote",
  "quote_tweet_id": "2065231677539176538",
  "quoted_tweet_id": "2065225362544726371",
  "url": "https://x.com/user/status/2065231677539176538",
  "created_at": "Fri Jun 12 00:36:16 +0000 2026",
  "text": "Brilliant move by @OpenAI",
  "screen_name": "example_user",
  "user_id": "1416365362505863169",
  "user_name": "Example User",
  "bio": "Building things on the internet.",
  "followers_count": 670,
  "following_count": 2206,
  "verified": false,
  "is_blue_verified": true,
  "replies_count": 0,
  "retweets_count": 2,
  "likes_count": 3,
  "views_count": 133,
  "media_count": 0,
  "media_urls": [],
  "source": "http",
  "collected_at": "2026-10-14T12:57:41.624Z"
}
```

`kind: "repost"` (reposts dataset):

```json
{
  "source_tweet_id": "2065225362544726371",
  "kind": "repost",
  "quote_tweet_id": null,
  "quoted_tweet_id": null,
  "url": "https://x.com/example_user",
  "created_at": null,
  "text": null,
  "screen_name": "example_user",
  "user_id": "1358024651956498433",
  "user_name": "Example User",
  "followers_count": 52,
  "following_count": 733,
  "replies_count": null,
  "retweets_count": null,
  "likes_count": null,
  "views_count": null,
  "media_count": 0,
  "media_urls": [],
  "source": "http",
  "collected_at": "2026-10-14T12:57:49.648Z"
}
```

### Setup

Store the session **once** with the Apify CLI, using a throwaway ("burner") account:

```bash
apify secrets add X_AUTH_TOKEN "<auth_token value>" --force
apify secrets add X_CT0 "<ct0 value>" --force
apify push
```

`.actor/actor.json` maps these secrets to the `X_AUTH_TOKEN` / `X_CT0` env vars, and the actor reads **only** those env vars — there is no cookie field in the input, so a run's `INPUT` never stores the session. Rotate by re-running the two commands and `apify push`. The run log prints the logged-in handle (`Session OK: posting as @…`). Use a burner account: this actor automates a logged-in session, and if an account ever gets a challenge, log in once manually in a normal browser to clear it.

### Integrations & Automation

- **Apify API** — trigger runs and read both datasets programmatically.
- **Webhooks** — act when a run finishes (e.g. push new quote tweets into a CRM or Slack).
- **Zapier / Make** — pipe engaged accounts into Google Sheets, Airtable or an outreach tool.

**Recommended schedule:** run **hourly/daily** for launch, PR or crisis monitoring (new quotes appear fast); **weekly** for competitive amplification reports.

### Related Actors

- [Tweet Scraper](https://apify.com/apidojo/tweet-scraper) — the largest X scraper in the cluster; the broad actor to link to from this narrower one.
- [X Quote Tweets Scraper](https://apify.com/seemuapps/x-quote-tweets-scraper) — sibling actor focused on quote tweets only.
- [X Tweet Engagement Scraper](https://apify.com/khadinakbar/x-tweet-engagement-scraper) — sibling actor for engagement metrics.
- [X Tweet Scraper Lite](https://apify.com/apidojo/twitter-scraper-lite) — lightweight X scraper for quick tweet lookups.
- [X Tweet Scraper](https://apify.com/xquik/x-tweet-scraper) — another broad X scraper for the same topic cluster.

### FAQ

#### Does this use the official X / Twitter API?

No. It calls X's own GraphQL endpoints over plain HTTPS — the same ones the x.com website uses — so there is no API key, no official tier limits and no per-call API fee.

#### Why is a session cookie required?

Both quote tweets and the repost list are **login-only** on X. The actor reads `auth_token` / `ct0` from Apify secrets, so the session never appears in the input or the run record.

#### Do I need a browser or a residential proxy?

No. It runs as pure HTTPS requests (no Chrome, no Playwright) and does not need a proxy.

#### Why is coverage below 100%?

X hides quote tweets and reposts from deleted, suspended and protected accounts, and its search index (which backs the quotes tab) only exposes a slice of very large threads. Quote tweets typically land ~15–40% of the reported count; the repost list is a direct query and reached ~91% in the reference run. `OUTPUT` always reports the real coverage.

#### What are alternatives to this actor or to X data?

The official X API is the main alternative for tweets themselves, but it does not expose the reposting-account list. Within the Apify Store, [Tweet Scraper](https://apify.com/apidojo/tweet-scraper) is the broadest X scraper for general tweet and profile data.

#### How much does a run cost?

Pay-per-event: **$0.15 per 1,000 quote tweets**, **$0.10 per 1,000 reposts**, plus a one-off ~$0.00005 start charge. The reference run (331 quotes + 1,584 reposts) cost about **$0.21**.

#### Is there a free trial?

Yes — a `minimalMaxTotalChargeUsd` of $0.01 makes a trial run possible without allowing a run to be capped below one result.

### For AI Agents & LLM Apps

Purpose: return the quote tweets and/or the reposting accounts for one or more X posts as flat JSON records with author profile, engagement counts and media.

Minimal working input:

```json
{ "tweetUrls": ["https://x.com/OpenAI/status/2065225362544726371"] }
```

Variant inputs:

```json
{ "tweetUrls": ["2065225362544726371"], "collect": "reposts", "maxRepostsPerPost": 500 }
```

```json
{ "tweetUrls": ["https://x.com/OpenAI/status/2065225362544726371"], "collect": "both" }
```

Output fields (both datasets, 24 columns): `source_tweet_id, kind, quote_tweet_id, quoted_tweet_id, url, created_at, text, screen_name, user_id, user_name, avatar_url, bio, followers_count, following_count, verified, is_blue_verified, replies_count, retweets_count, likes_count, views_count, media_count, media_urls, source, collected_at`. Run summary is in the key-value store item `OUTPUT`.

Behaviors an agent should know:

- `tweetUrls` is the **only required** field and accepts full URLs **or** bare numeric post IDs, including multiple posts per run.
- There is **no cookie field**: the session must already exist as Apify secrets `X_AUTH_TOKEN` and `X_CT0`, or the run will fail at the session step.
- `collect` chooses which dataset fills — `quotes` → default dataset, `reposts` → the `reposts` dataset alias, `both` → both. Every row is tagged `kind`.
- `0` on `maxQuotesPerPost` / `maxRepostsPerPost` means "until X stops serving new ones" — the default and the deepest collect; a non-zero value caps rows and nulls `coverage_pct`.
- On **repost** rows, the post-specific columns (`created_at`, `text`, engagement counts) are `null` and `url` is the retweeter's profile, not a post permalink.
- Billing model: pay-per-event — `quote_tweet` ~$0.00015 per row (~$0.15/1K) and `repost` ~$0.0001 per row (~$0.10/1K), plus one `apify-actor-start` charge per run. Pages are billed before they are stored, so a run stopped by a spending limit keeps every row it already paid for.

### Legal & Compliance Disclaimer

This actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by X Corp.** It reads publicly visible post data plus login-only lists **through your own X session** (the `auth_token` / `ct0` cookies you supply) — it is not a login bypass and holds no credentials of its own. It does not scrape data behind a login the user does not already hold, and does not solve CAPTCHAs. Use a throwaway account, and note X's Terms of Service and automation rules apply to your session. The output includes personal data (handles, display names, bios, avatars, follower counts); users are responsible for their own compliance with applicable data-protection law (including GDPR/CCPA where the data concerns EU or California individuals) and should not use it for unsolicited commercial outreach in violation of applicable law.

### SEO Keywords

x quotes scraper, twitter retweeters scraper, quote tweet scraper, retweeter list, who retweeted my tweet, twitter engagement data, x api alternative, twitter quote tweets data, repost scraper, x retweeter data, twitter influencer discovery, x post amplification, quote tweet export, twitter sentiment analysis data, x scraper no browser, retweeter dataset, twitter lead generation, x quote tweets scraper, twitter competitor analysis, x engagement scraper, twitter scraper without api

# Actor input Schema

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

One or more X post URLs (https://x.com/user/status/123...) or numeric post IDs. Whatever the 'collect' mode selects is gathered for each post.

## `collect` (type: `string`):

quotes = quote tweets only (default dataset). reposts = every account that reposted the post ('reposts' dataset). both = both lists, each in its own dataset. Every row is tagged kind='quote' or kind='repost'.

## `maxQuotesPerPost` (type: `integer`):

How many quote tweets to collect per post. 0 = keep going until X stops serving new ones (the usual choice). Pagination depth is derived from this, so you never have to think in pages.

## `maxRepostsPerPost` (type: `integer`):

How many accounts that reposted to collect per post. 0 = keep going until X stops serving new ones (the usual choice). Pagination depth is derived from this, so you never have to think in pages.

## Actor input object example

```json
{
  "tweetUrls": [
    "https://x.com/OpenAI/status/2065225362544726371"
  ],
  "collect": "quotes",
  "maxQuotesPerPost": 0,
  "maxRepostsPerPost": 0
}
```

# Actor output Schema

## `quotes` (type: `string`):

One row per quote post collected for the source post (kind='quote'). Default dataset.

## `reposts` (type: `string`):

One row per account that reposted the source post (kind='repost'). Separate 'reposts' dataset.

## `summary` (type: `string`):

JSON run summary: rows collected, coverage_pct vs reposts_coverage_pct, stopped_by vs reposts_stopped_by, charged events, errors.

# 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 = {
    "tweetUrls": [
        "https://x.com/OpenAI/status/2065225362544726371"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/x-quotes-reposts-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 = { "tweetUrls": ["https://x.com/OpenAI/status/2065225362544726371"] }

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/x-quotes-reposts-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 '{
  "tweetUrls": [
    "https://x.com/OpenAI/status/2065225362544726371"
  ]
}' |
apify call ahmed_jasarevic/x-quotes-reposts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/x-quotes-reposts-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/9Lxu6iV8fiINNqgQK/builds/MfWWeYwklEaIxTfB8/openapi.json
