# X (Twitter) Competitor Benchmark (`guezi/x-competitor-benchmark`) Actor

Compare 2-50 X (Twitter) accounts over the same period and see who is winning. Every row carries posts, likes, reposts, replies, views, engagement per post, share of voice and its rank in the group, plus a summary row naming the winner in each category. No X account or login needed.

- **URL**: https://apify.com/guezi/x-competitor-benchmark.md
- **Developed by:** [Kawtar](https://apify.com/guezi) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 account benchmarkeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## X (Twitter) Competitor Benchmark

Put two to fifty X (Twitter) accounts side by side over exactly the same period
and find out who is actually winning. Every account comes back with its own
numbers — posts published, likes, reposts, replies, views, engagement per post,
posting frequency — **and** its position in the group: its rank in each
category, its share of voice, its share of engagement, and how far above or
below the group average it sits. One extra summary row gives you the headline
without any spreadsheet work. No X account, no login, no browser extension.

The unit of value here is the comparison. A single account tells you a number;
a group tells you whether that number is good.

### What you can do with it

- **Settle the "how are we doing?" question.** Put your account in the list with
  four competitors and see, in one table, whether you are out-publishing them,
  out-engaging them, or quietly losing share of the conversation.
- **Build a monthly competitive report.** Schedule it on the first of the month
  over the same group and drop the summary row straight into your deck.
- **Find who punches above their weight.** Engagement rate is ranked separately
  from raw engagement, so a small account that gets more out of its audience
  than a giant one is easy to spot.
- **Pick the accounts worth studying.** The standout post of each account, and
  of the whole group, comes back with the row — so you can read what actually
  worked rather than guessing.
- **Brief a pitch or an audit.** Show a prospect exactly where they sit against
  the accounts they told you they compete with, over a period you choose.
- **Track share of voice over time.** Run it weekly and watch whether your slice
  of the group's posting and engagement is growing or shrinking.

### What you get

One row per account, in leaderboard order, each carrying both its own metrics
and its standing in the group (abridged):

```json
{
  "recordType": "account",
  "inputProfile": "nasa",
  "username": "NASA",
  "displayName": "NASA",
  "profileUrl": "https://x.com/NASA",
  "followers": 92245565,
  "following": 119,
  "totalPostsAllTime": 74298,
  "hasSubscriptionBadge": true,
  "windowFrom": "2026-07-06T21:48:10.080Z",
  "windowTo": "2026-08-05T21:48:10.080Z",
  "windowDays": 30,
  "posts": 39,
  "postsPerWeek": 9.1,
  "postsCapped": false,
  "totalLikes": 159579,
  "totalReposts": 33502,
  "totalReplies": 9440,
  "totalQuotes": 2238,
  "totalBookmarks": 8655,
  "totalEngagement": 204759,
  "totalViews": 48126201,
  "avgLikesPerPost": 4091.8,
  "avgEngagementPerPost": 5250.2,
  "avgViewsPerPost": 1234005.2,
  "engagementRate": 0.0057,
  "postsWithMedia": 26,
  "accountsCompared": 3,
  "rankByFollowers": 1,
  "rankByPosts": 2,
  "rankByTotalViews": 2,
  "rankByAvgEngagement": 2,
  "rankByEngagementRate": 3,
  "shareOfVoicePct": 24.53,
  "shareOfEngagementPct": 14.12,
  "shareOfViewsPct": 19.69,
  "vsSetAvgEngagementPct": -42.4,
  "isMostActive": false,
  "isMostEngaging": false,
  "topPost": {
    "url": "https://x.com/NASA/status/2079279133712957706",
    "text": "On July 20, 1969, Neil Armstrong and Buzz Aldrin became the first humans to walk on the Moon.",
    "createdAt": "2026-07-20T18:55:51.000Z",
    "likes": 38734,
    "reposts": 8439,
    "replies": 2564,
    "quotes": 958,
    "views": 3425575,
    "engagement": 50695
  },
  "unavailable": false,
  "scrapedAt": "2026-08-05T21:48:10.080Z"
}
```

And one final summary row with the group totals and the winner in each category
(abridged):

```json
{
  "recordType": "summary",
  "windowFrom": "2026-07-06T21:48:10.080Z",
  "windowTo": "2026-08-05T21:48:10.080Z",
  "windowDays": 30,
  "accountsRequested": 5,
  "accountsCompared": 3,
  "accountsUnavailable": 2,
  "unavailableAccounts": [
    { "username": "Blue_Origin", "reason": "Protected account" },
    { "username": "zzqnotreal12345", "reason": "Account not found" }
  ],
  "totalPosts": 159,
  "totalEngagement": 1449750,
  "totalViews": 244383531,
  "avgEngagementPerPost": 9117.9,
  "winners": {
    "mostFollowers": { "username": "NASA", "value": 92245565 },
    "mostActive": { "username": "SpaceX", "value": 100 },
    "mostViews": { "username": "SpaceX", "value": 194912675 },
    "mostEngagement": { "username": "SpaceX", "value": 1228104 },
    "mostEngagingPerPost": { "username": "SpaceX", "value": 12281 },
    "highestEngagementRate": { "username": "esa", "value": 0.0444 }
  },
  "leaderboard": [
    { "username": "SpaceX", "posts": 100, "shareOfEngagementPct": 84.71 },
    { "username": "NASA", "posts": 39, "shareOfEngagementPct": 14.12 },
    { "username": "esa", "posts": 20, "shareOfEngagementPct": 1.16 }
  ],
  "scrapedAt": "2026-08-05T21:48:10.080Z"
}
```

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `profiles` | array of text | — | **Required.** Two to fifty accounts to compare. An account name, an `@name` or a link to the profile all work. Add your own account to see where you stand. |
| `lastDays` | integer | `30` | How long the comparison period is, counting back from the end of the period (now, unless you set an end date). Every account is measured over exactly the same span. |
| `postedAfter` | text | — | Start of the period (`YYYY-MM-DD`). Overrides `lastDays`. |
| `postedBefore` | text | — | End of the period (`YYYY-MM-DD`). Defaults to now. |
| `postsPerAccount` | integer | `100` | A ceiling on how many posts are measured per account, newest first. Raise it for very prolific accounts or long periods. |
| `includeReposts` | boolean | `false` | Count reposts as output. Off by default, so the comparison measures what each account published itself. |
| `includeReplies` | boolean | `false` | Count replies as output. Turn it on when conversation volume is part of what you compare. |
| `topPostBy` | select | `engagement` | Which metric decides the standout post reported per account: engagement, likes, views or reposts. |
| `accountsAtOnce` | integer | `3` | How many accounts to work through at the same time. |
| `countryCode` | select | `us` | Which country to browse from. |

### Output fields

Every account produces one row, and the run ends with one summary row. Use
`recordType` to tell them apart.

#### Account rows (`recordType: "account"`)

| Field | Description |
| --- | --- |
| `username`, `displayName`, `profileUrl`, `bio`, `location`, `website`, `joinedAt`, `avatarUrl` | Who the account is. |
| `followers`, `following`, `totalPostsAllTime` | Audience size and lifetime output. |
| `isVerified`, `hasSubscriptionBadge` | Badge status. |
| `windowFrom`, `windowTo`, `windowDays` | The period every account was measured over. |
| `posts`, `postsPerWeek` | Output inside the period. |
| `postsCapped` | True when the account published more inside the period than your per-account ceiling allowed to be measured. |
| `totalLikes`, `totalReposts`, `totalReplies`, `totalQuotes`, `totalBookmarks` | Reaction totals for the period. |
| `totalEngagement` | Likes + reposts + replies + quotes. |
| `totalViews`, `avgViewsPerPost`, `postsWithViews` | Reach for the period. |
| `avgLikesPerPost`, `avgEngagementPerPost` | Per-post averages. |
| `engagementRate` | Engagement on an average post as a percentage of the follower count. |
| `postsWithMedia`, `postsWithLinks`, `topHashtags` | What the content looked like. |
| `firstPostAt`, `lastPostAt` | First and last post inside the period. |
| `accountsCompared` | How many accounts the ranks and shares are out of. |
| `rankByFollowers`, `rankByPosts`, `rankByTotalViews`, `rankByAvgEngagement`, `rankByEngagementRate` | Position in the group, 1 being best. Tied accounts share a position. |
| `shareOfVoicePct` | This account's posts as a share of all posts in the group. |
| `shareOfEngagementPct`, `shareOfViewsPct` | Its share of the group's engagement and reach. |
| `vsSetAvgEngagementPct` | How far above or below the group's engagement-per-post this account sits. |
| `isMostActive`, `isMostEngaging` | Quick flags for the leader in output and in engagement per post. |
| `topPost` | The account's standout post for the period, with its own numbers. |
| `unavailable`, `unavailableReason` | Set when an account could not be collected, so your list keeps its shape. |

#### Summary row (`recordType: "summary"`)

| Field | Description |
| --- | --- |
| `windowFrom`, `windowTo`, `windowDays` | The period the whole comparison used. |
| `accountsRequested`, `accountsCompared`, `accountsUnavailable`, `unavailableAccounts` | Who took part and who could not. |
| `totalPosts`, `totalLikes`, `totalReposts`, `totalReplies`, `totalQuotes`, `totalBookmarks`, `totalEngagement`, `totalViews` | Group totals. |
| `avgPostsPerAccount`, `avgPostsPerWeek`, `avgEngagementPerPost` | Group averages. |
| `winners` | The leader in followers, output, views, total engagement, engagement per post and engagement rate. |
| `leaderboard` | Every compared account with its posts, engagement and shares, in ranking order. |
| `topPost` | The single best-performing post across the whole group. |

### Pricing

This actor is **pay per result**. You are charged for each row that lands in
your dataset — one per account you compare, plus the one summary row — and for
nothing else. There is no time-based charge.

A five-account comparison produces six rows. Running that every day for a month
is 186 rows. Comparing thirty accounts once produces thirty-one rows. The bill
does not change with how many posts had to be measured behind each row, so a
deep 1,000-post-per-account comparison costs exactly the same as a shallow one.

Accounts that turn out to be private, suspended or missing still produce their
row, because dropping them silently would break your list — and they are left
out of every ranking and every share, so they never distort the accounts around
them.

### Limits & what this actor cannot do

- Every account is measured over the same period, but only up to the per-account
  ceiling you set. If an account published more than that inside the period, its
  row is flagged with `postsCapped` and its totals cover only the most recent
  posts measured. Raise the ceiling for a complete picture of prolific accounts.
- X publishes a limited window of recent public posts per account, so a very
  long comparison period will return what X makes available rather than a
  complete history.
- Keyword search across all of X is not part of what X publishes openly. This
  actor compares the accounts you supply rather than everyone talking about a
  topic.
- Replies written by *other people* underneath a post are not publicly
  available, and neither are the lists of who follows an account. Follower and
  following counts are included.
- Protected (private) accounts are not publicly viewable. They are reported as
  unavailable, excluded from every ranking and excluded from the share-of-voice
  totals — an account nobody could read did not post zero times, and counting it
  as zero would quietly inflate everyone else's share.
- Deleted and suspended accounts are reported the same way rather than silently
  skipped.
- Likes, reposts, replies, bookmarks and views are a snapshot at the moment of
  collection and keep changing afterwards. Two runs over the same period days
  apart will not produce identical numbers.
- X publishes view counts for most recent posts but not for every older one.
  Where X publishes none, the view fields are left empty rather than set to
  zero, and those posts are left out of the view averages.
- Engagement rate is engagement per post divided by the follower count. It is a
  comparison aid, not an official X metric.
- Speed depends on the size of the comparison and on X's own response times.
- X's terms govern automated access. You are responsible for using the data
  lawfully, in line with the source site's terms, and for handling any personal
  data in line with applicable privacy law.

### FAQ

**Do I need an X account?**
No. Nothing is required from you beyond the list of accounts you want compared.

**Does it need my login, password or a paid X subscription?**
No. None of those are used at any point.

**Why does it need at least two accounts?**
Because every comparative column — rank, share of voice, share of engagement,
distance from the group average — is meaningless with one account. If you only
want one account's numbers, a single-account report is the wrong shape for this
actor.

**Can I schedule it?**
Yes. Schedule it on Apify weekly or monthly over the same group and you get a
consistent competitive tracker.

**Is the data complete?**
For each account it is complete up to two honest boundaries, and both are marked
in the output. The first is the per-account ceiling you set: if an account
published more inside the period than the ceiling allowed to be measured, its row
comes back with `postsCapped` set to true, so you can raise the ceiling and rerun
rather than trusting a truncated total. The second is X itself, which publishes a
limited window of recent public posts per account — over a long period you get
what X makes available rather than a complete history. Accounts that could not be
read at all are marked `unavailable` with a reason and left out of every ranking
and share.

**What happens if one of the accounts is private or misspelled?**
It still appears in the output, marked unavailable with a reason, and it is
excluded from every ranking and from the share-of-voice totals. Your other
accounts' numbers are exactly what they would have been without it.

**Are the rankings computed across the whole group?**
Yes. Nothing is ranked until every account has been collected, so a rank always
reflects the complete group rather than whichever accounts finished first.

**How do I get the results into my tools?**
Export from Apify as CSV, Excel, JSON, XML or an HTML table, push into Google
Sheets, or pull them programmatically into your own system.

# Actor input Schema

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

Two to fifty X accounts to compare head-to-head. Paste an account name, an @name, or a link to the profile — all three work. Add your own account to see where you stand.

## `lastDays` (type: `integer`):

The comparison period, counting back from now. Every account is measured over exactly the same period. Ignored if you set an explicit start date below.

## `postedAfter` (type: `string`):

Start of the comparison period (YYYY-MM-DD). Overrides the last-N-days setting.

## `postedBefore` (type: `string`):

End of the comparison period (YYYY-MM-DD). Defaults to now.

## `postsPerAccount` (type: `integer`):

A ceiling on how many posts are measured for each account, newest first. Raise it if you compare very prolific accounts over a long period.

## `includeReposts` (type: `boolean`):

Off by default, so the comparison measures what each account published itself rather than what it amplified.

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

Off by default. Turn it on when conversation volume is part of what you are comparing.

## `topPostBy` (type: `string`):

Which metric decides the standout post reported for each account.

## `accountsAtOnce` (type: `integer`):

How many accounts to work through in parallel. Raise it for larger comparisons.

## `countryCode` (type: `string`):

The country to browse from. Affects which posts X shows publicly in a few regions.

## Actor input object example

```json
{
  "profiles": [
    "nasa",
    "SpaceX"
  ],
  "lastDays": 30,
  "postedAfter": "2026-01-01",
  "postedBefore": "2026-01-31",
  "postsPerAccount": 100,
  "includeReposts": false,
  "includeReplies": false,
  "topPostBy": "engagement",
  "accountsAtOnce": 3,
  "countryCode": "us"
}
```

# 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 = {
    "profiles": [
        "nasa",
        "@SpaceX",
        "https://x.com/esa"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("guezi/x-competitor-benchmark").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 = { "profiles": [
        "nasa",
        "@SpaceX",
        "https://x.com/esa",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("guezi/x-competitor-benchmark").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 '{
  "profiles": [
    "nasa",
    "@SpaceX",
    "https://x.com/esa"
  ]
}' |
apify call guezi/x-competitor-benchmark --silent --output-dataset

```

## MCP server setup

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

```

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/6zLq2AmILwC2xHjjp/builds/JnBMzWLfFN08O1gvG/openapi.json
