# Creator Contact & Socials Resolver (`findcreators/creator-contact-resolver`) Actor

Turn creator profile URLs (YouTube, TikTok, Instagram, Linktree, websites) into contact rows: name, followers, bio, every linked social account, emails and website from the public page and its bio-link page. One row per input; blocked pages return an error field instead of failing the run.

- **URL**: https://apify.com/findcreators/creator-contact-resolver.md
- **Developed by:** [Find Creators](https://apify.com/findcreators) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 creator resolveds

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Creator Contact & Socials Resolver

Paste in creator profile links, get back who they are and how to reach them. For every
YouTube channel, TikTok profile, Instagram profile, Linktree / Beacons / other bio-link page
or personal website you give it, the actor reads the public page, opens the bio-link page it
points to, and returns one row with the name, follower count, bio, profile picture, every
other social account it found, emails, and the website.

It reads **public pages only**. It never logs in, never uses cookies, and never touches
private data. If a platform blocks the request, you get a row that says so instead of a
failed run.

### What it deliberately does not do

- **Sponsor and affiliate links are never opened.** Bio-link pages list merch partners and
  sponsors next to the creator's own accounts. A plain website is only read when its domain
  contains the creator's handle or a word of their display name. Other linked sites are still
  listed under `websites`, but their emails and social accounts are never attributed to the
  creator.
- **Single videos and posts are not "socials".** Links to a video, short, reel, tweet or clip
  are dropped; only account pages are returned.

### What you get

One dataset row per input URL:

| Field | Meaning |
| --- | --- |
| `input` | The URL you supplied. |
| `url` | The same URL, normalised. |
| `platform` | `youtube`, `tiktok`, `instagram`, `linktree` (any bio-link service) or `web`. |
| `handle` | Username on that platform (without `@`). |
| `display_name` | Public display name / channel title. |
| `followers` | Followers or subscribers as shown on the public page. `null` if hidden or blocked. |
| `following`, `posts`, `likes` | Extra public counters where the platform shows them. |
| `bio` | Profile description. |
| `profile_image` | URL of the profile picture (hot-linked from the platform's CDN; can expire). |
| `country` | Country/region when the platform exposes it (TikTok, YouTube API). |
| `socials` | Every other social account found: `{platform, url, source, via}`. `via` is `link` (found as a link) or `bio_mention` (an `@name` written in the bio, which may be a collaborator rather than the same person). |
| `emails` | All email addresses found in the bio, bio-link page and website, lower-cased and de-duplicated. |
| `website` | The first personal website found; `websites` lists all of them. |
| `source_urls` | Every page that was actually opened for this row (normalised URLs; `source` on each social uses the same form). |
| `hop_errors` | Bio-link pages / websites that could not be opened, with the reason. |
| `hops_followed`, `pages_fetched` | How far the actor went. |
| `error` | `null` on success. Otherwise why the profile itself could not be read, e.g. `HTTP 403`, `blocked or empty profile page (...)`, `bare handle: ...`. |

#### Example

Input `https://www.tiktok.com/@creator.demo` (a TikTok bio that links to a Linktree):

```json
{
  "input": "https://www.tiktok.com/@creator.demo",
  "platform": "tiktok",
  "handle": "creator.demo",
  "display_name": "Creator Demo",
  "followers": 152300,
  "bio": "Daily picks and behind the scenes\nBusiness: hello@creatordemo.example\nCollabs with @other.creator",
  "profile_image": "https://p16-sign.tiktokcdn-us.com/.../abc123~c5_1080x1080.jpeg",
  "socials": [
    {"platform": "linktree",  "url": "https://linktr.ee/creatordemo",       "source": "https://tiktok.com/@creator.demo", "via": "link"},
    {"platform": "tiktok",    "url": "https://tiktok.com/@other.creator",    "source": "https://tiktok.com/@creator.demo", "via": "bio_mention"},
    {"platform": "youtube",   "url": "https://youtube.com/@creatordemo",     "source": "https://linktr.ee/creatordemo",        "via": "link"},
    {"platform": "instagram", "url": "https://instagram.com/creatordemo",    "source": "https://linktr.ee/creatordemo",        "via": "link"},
    {"platform": "twitter",   "url": "https://twitter.com/creatordemo",      "source": "https://linktr.ee/creatordemo",        "via": "link"}
  ],
  "emails": ["hello@creatordemo.example", "booking@creatordemo.example"],
  "website": "https://creatordemo.example/",
  "source_urls": ["https://tiktok.com/@creator.demo", "https://linktr.ee/creatordemo"],
  "hops_followed": 1,
  "error": null
}
```

### Input

| Field | Default | What it does |
| --- | --- | --- |
| `startUrls` | required | One creator per line. Full URLs. A bare `@name` is rejected because it could be on any platform. |
| `followLinkPages` | `true` | Open the Linktree-style page / website a profile links to and collect what it lists. |
| `maxHops` | `1` (max `2`) | How many levels of bio-link pages and websites to follow. Other social profiles are listed, never opened. |
| `minDelaySeconds` | `3` | Minimum gap between two requests to the same site, plus up to one second of random jitter. |
| `proxyConfiguration` | Apify proxy | Route requests through proxies. Residential proxies make a real difference on TikTok and Instagram. |
| `youtubeApiKey` | none | Optional. Makes YouTube results come from the official Data API instead of page parsing. Never written to output or logs. |

### What it cannot do (read this before buying)

- **It does not discover creators.** You give it URLs; it resolves them. It will not search
  TikTok or Instagram by topic.
- **Instagram is frequently blocked without a logged-in session.** This actor deliberately
  does not log in. Expect many Instagram inputs to come back with `error: "blocked or empty profile page (...)"`, particularly on datacenter proxies. Rows are still returned and are
  not charged.
- **TikTok public profiles show followers, total likes and video count only.** There are no
  per-video views on the public page, so no engagement rate is computed. None is faked.
- **YouTube without an API key** parses the public channel page, which works until YouTube
  changes its markup. With your own free Data API key the subscriber count and description
  come from the official API. The actor does not require a key.
- **Emails are whatever is publicly written** in bios, bio-link pages and websites. Nothing
  is guessed or generated. A creator who publishes no email gets an empty `emails` list.
- **`bio_mention` socials may be other people.** An `@name` in a bio is often a collaborator
  or a brand; it is reported with `via: "bio_mention"` so you can treat it accordingly.
- All scrapers read undocumented embedded page data and break when platforms change their
  pages. When that happens rows come back with an `error` rather than wrong data.

### Pricing

Pay-per-event: **one event per input that resolves successfully** (`creator-resolved`).
Inputs that end with an `error` are not charged. Compute and proxy usage are covered by the
event price, so you are not billed separately for residential proxy traffic.

Why per resolved creator rather than per run or per page: a run's cost is dominated by how
many profiles it reads, and a blocked profile is worth nothing to you, so you should not pay
for it. The price is set to cover the residential-proxy bandwidth and compute that a TikTok
or Instagram profile plus one bio-link page typically consumes.

### Running it locally (without the Apify platform)

From the repository root:

```bash
pip install -r apify_actor/requirements.txt
## No SDK needed for this path: prints one JSON row per line.
python apify_actor/run_local.py --url https://linktr.ee/someone --url https://www.tiktok.com/@someone
python apify_actor/run_local.py --input input.json --out rows.jsonl
```

`input.json` uses the same shape as the actor input:

```json
{"startUrls": ["https://www.youtube.com/@someone"], "followLinkPages": true, "maxHops": 1, "minDelaySeconds": 3}
```

To exercise the real Actor entry point (dataset written under `./storage`):

```bash
## Option A: the SDK's local key-value store
mkdir -p storage/key_value_stores/default
echo '{"startUrls": ["https://linktr.ee/someone"]}' > storage/key_value_stores/default/INPUT.json
python -m apify_actor.src.main
## Option B: env var, no storage setup
ACTOR_INPUT_JSON='{"startUrls": ["https://linktr.ee/someone"]}' python -m apify_actor.src.main
## Rows land in storage/datasets/default/*.json
```

Set `HTTPS_PROXY` in the environment if you need a proxy locally.

### Tests

```bash
pip install pytest
python -m pytest apify_actor/tests -q
```

The tests mock the HTTP layer with realistic page fixtures (TikTok rehydration JSON, Linktree
`__NEXT_DATA__`, an Open Graph website, a 403 page) and check the row shape, the error row for
a blocked profile, and that link pages are followed exactly `maxHops` deep.

### Deploying to Apify

The actor reuses the `creatorscout` scraper package from the repository root. `apify push`
uploads only this directory, so the package is staged in first:

```bash
cd apify_actor
./stage.sh            # copies ../creatorscout into ./creatorscout (verbatim, disposable)
apify push            # builds from .actor/Dockerfile and uploads
./stage.sh --clean    # remove the staged copy so it is never committed
```

Pay-per-event pricing (`creator-resolved`) is configured in the Apify Console under the
actor's Publication settings; it is not part of `actor.json`. When PPE is not configured
(local runs, or before you set it up) the charge call is skipped and logged once.

# Actor input Schema

## `startUrls` (type: `array`):

One creator per line: a YouTube channel, TikTok profile, Instagram profile, Linktree / Beacons / any bio-link page, or a personal website. Full URLs work best (https://www.tiktok.com/@name). A bare '@name' is rejected because the platform is ambiguous.

## `followLinkPages` (type: `boolean`):

When a profile links to a Linktree-style page or a personal website, open it too and collect the socials and emails listed there. Turn off to read only the profile page itself.

## `maxHops` (type: `integer`):

How many pages deep to follow bio-link pages and websites. 1 = profile plus the pages it links to directly. 2 = also the pages those pages link to. Only Linktree-style pages and plain websites are followed; other social profiles are listed, not opened.

## `minDelaySeconds` (type: `integer`):

Polite pacing per host, plus up to one second of random jitter. Lower values run faster but get blocked more often, especially on TikTok and Instagram. Use 0 only with rotating residential proxies.

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

Proxies to route requests through. TikTok and Instagram block datacenter IPs frequently; residential proxies give noticeably better results for those two platforms.

## `youtubeApiKey` (type: `string`):

Optional. With a key, YouTube channels are read through the official Data API (reliable subscriber counts, full description). Without it the public channel page is parsed, which works but breaks whenever YouTube changes its markup. The key is never written to the dataset or logs.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/@MrBeast",
    "https://linktr.ee/mrbeast"
  ],
  "followLinkPages": true,
  "maxHops": 1,
  "minDelaySeconds": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `creators` (type: `string`):

All resolved creator rows as JSON.

## `creatorsCsv` (type: `string`):

The same rows as a CSV download.

# 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 = {
    "startUrls": [
        "https://www.youtube.com/@MrBeast",
        "https://linktr.ee/mrbeast"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("findcreators/creator-contact-resolver").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 = {
    "startUrls": [
        "https://www.youtube.com/@MrBeast",
        "https://linktr.ee/mrbeast",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("findcreators/creator-contact-resolver").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 '{
  "startUrls": [
    "https://www.youtube.com/@MrBeast",
    "https://linktr.ee/mrbeast"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call findcreators/creator-contact-resolver --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,findcreators/creator-contact-resolver"
        }
    }
}
```

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/ycv01kbK4tEJ429Gi/builds/FaakCdhXKNu9VTlNL/openapi.json
