# Fake Engagement & Bot Account Detector - IG, TikTok & YouTube (`apricot_blackberry/fake-engagement-detector`) Actor

Instagram, TikTok and YouTube audience-authenticity API for AI agents and pipelines: typed JSON per profile with authenticity score, bot likelihood, verdict, weighted signals and raw metrics. Strict output schema, per-profile pricing, MCP-ready, no login. 刷粉检测: 打款前识别假粉丝与虚假互动.

- **URL**: https://apify.com/apricot\_blackberry/fake-engagement-detector.md
- **Developed by:** [Creator Fusion](https://apify.com/apricot_blackberry) (community)
- **Categories:** Social media, Marketing, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Fake Engagement & Bot Account Detector — Instagram, TikTok & YouTube Audience Authenticity API

Score any creator's audience for fake followers and bot engagement across **Instagram, TikTok and YouTube in a single call**. Give it a list of handles; it returns typed JSON rows — authenticity score, bot likelihood, verdict, a weighted signal breakdown, and the raw follower/engagement metrics behind every number, so you can re-derive the maths yourself. Schema below.

Built for creator platforms, influencer agencies and brand-safety pipelines that check audiences at volume: priced per profile analyzed, not per report.

### Why agents use this actor

- **Deterministic typed output** — one row per profile, stable field names, every nullable field declared in the dataset schema.
- **Per-profile pricing** — one `profile-analyzed` event per scored profile, so an autonomous budget maps 1:1 to work done. Profiles whose engagement could not be retrieved are **not** charged.
- **Never guesses** — if engagement data is unavailable, `verdict` is `null`, `confidence` is `insufficient-data`, and `unavailableSignals` names exactly which signals were missing. Nulls are never scored as zeros.
- **Explainable, not a black box** — every signal ships with its value, status, weight and a plain-language explanation an agent can quote straight to a user.
- **No auth, no cookies** — public data only. Retries with backoff on 429/5xx, 30 s timeouts, per-item pushes.
- **Three platforms, one contract** — the same row shape for Instagram, TikTok and YouTube.

### Input schema

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `profiles` | string\[] | no\* | `[]` | `"instagram:handle"`, `"tiktok:handle"`, `"youtube:@handle"`, or a plain profile URL (platform auto-detected). |
| `instagramUsernames` | string\[] | no\* | `[]` | Instagram usernames, with or without `@`. |
| `tiktokUsernames` | string\[] | no\* | `[]` | TikTok usernames, with or without `@`. |
| `youtubeChannels` | string\[] | no\* | `[]` | YouTube handles (`@MrBeast`) or channel IDs (`UC...`). |
| `maxPostsPerProfile` | integer | no | `12` | Recent posts/videos sampled per profile for engagement, variance and cadence. 1–50. |
| `proxyConfiguration` | object | no | `{ "useApifyProxy": true }` | Apify Proxy settings. TikTok and YouTube run on datacenter/auto; Instagram automatically uses the residential group, billed at the standard residential rate. |

\* At least one profile across the four lists is required. All four lists are merged and de-duplicated.

### Output schema

One row per profile.

| Field | Type | Nullable | Description |
|---|---|---|---|
| `platform` | string | no | `instagram`, `tiktok` or `youtube`. |
| `handle` | string | no | Canonical handle as published by the platform. |
| `profileUrl` | string | no | Public profile URL. |
| `displayName` | string | yes | Display name / full name / channel title. |
| `verified` | boolean | yes | Verification badge; null where the platform hides it. |
| `isPrivate` | boolean | yes | True for private accounts. |
| `followers` | integer | yes | Followers / subscribers. |
| `following` | integer | yes | Accounts followed. Null on YouTube — it does not publish this. |
| `posts` | integer | yes | Total posts / videos. |
| `totalLikes` | integer | yes | Lifetime likes across all videos (TikTok). |
| `avgLikes` | integer | yes | Average likes per sampled post (TikTok: lifetime likes ÷ video count). |
| `avgComments` | integer | yes | Average comments per sampled post (Instagram). |
| `avgViews` | integer | yes | Average views per sampled video (TikTok, YouTube, Instagram video posts). |
| `engagementRate` | number | yes | Fraction of audience engaging — `0.032` = 3.2%. |
| `benchmarkEngagementRate` | number | yes | Typical rate for this platform at this audience size — the benchmark used. |
| `engagementBasis` | string | yes | Which observable metric fed the rate: `recent-posts-likes-comments`, `lifetime-avg-likes-per-video`, `recent-video-views-per-subscriber`. |
| `postsAnalyzed` | integer | yes | Posts actually sampled. |
| `authenticityScore` | integer | yes | 0–100. Null when engagement could not be retrieved. |
| `botLikelihood` | number | yes | 0–1 weighted share of signals pointing to inauthentic engagement. |
| `verdict` | string | yes | `authentic` (≥80) / `likely-authentic` (65–79) / `suspicious` (45–64) / `likely-fake` (<45). Null when confidence is `insufficient-data`. |
| `confidence` | string | no | `high` / `medium` / `low` / `insufficient-data`, from how many signals were retrievable. |
| `signals` | object\[] | no | `{ name, value, status, weight, explanation }` per signal. `status` ∈ `ok`/`warn`/`bad`/`unavailable`. |
| `redFlags` | string\[] | no | Explanations of every signal that scored `bad`. |
| `greenFlags` | string\[] | no | Explanations of every healthy signal. |
| `unavailableSignals` | string\[] | no | Signals with no retrievable data for this profile. |
| `bio`, `externalUrl`, `avatarUrl` | string | yes | Profile completeness inputs, returned raw. |
| `channelId` | string | yes | YouTube channel ID. |
| `analyzedAt` | string | no | ISO 8601 timestamp. |
| `error` | string | yes | Always null on pushed rows — failures go to the `SUMMARY` record. |

#### The signals

| Signal | Weight | What it catches |
|---|---|---|
| `engagementRate` | 3 | Engagement graded against the typical rate **for that audience size**, never an absolute — large accounts naturally engage lower. Far below tier reads as inactive/purchased followers; implausibly above reads as purchased engagement. |
| `likeToCommentRatio` | 2 | Bot engagement is likes-only. Real audiences comment. |
| `engagementVariance` | 2 | Organic reach is spiky. Near-identical numbers post to post are a delivered quota. |
| `followerFollowingRatio` | 1 | Mass-follow and follow/unfollow growth tactics. |
| `postingCadence` | 1 | Burst-and-dormancy publishing patterns. |
| `accountCompleteness` | 1 | Bio, link, avatar, name and verification — disposable accounts skip them. |

The score is the weighted share of **available** signals that came back clean. Unavailable signals are excluded from both numerator and denominator and lower `confidence` instead.

#### Example rows (real output)

```json
{
  "platform": "instagram", "handle": "nasa",
  "profileUrl": "https://www.instagram.com/nasa/", "displayName": "NASA",
  "verified": true, "isPrivate": false,
  "followers": 104398251, "following": 92, "posts": 4882,
  "avgLikes": 457668, "avgComments": 1488, "avgViews": 1514238,
  "engagementRate": 0.004398, "benchmarkEngagementRate": 0.015,
  "engagementBasis": "recent-posts-likes-comments", "postsAnalyzed": 12,
  "authenticityScore": 75, "botLikelihood": 0.25,
  "verdict": "likely-authentic", "confidence": "high",
  "signals": [
    { "name": "engagementRate", "value": 0.004398, "status": "warn", "weight": 3,
      "explanation": "0.44% engagement — 0.29x the 1.5% typical for this audience size. Below the healthy band for the tier." },
    { "name": "engagementVariance", "value": 1.259, "status": "ok", "weight": 2,
      "explanation": "Engagement varies by 126% across recent posts — the spiky pattern of organic reach." }
  ],
  "redFlags": [], "greenFlags": ["..."], "unavailableSignals": [],
  "analyzedAt": "2026-08-15T10:09:49.079Z", "error": null
}
```

```json
{
  "platform": "tiktok", "handle": "charlidamelio",
  "followers": 159343052, "following": 1433, "posts": 3221,
  "totalLikes": 12306793939, "avgLikes": 3820800, "avgComments": null, "avgViews": 2964850,
  "engagementRate": 0.023978, "benchmarkEngagementRate": 0.025,
  "engagementBasis": "lifetime-avg-likes-per-video", "postsAnalyzed": 10,
  "authenticityScore": 100, "botLikelihood": 0, "verdict": "authentic", "confidence": "medium",
  "unavailableSignals": ["likeToCommentRatio", "postingCadence"]
}
```

```json
{
  "platform": "youtube", "handle": "@MrBeast",
  "channelId": "UCX6OQ3DkcsbYNE6H8uQQuVA",
  "followers": 513000000, "following": null, "posts": 997,
  "avgViews": 110166667, "engagementRate": 0.21475, "benchmarkEngagementRate": 0.1,
  "engagementBasis": "recent-video-views-per-subscriber", "postsAnalyzed": 12,
  "authenticityScore": 100, "botLikelihood": 0, "verdict": "authentic", "confidence": "medium",
  "unavailableSignals": ["followerFollowingRatio", "likeToCommentRatio"]
}
```

### Error semantics

- **Bad input** — no valid profile in any list: run fails fast, exit code 1, status message naming the accepted formats. Nothing is charged beyond `actor-start`.
- **Per-profile failure** — extraction errors never abort the run and never appear in the paid dataset. They are collected in the `SUMMARY` key-value record as `{ platform, handle, error }`, so retry logic can pick them up: `GET /v2/key-value-stores/{storeId}/records/SUMMARY`.
- **Partial data** — a profile that resolves but exposes no engagement is pushed with `verdict: null`, `confidence: "insufficient-data"` and populated `unavailableSignals`, and is **not** charged.
- **Zero results** — if every profile fails, the run exits 1 with the first error in the status message.
- `SUMMARY` always contains `{ requested, analyzed, insufficientData, failed, failures, finishedAt }`.

### Use from AI agents (MCP)

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=apricot_blackberry/fake-engagement-detector",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

Works in Claude, Cursor, ChatGPT deep-research connectors and any MCP client; the input schema above is the tool's parameter schema.

### Use from code

```bash
curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~fake-engagement-detector/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profiles":["tiktok:charlidamelio","youtube:@MrBeast"],"maxPostsPerProfile":12}'
```

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/fake-engagement-detector').call({
    profiles: ['tiktok:charlidamelio', 'youtube:@MrBeast'],
    maxPostsPerProfile: 12,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.handle, i.verdict, i.authenticityScore]));
