X (Twitter) Audience Audit | Are the Followers Real avatar

X (Twitter) Audience Audit | Are the Followers Real

Pricing

from $2.00 / 1,000 row delivereds

Go to Apify Store
X (Twitter) Audience Audit | Are the Followers Real

X (Twitter) Audience Audit | Are the Followers Real

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.

Pricing

from $2.00 / 1,000 row delivereds

Rating

0.0

(0)

Developer

FeedMiner

FeedMiner

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

๐Ÿ”“ 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

FeedMiner ยท X (Twitter) suite โ€ข Posts, profiles, audiences, media, trends
X Audience Audit
โžค You are here
X Tweet Scraper
Search, threads, lists, communities ยท $0.12/1K tweets
X Profile Scraper
Profile + first 100 posts included ยท $1.70/1K profiles

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

{ "handles": ["mkbhd"] }

The shortlist, in one run

{ "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

{ "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

{ "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

ParameterTypeDefaultDescription
handlesarrayrequired1 to 4 X accounts, as handles or profile URLs. A fifth handle is not audited and the run says so by name
sampleSizeinteger2000Rows drawn from the head of each account's follower list, 500 to 2000. More rows is a firmer number and a longer run
debugModebooleanfalseVerbose 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

FieldTypeDescription
rowTypestringaudit on this row, follower on the evidence rows below
handle, userId, name, twitterUrlstringThe audited account, as X resolved it
statestringHow the audit ended. ok and partial carry a verdict; the rest are answers of their own
statementstringThe finding in one sentence, naming the sample it rests on. Written to be read out loud to a client
followersTotal, followingTotal, postsTotalintegerThe audited account's own counts, at run time
samplingBasisstringWhat the rows were drawn from โ€” always follower_roster_head
sampleRequested, sampledFollowersintegerRows asked for, and rows the list actually served
sampleShareOfFollowersfloatWhat fraction of the follower count was drawn. Null when nothing was drawn, and when the follower count itself could not be read
machineShapedInSampleintegerHow many drawn rows scored at or above the threshold
machineShareOfSamplefloatThat 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
flagCountsobjectHow many drawn rows each of the seven signals fired on, zeros included
isBlueVerifiedbooleanWhether the audited account itself carries the paid badge
blueVerifiedInSampleintegerHow many drawn rows carry the paid badge. Reported, never scored
medianAgeDaysInSamplefloatThe median account age of the rows drawn, in days
accountCreatedAt, scoredAtstringWhen the audited account joined, and when the run scored it

The evidence โ€” one follower row per sampled follower

FieldTypeDescription
rowType, auditHandlestringfollower, and which audited account this row was drawn for
samplingBasisstringWhere the row came from, repeated on every row so a row read alone still says what it is
sampleRankintegerIts place in the order the list served it, first row 1
id, userName, name, twitterUrlstringThe sampled account
followers, following, statusesCountintegerThe counts the scoring reads
favouritesCount, mediaCountintegerLikes and media, the two halves of the footprint signal
description, location, profilePicturestringBio, location, and the avatar URL the default-picture signal reads
coverPicturestringThe banner image, on the rows where X serves one
createdAtstringWhen the account joined, ISO format
isVerified, isBlueVerifiedbooleanLegacy verification and the paid badge
machineScoreintegerThis row's score out of 100
machineShapedbooleanWhether it reached the threshold
flagsarrayEvery 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

Example verdict row

{
"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

{
"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.

SignalFires whenWeight
no_poststhe account has never posted30
no_followersnobody follows it20
new_accountit was created in the last 30 days20
lopsided_ratioit follows 50+ accounts and is followed by at most a fiftieth of that15
default_avatarit still has X's default profile picture15
empty_bioit has no bio10
no_footprintit has never liked anything and has no media10

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:

stateWhat happened
okthe run drew what it asked for, or the whole list
partialit drew fewer rows than asked; the share is computed on what it drew
insufficient_sampletoo few rows to state a share โ€” either the account has fewer followers than the floor, or the list stopped short
protectedthe account is private โ€” X publishes no follower list for it
suspendedX has suspended the account
unavailableX answers that it will not serve the account, for some reason other than suspension
no_followersthe account has no followers to sample
unreadablethe 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

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

EventWhat it bills
rowOne 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

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

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

MeasureValueMeasured on
Reading speedAround 160 rows per secondA run of two accounts audited at full depth: 4,002 rows in 25.0 seconds
Confirmed atAround 139 rows per secondThe same shape of run, 4,002 rows in 28.8 seconds
One audit of 2,000About fifteen seconds of wall clockThe same runs, per account
Rows per audit2,001 at the default depth2,000 sampled followers plus the verdict
Requests per audit41 โ€” one handle lookup and 40 follower pagesCounted on both runs above, plus 4 fixed setup requests per run
Pages per audit40 pages for 2,000 rowsThe first page serves 65 to 70 rows and the rest about 50, whatever the request asks for
Accounts per runAt most 4, read in parallelOne audit fits inside one rate window; a fifth would be read against a spent budget
Partial resultsKept and delivered, never discardedA run stopped on purpose mid-walk delivered the 3,796 rows it had already collected
A walled walkStill delivers, with a partial verdictA 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, liveNo share below 200 drawn rowsA 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.

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 ยท All FeedMiner Actors