# X (Twitter) Audience Audit | Are the Followers Real (`feedminer/x-audience-audit`) Actor

Audit an X (Twitter) account's recent follower intake before you sign it. Samples the head of the follower list, scores each one on age, activity, ratio, bio and avatar, and returns the machine-shaped share with every sampled row attached, so you can re-derive the number rather than trust it.

- **URL**: https://apify.com/feedminer/x-audience-audit.md
- **Developed by:** [FeedMiner](https://apify.com/feedminer) (community)
- **Categories:** Social media, Marketing, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 row delivereds

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?

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

<div style="border-left:4px solid #1D9BF0;background:#F2F9FF;padding:14px 18px;border-radius:6px">
<strong>Is this creator's audience real?</strong> Paste 1 to 4 X (Twitter) handles. Each run reads the head of the account's follower list, scores every follower it draws on the evidence X itself publishes, and returns the share of those rows that is machine-shaped &mdash; with every sampled row attached, as JSON, CSV or Excel. No X account, no API key, no code.
</div>

🔓 **Nothing to connect.** No X login of yours, no cookies to paste, nothing to create or keep alive. Follower lists are closed to logged-out visitors, so the reads ride borrowed sessions from a pool this Actor's operator maintains; your own account is never involved.

⚡ **An audit in about fifteen seconds.** Measured on a real run: 4,002 rows in 25.0 seconds across two accounts audited at full depth — around 160 rows per second, 41 requests per audit.

🧾 **The number arrives with the rows it came from.** Every sampled follower is delivered with its score and the signals that fired on it — 2,001 rows for an audit of 2,000. You re-derive the percentage instead of trusting it, which is what makes it usable in a negotiation.

⚖️ **It never claims more than it sampled.** The finding is about recent follower intake, out of a stated follower count, never a share of the whole audience. A sample too thin to carry a verdict ships its rows and no percentage at all.

![How the X Audience Audit works: handles in, one verdict per account and one row per sampled follower out](https://api.apify.com/v2/key-value-stores/RPT1LNaNbQoVImUE9/records/x-audience-audit-how-it-works-v2.png)

#### Copy to your AI assistant

```
feedminer/x-audience-audit on Apify. Audits whether an X (Twitter) account's recent follower intake is real. For each of 1-4 handles it reads the head of that account's follower list (up to 2000 rows), scores every drawn follower on seven published signals, and delivers one `audit` verdict row per handle plus one `follower` row per sampled account — 2001 rows for an audit of 2000. Rows are told apart by rowType. audit row: handle, state (ok|partial|insufficient_sample|protected|suspended|unavailable|no_followers|unreadable), followersTotal, samplingBasis, sampleRequested, sampledFollowers, machineShapedInSample, machineShareOfSample (null whenever the sample cannot carry a share), flagCounts, blueVerifiedInSample, medianAgeDaysInSample, statement, twitterUrl. follower row: auditHandle, sampleRank, userName, name, followers, following, statusesCount, favouritesCount, mediaCount, description, createdAt, isBlueVerified, machineScore, machineShaped, flags[], twitterUrl. The share is always of the rows drawn from the head of the list, never of the whole audience. Call ApifyClient("TOKEN").actor("feedminer/x-audience-audit").call(run_input={"handles": ["mkbhd"], "sampleSize": 2000}), then client.dataset(run["defaultDatasetId"]).list_items().items. Required: handles (string[], 1 to 4, handles or profile URLs). Optional: sampleSize (integer, 500-2000, default 2000). Full spec: GET https://api.apify.com/v2/acts/feedminer~x-audience-audit/builds/default (Bearer TOKEN) → inputSchema, actorDefinition.storages.dataset, readme. Token: https://console.apify.com/account/integrations
```

***

### How to audit an X account's followers

Paste the handles you are about to sign. That is the whole input.

##### One account

```json
{ "handles": ["mkbhd"] }
```

##### The shortlist, in one run

```json
{ "handles": ["mkbhd", "verge", "nytimes"] }
```

Audit the creators you are comparing in the same run. What counts as a normal intake depends on the account, so the comparison between them is the yardstick — a number on its own has nothing to sit against.

##### Profile URLs, if that is what you were sent

```json
{ "handles": ["https://x.com/mkbhd", "@verge"] }
```

A profile URL is reduced to its handle, a leading `@` is stripped, and duplicates fold away on read.

##### A cheaper first pass over four accounts

```json
{ "handles": ["mkbhd", "verge", "nytimes", "barackobama"], "sampleSize": 500 }
```

A shallower sample costs less and reads faster; it is also a thinner number. Use it to sort a longlist, then re-run the two accounts that matter at full depth.

***

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `handles` | array | required | 1 to 4 X accounts, as handles or profile URLs. A fifth handle is not audited and the run says so by name |
| `sampleSize` | integer | `2000` | Rows drawn from the head of each account's follower list, 500 to 2000. More rows is a firmer number and a longer run |
| `debugMode` | boolean | `false` | Verbose logs for support. Leave off |

That is the whole form. The run is bounded by the two together: at most four accounts, at most `sampleSize` rows each, so an audit of four handles at full depth delivers 8,004 rows and nothing you did not ask for.

***

### What this measures

It measures **recent follower intake**: the head of the follower list, which X serves as about twenty notable followers followed by the most recent arrivals. That is the useful question — *did this account pick up machine-shaped followers lately* — and it is one the rows can support.

It is **not** a share of the whole audience. A large account's full follower list runs to tens of thousands of rate-limited pages; nobody reads it, and any actor that quotes you a whole-audience percentage from a sample is guessing. Every row here states how many rows it drew, out of what follower count, and says so in its own sentence.

A high share is also not proof anybody bought followers: large accounts attract machine-shaped follows they never asked for. The number is evidence you take into the conversation, alongside the rows it came from.

***

### What data can you extract from X?

Two kinds of row in one dataset, told apart by `rowType`. The run's output tab splits them into an **Audit verdicts** view and a **Sampled followers** view.

**The verdict — one `audit` row per handle**

| Field | Type | Description |
|---|---|---|
| `rowType` | string | `audit` on this row, `follower` on the evidence rows below |
| `handle`, `userId`, `name`, `twitterUrl` | string | The audited account, as X resolved it |
| `state` | string | How the audit ended. `ok` and `partial` carry a verdict; the rest are answers of their own |
| `statement` | string | The finding in one sentence, naming the sample it rests on. Written to be read out loud to a client |
| `followersTotal`, `followingTotal`, `postsTotal` | integer | The audited account's own counts, at run time |
| `samplingBasis` | string | What the rows were drawn from — always `follower_roster_head` |
| `sampleRequested`, `sampledFollowers` | integer | Rows asked for, and rows the list actually served |
| `sampleShareOfFollowers` | float | What fraction of the follower count was drawn. **Null when nothing was drawn, and when the follower count itself could not be read** |
| `machineShapedInSample` | integer | How many drawn rows scored at or above the threshold |
| `machineShareOfSample` | float | That count over the rows drawn. **Null whenever the sample cannot carry a share** — never a zero that would read as a clean bill of health |
| `flagCounts` | object | How many drawn rows each of the seven signals fired on, zeros included |
| `isBlueVerified` | boolean | Whether the audited account itself carries the paid badge |
| `blueVerifiedInSample` | integer | How many drawn rows carry the paid badge. Reported, never scored |
| `medianAgeDaysInSample` | float | The median account age of the rows drawn, in days |
| `accountCreatedAt`, `scoredAt` | string | When the audited account joined, and when the run scored it |

**The evidence — one `follower` row per sampled follower**

| Field | Type | Description |
|---|---|---|
| `rowType`, `auditHandle` | string | `follower`, and which audited account this row was drawn for |
| `samplingBasis` | string | Where the row came from, repeated on every row so a row read alone still says what it is |
| `sampleRank` | integer | Its place in the order the list served it, first row 1 |
| `id`, `userName`, `name`, `twitterUrl` | string | The sampled account |
| `followers`, `following`, `statusesCount` | integer | The counts the scoring reads |
| `favouritesCount`, `mediaCount` | integer | Likes and media, the two halves of the footprint signal |
| `description`, `location`, `profilePicture` | string | Bio, location, and the avatar URL the default-picture signal reads |
| `coverPicture` | string | The banner image, on the rows where X serves one |
| `createdAt` | string | When the account joined, ISO format |
| `isVerified`, `isBlueVerified` | boolean | Legacy verification and the paid badge |
| `machineScore` | integer | This row's score out of 100 |
| `machineShaped` | boolean | Whether it reached the threshold |
| `flags` | array | Every signal that fired on it, by name |

The account fields use the same names as our other X profile and follower scrapers, so the rows drop into an existing pipeline without a mapping.

![What one sampled follower row of the X Audience Audit carries, field by field](https://api.apify.com/v2/key-value-stores/RPT1LNaNbQoVImUE9/records/x-audience-audit-row-anatomy-v1.png)

##### Example verdict row

```json
{
  "rowType": "audit",
  "handle": "mkbhd",
  "state": "ok",
  "followersTotal": 6130092,
  "samplingBasis": "follower_roster_head",
  "sampleRequested": 2000,
  "sampledFollowers": 2000,
  "machineShapedInSample": 992,
  "machineShareOfSample": 0.496,
  "twitterUrl": "https://x.com/mkbhd",
  "statement": "992 of the first 2,000 rows of @mkbhd's follower list (6,130,092 followers) are machine-shaped — 49.6% of the rows drawn. X serves about twenty notable followers at the head of that list and the rest most-recent-first, so this measures recent follower intake, not the whole audience."
}
```

That row is from a real run, shortened. Every field in the table above rides on it too — `flagCounts`, `blueVerifiedInSample`, `medianAgeDaysInSample`, `userId`, `name`, `accountCreatedAt`, `followingTotal`, `postsTotal`, `isBlueVerified`, `sampleShareOfFollowers` and `scoredAt` — and is left out here only to keep the example readable.

##### Example evidence row

```json
{
  "rowType": "follower",
  "auditHandle": "mkbhd",
  "sampleRank": 1487,
  "userName": "kwtnjq_8821",
  "name": "Kay",
  "createdAt": "2019-03-02T09:41:55Z",
  "followers": 0,
  "following": 612,
  "statusesCount": 0,
  "favouritesCount": 0,
  "mediaCount": 0,
  "description": "",
  "profilePicture": "https://abs.twimg.com/sticky/default_profile_images/default_profile_normal.png",
  "isBlueVerified": false,
  "machineScore": 100,
  "machineShaped": true,
  "flags": ["no_posts", "no_followers", "lopsided_ratio", "default_avatar", "empty_bio", "no_footprint"],
  "twitterUrl": "https://x.com/kwtnjq_8821"
}
```

The account on that row is invented, not a real one we are calling a bot. Its score is the table below applied to the values shown.

***

### How a follower is scored

Seven signals, published so you can argue with them. They are additive, and each row carries the ones that fired on it along with its own score out of 100.

| Signal | Fires when | Weight |
|---|---|---|
| `no_posts` | the account has never posted | 30 |
| `no_followers` | nobody follows it | 20 |
| `new_account` | it was created in the last 30 days | 20 |
| `lopsided_ratio` | it follows 50+ accounts and is followed by at most a fiftieth of that | 15 |
| `default_avatar` | it still has X's default profile picture | 15 |
| `empty_bio` | it has no bio | 10 |
| `no_footprint` | it has never liked anything and has no media | 10 |

A row is called **machine-shaped at 60 or more**. That is above the sum of the two heaviest signals on purpose: no signal, and no pair of signals, can put a row over the line on its own — a quiet real person who never posts and has no followers is not accused on two absences.

What that does *not* guarantee, said here rather than left for you to discover: the four absence signals together reach 70, so an account that has never posted, has no followers, no bio and has never liked anything is called machine-shaped on absences alone. The table above is published so you can weigh that yourself against the rows, which carry every signal that fired.

The paid badge is reported (`blueVerifiedInSample`, `isBlueVerified`) but never scored: it is bought, not earned, and machine-shaped accounts carry it too.

##### When an account cannot be audited

You still get its row, with a `state` saying why and no invented number:

| `state` | What happened |
|---|---|
| `ok` | the run drew what it asked for, or the whole list |
| `partial` | it drew fewer rows than asked; the share is computed on what it drew |
| `insufficient_sample` | too few rows to state a share — either the account has fewer followers than the floor, or the list stopped short |
| `protected` | the account is private — X publishes no follower list for it |
| `suspended` | X has suspended the account |
| `unavailable` | X answers that it will not serve the account, for some reason other than suspension |
| `no_followers` | the account has no followers to sample |
| `unreadable` | the list did not read cleanly on this run — including when one signal came back identical on every row drawn, which the run cannot tell apart from an audience that really is uniform |

On `insufficient_sample` and `unreadable`, any rows that *were* drawn are still delivered with their signals, and billed — what is withheld is the verdict, not the evidence. The row's own sentence says so and says how many rows you hold.

***

### What this Actor covers

![What the X Audience Audit reaches, and what it does not](https://api.apify.com/v2/key-value-stores/RPT1LNaNbQoVImUE9/records/x-audience-audit-coverage-v1.png)

This Actor reads follower lists and nothing else. Plain follower and following lists, without the scoring, are **X Follower Scraper**; who likes and reposts an account's posts is **X Likes and Reposts Scraper**; the accounts that quote and reply to it are **X Engagement Scraper**. One run is one snapshot — there is no monitoring, alerting or history kept between runs, so schedule the Actor again to compare two moments.

***

### Pricing

| Event | What it bills |
|---|---|
| `row` | One delivered dataset row — a sampled follower, or an audit verdict |

One event, and only rows bill. An audit at the default depth delivers **2,001 rows**: 2,000 sampled followers and the verdict computed from them. You are billed on **what the dataset actually stored**, never on what the run collected — a run stopped mid-walk bills the rows it had already delivered and nothing else, and a run that could not sample at all delivers nothing and bills nothing.

##### What this replaces

The audit a brand runs before signing a creator is bought today in two places, and neither hands over the rows behind its number.

- **HypeAuditor** covers X and is the reference tool agencies name, but it is demo-gated: the published entry point is roughly $299 to $499 a month, and you talk to sales before you see an account.
- **Fiverr** sells the same deliverable by hand, priced by the size of the account audited — around $45 under 25K followers, $100 up to 100K, $250 up to 500K — with a turnaround measured in days rather than seconds.
- On the Apify Store, `seemuapps/x-influencer-analytics-scraper` sells an influencer report at $0.1 per report plus $0.002 per tweet, and `seemuapps/x-account-origin-checker` sells an account-origin check at $0.005 per account. Both sell adjacent rows; neither sells a follower-intake verdict with its sample attached.

***

### Advanced usage

##### The shortlist, ranked

Put every creator you are considering into one run. Read the `audit` rows side by side rather than each on its own: 40% of 2,000 drawn rows means one thing next to a peer at 12% of 2,000 and another next to a peer at 45% of 2,000. The comparison is the part of the finding you can defend.

##### Re-derive the number yourself

Filter the dataset to `rowType: "follower"` and recount. Every signal that fired is on the row, and the weights are published above, so you can re-score with your own threshold, drop a signal you disagree with, or count only rows past `sampleRank` 20 to exclude the notable block at the head of the list.

##### Look at where in the intake the machines sit

Sort the `follower` rows by `sampleRank` and read `machineShaped` down the column. A run of machine-shaped rows clustered together reads differently from the same share spread evenly through the sample — one looks like a burst, the other like background noise.

##### A cheap longlist pass, then depth

Audit four accounts at `sampleSize: 500` to sort them, then re-run the two that matter at 2,000. The floor for a published share is 200 drawn rows, so 500 still yields a verdict while costing a quarter of the rows.

***

### Integrate audience data into your stack

Results flow through the Apify API into Python, JavaScript, Google Sheets, Make, Zapier, n8n, Airtable or Slack. Runs can be scheduled, monitored and chained with other Actors.

##### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("feedminer/x-audience-audit").call(run_input={
    "handles": ["mkbhd", "verge"],
    "sampleSize": 2000,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["rowType"] == "audit":
        print(row["handle"], row["state"], row["statement"])
```

##### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('feedminer/x-audience-audit').call({
    handles: ['mkbhd'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const verdicts = items.filter((row) => row.rowType === 'audit');
const evidence = items.filter((row) => row.rowType === 'follower');
console.log(`${verdicts.length} verdicts over ${evidence.length} sampled followers`);
```

***

### Performance

| Measure | Value | Measured on |
|---|---|---|
| Reading speed | Around 160 rows per second | A run of two accounts audited at full depth: 4,002 rows in 25.0 seconds |
| Confirmed at | Around 139 rows per second | The same shape of run, 4,002 rows in 28.8 seconds |
| One audit of 2,000 | About fifteen seconds of wall clock | The same runs, per account |
| Rows per audit | 2,001 at the default depth | 2,000 sampled followers plus the verdict |
| Requests per audit | 41 — one handle lookup and 40 follower pages | Counted on both runs above, plus 4 fixed setup requests per run |
| Pages per audit | 40 pages for 2,000 rows | The first page serves 65 to 70 rows and the rest about 50, whatever the request asks for |
| Accounts per run | At most 4, read in parallel | One audit fits inside one rate window; a fifth would be read against a spent budget |
| Partial results | Kept and delivered, never discarded | A run stopped on purpose mid-walk delivered the 3,796 rows it had already collected |
| A walled walk | Still delivers, with a `partial` verdict | A run whose two walks were cut short shipped verdicts computed on 420 and 617 drawn rows, each naming how many of the 2,000 asked for were served |
| The floor, live | No share below 200 drawn rows | A run where one account served 53 rows shipped `insufficient_sample` with no share, and delivered the 53 rows anyway |

***

### FAQ

**Do I need an X account, cookies or an API key?**
No. There is nothing to connect and nothing to keep alive on your side. Follower lists are closed to logged-out visitors, so the reads ride borrowed sessions from a pool the operator maintains — never yours.

**How many followers does it actually read?**
Up to 2,000 per account per run, drawn from the head of the follower list. Every row states how many it drew and out of what follower count.

**Why not the whole follower list?**
Because it is not reachable. A large account's full list runs to tens of thousands of rate-limited pages — an account with tens of millions of followers would take tens of thousands of rate windows to read once. Any tool that quotes you a whole-audience percentage is extrapolating from a sample and not telling you so.

**Is a high machine-shaped share proof the account bought followers?**
No, and the page will not say it is. Large accounts attract machine-shaped follows nobody asked for. The number is evidence you take into a conversation, alongside the rows it was computed from.

**Can I disagree with the scoring?**
Yes — that is what the evidence rows are for. The seven signals and their weights are published above, every row carries the signals that fired on it and its own score, and you can recount with your own threshold.

**My account came back with no percentage. Is that a failure?**
No, it is the actor refusing to guess. `insufficient_sample` means too few rows were served to state a share; `unreadable` means one signal came back identical on every row, which the run cannot tell apart from an audience that really is uniform. Both still deliver the rows that were drawn, with their signals.

**The run told me to try again in a few minutes. Why?**
The borrowed sessions rest on a rate window. When they are all resting, the run exits early, delivers nothing and bills nothing, and says roughly how long to wait. Running again after the wait works.

**Is the data live?**
Yes. Every run reads X at that moment. Nothing is cached or resold, and nothing is kept between runs.

**What does it not do?**
It does not read the whole follower list, does not follow an account over time, and does not judge engagement on posts. Protected accounts publish no follower list at all, so there is nothing to audit for them — the row says so instead of inventing a number.

***

### Support

- Found something off in the output, or need a field that is missing? Open the **Issues** tab on this Actor's page with what you ran and what you expected. Issues are read daily.
- Need a custom variant of this audit for your pipeline — a different threshold, a different depth, a different set of signals? Ask in the Issues tab, FeedMiner builds on request.

### Legal compliance

This Actor collects only public data, the same information any visitor can see on x.com. Every sampled follower is an identifiable person: processing information about people in the EU, the UK or California requires a lawful basis under GDPR or CCPA, and the scores here are an observation about published account fields, never a judgement about a person. Use the findings for due diligence, research and business intelligence — not to target, publish accusations about, or harass the accounts in the sample.

***

**FeedMiner** · Fast, honestly priced scrapers, kept alive when platforms change.

X Audience Audit · [X Follower Scraper](https://apify.com/feedminer/x-follower-scraper) · [All FeedMiner Actors](https://apify.com/feedminer)

# Actor input Schema

## `handles` (type: `array`):

1 to 4 X accounts to audit, as handles or profile URLs. Audit the creators you are comparing in the same run: the run's own comparison is the yardstick.

## `sampleSize` (type: `integer`):

How many rows to draw from the head of each account's follower list. More rows is a firmer number and a longer run. Default 2000.

## `debugMode` (type: `boolean`):

Verbose logs for support. Leave off.

## Actor input object example

```json
{
  "handles": [
    "mkbhd",
    "verge"
  ],
  "sampleSize": 2000,
  "debugMode": false
}
```

# Actor output Schema

## `audits` (type: `string`):

No description

## `followers` (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 = {
    "handles": [
        "mkbhd",
        "verge"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("feedminer/x-audience-audit").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 = { "handles": [
        "mkbhd",
        "verge",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("feedminer/x-audience-audit").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 '{
  "handles": [
    "mkbhd",
    "verge"
  ]
}' |
apify call feedminer/x-audience-audit --silent --output-dataset

```

## MCP server setup

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

```

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/gSghvbx9xIyd1tGJx/builds/uQ7aGKsy3NlsonlNN/openapi.json