```

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("apricot_blackberry/fake-engagement-detector").call(run_input={
    "profiles": ["tiktok:charlidamelio", "youtube:@MrBeast"],
    "maxPostsPerProfile": 12,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["handle"], item["verdict"], item["authenticityScore"])
```

### Use from automation platforms

- **n8n / Make / Zapier** — pick the Apify integration, choose *Run actor*, select **Fake Engagement & Bot Account Detector**, paste the input JSON above.
- **LangChain / LlamaIndex** — wrap it with the Apify actor tool (`ApifyActorsTool` / `ApifyWrapper`) using actor id `apricot_blackberry/fake-engagement-detector`.
- **Webhooks** — Apify can fire a webhook on `ACTOR.RUN.SUCCEEDED`/`FAILED` for this actor, so a brand-safety pipeline picks up verdicts on run completion without polling.

### Pricing

Pay per event:

| Event | When it fires |
|---|---|
| `actor-start` | Once per run. |
| `profile-analyzed` | Once per profile that returns a real verdict. Profiles with `confidence: "insufficient-data"` and failed extractions are not charged. |

Instagram profiles route through Apify residential proxy, billed to your account at the standard residential rate.

### FAQ

**How do you detect fake followers without follower-list access?**
By the fingerprint that purchased audiences leave in public metrics: engagement far below the norm for the audience size, likes without comments, and suspiciously uniform post-to-post numbers. Every one of those is computed from data the platform publishes on the profile itself.

