# TikTok Brand Presence Mapper - Followers for Clay (`mambalabs/tiktok-brand-presence-mapper`) Actor

Resolves a TikTok handle or a company domain to the brand account and returns follower, following, likes and video counts, verification status, display name and bio. Flat, Clay ready, one row per company.

- **URL**: https://apify.com/mambalabs/tiktok-brand-presence-mapper.md
- **Developed by:** [Mamba Labs](https://apify.com/mambalabs) (community)
- **Categories:** Lead generation, Automation, Social media
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 company checkeds

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/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

### 🔎 What can TikTok Brand Presence Mapper do?

Give it a TikTok handle, or a company domain, and it returns that brand's TikTok account with follower, following, likes and video counts, verification status, display name and bio, in one flat row.

No login, no API key, no browser. It reads the same public profile page a logged out visitor sees.

### 💡 Why use TikTok Brand Presence Mapper?

**It starts from a domain, and nothing else on the Store does.** Every TikTok profile scraper on the Apify Store requires a handle or a profile URL as input. If you have a list of company domains, that is precisely the thing you do not have, and TikTok is the hardest platform to guess it for: measured across fifteen B2B homepages, only 4 of 14 declare a TikTok link at all, and the handles that were declared included `@supabase.com` and `@HubSpot`, neither of which is the domain stem.

**It tells you when the number is rounded.** TikTok rounds every count above roughly ten thousand to three significant figures: 2,000,000 and 927,300 are what the platform serves, not what the account has. `tiktok_followers_exact` is false on those rows so you know before you sum a column.

**A blocked fetch says blocked.** TikTok's bot detection returns HTTP 200 with a small page, so a naive scraper reports success and no followers. This one detects the downgrade page and reports `blocked`, which means unknown, not zero.

#### 🧭 Cheap route first, expensive route only when needed

The profile page is fetched over datacenter proxy first, which measured 5 of 6 on real brand handles and costs about 40 times less than residential. The sixth returned TikTok's bot detection page, and only that one is retried over residential. `tiktok_fetch_route` on the row tells you which one answered.

Turn `escalateOnBlock` off if you would rather accept the occasional `blocked` row than pay for the retry.

### 📋 What data can TikTok Brand Presence Mapper extract?

| Field | Type | Meaning |
|---|---|---|
| `degraded` | boolean | True when this row could not be produced normally, for example the company site was unreachable and no discovery could run. A degraded row is never charged. |
| `degradation_reason` | string | null | Why the row is degraded, in plain words. Null on a normal row. |
| `company_domain` | string | null | The company domain this row is about, normalized. Null when only a handle or a name was supplied. This is the join key across the whole Mamba Labs fleet. |
| `company_name` | string | null | The company name as supplied or derived. Improves search accuracy and is what the identity gate matches against. |
| `tiktok_url` | string | null | Canonical TikTok profile URL, or null when no trustworthy account was found. |
| `tiktok_handle` | string | null | The resolved handle, without the leading @. |
| `tiktok_followers` | integer | null | Follower count as TikTok reports it. Rounded by TikTok above roughly ten thousand: read tiktok\_followers\_exact before treating this as a measurement. |
| `tiktok_followers_exact` | boolean | null | True when the count is below TikTok's rounding floor and is therefore exact. False when TikTok rounded it, which it does to three significant figures above roughly ten thousand. Never sum a column where this is false without saying so. |
| `tiktok_following` | integer | null | How many accounts this account follows. |
| `tiktok_likes` | integer | null | Cumulative likes across the account's videos, as TikTok reports it. Rounded on the same basis as followers. |
| `tiktok_video_count` | integer | null | Number of public videos on the account. Zero is a real answer and means a registered but unused account. |
| `tiktok_verified` | boolean | null | TikTok's blue check. False means we read the page and it is not verified. Null means we could not read the page. |
| `tiktok_display_name` | string | null | The account display name as set by its owner. |
| `tiktok_bio` | string | null | The account bio text. |
| `tiktok_bio_link` | string | null | The outbound link on the account bio. When its host is the company domain, the account is the company's own and this row needed no guessing: it is the one field on a TikTok profile that is independent of both the handle and the display name, which is what makes it usable as proof. |
| `tiktok_discovery` | string | null | How the account was found: input\_handle, homepage\_sameas, homepage\_link, search or pattern\_guess. On TikTok specifically, treat pattern\_guess with suspicion: measured handles include supabase.com and HubSpot, neither of which is the domain stem. |
| `tiktok_fetch_route` | string | null | Which proxy route answered: datacenter or residential. Datacenter is cheap and answers most requests; a residential value means the cheap route was blocked and the fetch was escalated. |
| `tiktok_status` | string | ok, not\_found, not\_extractable, blocked, identity\_mismatch, auth\_failed or skipped. Read this before reading any count. blocked means TikTok served its bot detection page and the answer is unknown, which is not the same as the account not existing. |
| `run_date` | string | ISO 8601 timestamp of this run. Social counts move, so a row without a date is a number with no shelf life. |

`tiktok_video_count: 0` and `tiktok_followers: 0` are real answers. They mean a registered account that was never used, which is a different qualification signal from having no account at all, and a different one again from `null`, which means we could not read the page.

### 🛠️ How to find a company's TikTok account

1. **If you have the handle**, put it in `handle`. That is the cheapest and most reliable path, and it skips discovery entirely.
2. **If you only have a domain**, put it in `company_domain`. The actor reads the company's own homepage for a declared TikTok link, then falls back to a web search, then to a pattern guess, and identity checks anything it did not get from the company itself.
3. **Supply both** when you can. The domain is what the identity gate checks a discovered handle against.
4. For a list, pass an array of objects with the same fields.

#### 🧪 Using it in Clay

Add an **Enrichment > Apify** column, pick this actor, and map either your handle column or your domain column. Every output field arrives flat and snake\_case.

If your table already holds TikTok handles from another source, map `handle` and you pay only for the fetch. If it holds domains, map `company_domain` and the actor does discovery too.

### 💵 How much does it cost?

Pay per event. You are charged for output, never for input.

| Event | Fires when | Price |
|---|---|---|
| `company-checked` | Once per company for which the discovery cascade completed and a non degraded row was produced, whether or not a TikTok profile was found. Does not fire on a degraded row, because on a degraded row no discovery was performed. | $0.0040 |
| `profile-resolved` | Once per company whose candidate TikTok URL passed the identity gate. Fires on the validation work, not on a populated count. A candidate dropped as an impersonator does not charge: the work was done and the honest answer is that there is no such profile. | $0.0030 |
| `follower-count-extracted` | Once per company where a numeric follower count was read off the public page. Does not fire on not\_extractable, blocked, identity\_mismatch or url only runs. | $0.0025 |

A run that resolves a profile but hits TikTok's bot detection charges `company-checked` and `profile-resolved` and NOT `follower-count-extracted`. You are never charged for a number you did not receive.

Setting `includeFollowerCounts` to `false` resolves the URL only and never fires the extraction event.

**What the same coverage costs bought a la carte:** clockworks/tiktok-profile-scraper, the largest TikTok presence actor on the Store at 4,865 thirty day users, charges $0.0030 per result plus a $0.0040 follower add on, so $0.0070 for the same fields. It requires the handle or the profile URL as input. No Store actor resolves a company domain to a TikTok handle, which is the step this actor performs before it fetches anything.

### ⌨️ Input

| Field | Type | Required | Meaning |
|---|---|---|---|
| `handle` | string | no | The TikTok handle, with or without the leading @, for example shopify. This is the PRIMARY input: only 4 of 14 B2B homepages declare a TikTok link, so supplying the handle is both cheaper and far more reliable than asking the actor to find it. |
| `company_domain` | string | no | Bare company domain, for example shopify.com. Used to FIND the handle when you do not have one, and to identity check a handle that discovery guessed. Supplying both is the best case. |
| `company_name` | string | no | Optional. Improves search accuracy and is what the identity gate checks a discovered account against. |
| `includeFollowerCounts` | string | no | When "true" (default) the profile page is fetched and the counts are extracted. Set "false" to resolve the profile URL only, which is cheaper and needs no proxy. Sent as a string for Clay compatibility. |
| `skipCache` | string | no | When "false" (default) a successful lookup is cached for seven days and reused. Set "true" to force a fresh fetch. Sent as a string for Clay compatibility. |
| `escalateOnBlock` | string | no | TikTok answers most requests over cheap datacenter routing and blocks a minority with a bot detection page. When "true" (default) a blocked fetch is retried once over residential, which recovers most of them at a higher cost per recovered row. Set "false" to accept the block and return status blocked. Sent as a string for Clay compatibility. |

```json
{
  "handle": "shopify",
  "company_domain": "shopify.com",
  "includeFollowerCounts": "true"
}
```

### 📤 Output

One flat, snake\_case row per company. No nested objects, so it drops straight
into Clay, a spreadsheet or a warehouse table without a flattening step.

```json
{
  "degraded": false,
  "degradation_reason": null,
  "company_domain": "shopify.com",
  "company_name": "Shopify",
  "tiktok_url": "https://www.tiktok.com/@shopify",
  "tiktok_handle": "shopify",
  "tiktok_followers": 2000000,
  "tiktok_followers_exact": false,
  "tiktok_following": 249,
  "tiktok_likes": 24900000,
  "tiktok_video_count": 915,
  "tiktok_verified": true,
  "tiktok_display_name": "Shopify",
  "tiktok_bio": "The entrepreneurship company",
  "tiktok_bio_link": "https://shopify.com",
  "tiktok_discovery": "input_handle",
  "tiktok_fetch_route": "datacenter",
  "tiktok_status": "ok",
  "run_date": "2026-08-22T09:00:00.000Z"
}
```

#### false versus null, and why the difference matters

`false` means we looked and the answer is no. `null` means we could not look,
or the platform withheld it. They are never interchangeable in this output. If
you filter for companies with no presence on this platform, filter on `false`,
because `null` rows are unknown rather than absent and including them will
overstate your list.

### 💡 Tips

- **Supply the handle when you have it.** TikTok handles are the least guessable in this family.
- **Read `tiktok_followers_exact` before you sum.** A column of TikTok follower counts is mostly TikTok's rounding above ten thousand.
- **Treat `pattern_guess` rows with suspicion on this platform specifically.** The measured real handles include `supabase.com` and `HubSpot`; the stem guess would have missed both.
- **`blocked` is worth retrying later.** It is a transient refusal, not an answer.

### ⚠️ Known limits

- **No videos, comments, hashtags or search.** This actor returns account level presence and counts. It does not scrape content. Most of the TikTok demand on the Apify Store is for content scraping, and this is deliberately not that.
- **Counts above roughly ten thousand are rounded by TikTok.** There is no exact figure on the page to read. `tiktok_followers_exact` marks every row where that applies.
- **A minority of fetches are blocked.** Measured at one in six on the datacenter route. The residential retry recovers most of them and `blocked` is the honest answer for the rest.
- **Discovery from a domain is genuinely hard here.** 4 of 14 B2B homepages declare a TikTok link. Expect `not_found` more often than on LinkedIn or YouTube, and expect it to be correct.
- **Follower counts are a public vanity metric,** a proxy for reach rather than a measurement of it.

### ❓ FAQ

**Do I need a TikTok account or an API key?**
No. This reads the public profile page.

**Why is `tiktok_followers_exact` false on most rows?**
Because TikTok rounds. It serves 2,000,000 and 927,300, not the underlying integers. Below roughly ten thousand it serves the real number and the flag is true.

**What does `blocked` mean?**
TikTok served its bot detection page instead of the profile. The account may well exist and have followers; we could not see them. It is not the same as `not_found`.

**Can I pass a domain instead of a handle?**
Yes, in `company_domain`. Be aware that TikTok is the hardest platform in this family to discover from a domain, for the reasons in Known limits.

**Why is this priced above `clockworks/tiktok-profile-scraper`?**
It is not, for the same fields: that actor is $0.0030 per result plus a $0.0040 follower add on, which is $0.0070. It also requires the handle. This actor resolves the handle from a domain first, and no Store actor sells that step.

### 🧩 Want other GTM data?

Mamba Labs builds a fleet of GTM enrichment actors that share one flat,
Clay-ready output convention, so their rows join on `company_domain` with no
cleaning step:

- **Company Firmographic Enricher** headcount, industry, location and revenue band from a domain, joins on `company_domain`.
- **Domain to LinkedIn URL Resolver** the company LinkedIn URL for domains where the social mapper found none.
- **GTM Hiring Signal Scraper** open go to market roles, which is the buying signal a follower count is a proxy for.
- **Company Social Presence Mapper** every platform in one row when you want breadth rather than depth on one network.

Full fleet: [apify.com/mambalabs](https://apify.com/mambalabs)

### 🆘 Support

Issues, field requests and bug reports: open an issue on the actor's Issues tab.
Mamba Labs reads every one.

# Actor input Schema

## `handle` (type: `string`):

The TikTok handle, with or without the leading @, for example shopify. This is the PRIMARY input: only 4 of 14 B2B homepages declare a TikTok link, so supplying the handle is both cheaper and far more reliable than asking the actor to find it.

## `company_domain` (type: `string`):

Bare company domain, for example shopify.com. Used to FIND the handle when you do not have one, and to identity check a handle that discovery guessed. Supplying both is the best case.

## `company_name` (type: `string`):

Optional. Improves search accuracy and is what the identity gate checks a discovered account against.

## `includeFollowerCounts` (type: `string`):

When "true" (default) the profile page is fetched and the counts are extracted. Set "false" to resolve the profile URL only, which is cheaper and needs no proxy. Sent as a string for Clay compatibility.

## `skipCache` (type: `string`):

When "false" (default) a successful lookup is cached for seven days and reused. Set "true" to force a fresh fetch. Sent as a string for Clay compatibility.

## `escalateOnBlock` (type: `string`):

TikTok answers most requests over cheap datacenter routing and blocks a minority with a bot detection page. When "true" (default) a blocked fetch is retried once over residential, which recovers most of them at a higher cost per recovered row. Set "false" to accept the block and return status blocked. Sent as a string for Clay compatibility.

## `source_tag` (type: `string`):

Internal attribution tag set by Mamba Labs on published task examples. Not required, and nothing depends on it. Leave it empty.

## Actor input object example

```json
{
  "handle": "shopify",
  "includeFollowerCounts": "true",
  "skipCache": "false",
  "escalateOnBlock": "true"
}
```

# Actor output Schema

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

Dataset of one flat row per company, with per platform status so a blocked fetch never reads as a zero.

# 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 = {
    "handle": "shopify",
    "company_domain": "",
    "company_name": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("mambalabs/tiktok-brand-presence-mapper").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 = {
    "handle": "shopify",
    "company_domain": "",
    "company_name": "",
}

# Run the Actor and wait for it to finish
run = client.actor("mambalabs/tiktok-brand-presence-mapper").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 '{
  "handle": "shopify",
  "company_domain": "",
  "company_name": ""
}' |
apify call mambalabs/tiktok-brand-presence-mapper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mambalabs/tiktok-brand-presence-mapper"
        }
    }
}

```

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/fco8FvoJcSafbeyd1/builds/wI1ok5nUM3Dnh0bpf/openapi.json