**Can I check Instagram, TikTok and YouTube in one run?**
Yes — that is the point. Mix platforms freely in `profiles`; each row states its own `platform` and `engagementBasis`.

**What does a good engagement rate look like?**
It depends entirely on audience size, which is why every profile is graded against `benchmarkEngagementRate` for its tier rather than a fixed number. A 1.5% rate is healthy for a 10M-follower account and weak for a 5K one.

**Why is `avgComments` null on TikTok and YouTube?**
Those platforms publish likes and views on the profile surface but not per-post comment counts. The like-to-comment signal is then reported as `unavailable` and excluded from the score — it is never assumed to be zero.

**Is a `likely-fake` verdict proof of fraud?**
It is a statistical assessment of public engagement patterns, with the full evidence in `signals` and `redFlags` so you can review the reasoning behind it.

**How many profiles can I check per run?**
As many as you pass. Profiles are processed sequentially with retries; a 100-profile run is routine.

### Changelog

**1.0** — Initial release. Instagram, TikTok and YouTube extraction; six-signal weighted authenticity scoring with tier-relative engagement benchmarks; typed dataset schema with per-signal breakdown; per-profile pay-per-event pricing.

***

### 中文说明（刷粉检测 / 假粉丝识别）

**给达人打款之前，先查一查。** 输入 Instagram / TikTok / YouTube 创作者账号，输出机器人粉丝/虚假互动评分，帮助你在投放前识别刷粉账号、僵尸粉和虚假互动。

- 覆盖三大平台：Instagram、TikTok、YouTube
- 评分为概率性参考指标，配合人工复核使用
- 按结果计费，批量筛选达人名单成本可控

适合 MCN、达人投放团队、跨境电商卖家在建联和签约前做尽调。Apify 每月 $5 免费额度，无需信用卡。

### For AI agents

Bot-likelihood scoring rows with per-signal breakdown — machine-consumable for automated creator vetting pipelines:

```bash
curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~fake-engagement-detector/run-sync-get-dataset-items?token=$APIFY_TOKEN" -H "Content-Type: application/json" -d '{"profiles": ["https://www.tiktok.com/@natgeo"]}'
```

# Actor input Schema

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

Profiles to analyze. Accepts "instagram:handle", "tiktok:handle", "youtube:@handle", or a plain profile URL (platform auto-detected from the URL).

## `instagramUsernames` (type: `array`):

Convenience list of Instagram usernames (with or without @). Merged with the profiles list.

## `tiktokUsernames` (type: `array`):

Convenience list of TikTok usernames (with or without @). Merged with the profiles list.

## `youtubeChannels` (type: `array`):

Convenience list of YouTube handles (@MrBeast) or channel IDs (UC...). Merged with the profiles list.

## `maxPostsPerProfile` (type: `integer`):

How many recent posts/videos to sample per profile when computing engagement, variance and cadence.

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

Apify Proxy settings. Leave at the default (automatic) — TikTok and YouTube run on datacenter/auto and Instagram automatically uses the residential group, which is billed to your account at the standard residential rate.

## Actor input object example

```json
{
  "profiles": [
    "tiktok:charlidamelio",
    "youtube:@MrBeast"
  ],
  "instagramUsernames": [],
  "tiktokUsernames": [],
  "youtubeChannels": [],
  "maxPostsPerProfile": 12,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per analyzed profile in the default dataset - authenticity score, bot likelihood, weighted signals, and raw metrics.

# 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": [
        "tiktok:charlidamelio",
        "youtube:@MrBeast"
    ],
    "maxPostsPerProfile": 12,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("apricot_blackberry/fake-engagement-detector").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": [
        "tiktok:charlidamelio",
        "youtube:@MrBeast",
    ],
    "maxPostsPerProfile": 12,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("apricot_blackberry/fake-engagement-detector").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": [
    "tiktok:charlidamelio",
    "youtube:@MrBeast"
  ],
  "maxPostsPerProfile": 12,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call apricot_blackberry/fake-engagement-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apricot_blackberry/fake-engagement-detector"
        }
    }
}

```

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/QiCBIek6f7GW6feVW/builds/KhE5T1IksJTQdJsIi/openapi.json
