# Social Media Profile Finder - Handles, Links & Follower Counts (`neverempty/social-media-profile-finder`) Actor

For lead and influencer research: a name, brand or company domain comes back as the TikTok, YouTube, Twitch, Telegram, Bluesky, Snapchat, GitHub and Discord accounts behind it, with the follower count and the evidence for each match. All 8 answered on 2026-09-23; 5 give an exact count.

- **URL**: https://apify.com/neverempty/social-media-profile-finder.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.92 / 1,000 profile founds

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?

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

## Social Media Profile Finder - Handles, Links & Follower Counts

For lead and influencer research: give a name, a brand or a company domain and get back the TikTok, YouTube, Twitch, Telegram, Bluesky, Snapchat, GitHub and Discord accounts behind it, each row carrying the follower, subscriber or member count read from the platform itself and the evidence the match rests on. Measured on 2026-09-23 from Apify's datacenter connection: all 8 platforms answered on two reads out of two, and 5 of them publish an exact count. You stop opening eight tabs per prospect, and you stop pasting handles into a second tool to get the numbers.

Export as JSON, CSV or Excel.

Unofficial. Public data only.

### Input

| Field | What it does |
|---|---|
| `names` | One person, brand or company name per line. Each one is searched on the web and turned into a handle that is opened on every platform you picked. |
| `domains` | One website per line. The site's own pages are read for the social links published there, and the domain's label is used as the name to match against. |
| `platforms` | Which of the eight to search. Leave empty for all eight; every row says which were searched. |
| `searchTheWeb` | On by default. Off skips the web search and relies on the handles built from the name and, for websites, on the links published on the site. |
| `includeWeakMatches` | Off by default. On also returns profiles that rest on one piece of evidence, marked `confidence: low`. They are charged like any other row. |
| `country` | Two-letter country code the web search runs from (`us` when left empty). Profile pages are read the same way whatever you pick. |
| `maxResults` | The run stops once this many charged rows have been returned; a free row says what was left out. |
| `maxPagesPerWebsite` | How many pages of each website to open: the home page, then the contact, about, imprint or press pages it links to. |

### What you get

One row per confirmed profile:

| Column | What it is |
|---|---|
| `query`, `queryType` | what you asked for, and whether it was a name or a website |
| `platform`, `platformName` | `tiktok`, `youtube`, `twitch`, `telegram`, `bluesky`, `snapchat`, `github`, `discord` |
| `handle`, `profileUrl` | the handle **as the platform spells it**, and the link |
| `displayName` | the name the platform itself shows for that account |
| `followers`, `followersLabel` | the audience number, and what that platform calls it (followers / subscribers / members) |
| `followersAreRounded` | `false` only where the platform publishes the real figure (see the table below) |
| `followersAsShown` | the number as the page prints it, where the page prints one |
| `secondaryCount`, `secondaryCountLabel` | likes, total views, following, or members online, depending on the platform |
| `postCount`, `postCountLabel` | videos, posts or public repositories, where the platform publishes it |
| `isVerifiedOnPlatform`, `accountType` | where the platform states them; `null` where it does not |
| `confidence`, `matchScore`, `matchReasons` | **why this account is the one you asked for** (below) |
| `foundVia`, `evidenceUrl`, `evidencePageTitle` | where it was found: a link on your website, a search result, or the handle built from the name |
| `searchResultPosition`, `searchUrl` | for a profile taken from web search: its position on the results page, and the search that was run |
| `source`, `status`, `scrapedAt`, `profileCheckedAt` | the Actor name, `ok` for a returned profile, and when the row and the profile were read |
| `queryInput`, `platformsSearched`, `searchCountry` | the entry exactly as you typed it, the platforms this run searched, and the country the search ran from |
| `note` | on free rows only: why nothing was returned |

Entries where nothing could be confirmed, platforms that answered with a bot check, pages a site's robots.txt keeps this Actor out of, and unusable input come back as **free rows that say why**. They are not charged.

### Output

A profile found by opening the handle built from the name, on a platform that publishes the real figure:

```json
{
  "source": "social-media-profile-finder",
  "scrapedAt": "2026-09-25T09:00:00.000Z",
  "query": "Duolingo",
  "queryType": "name",
  "queryInput": "Duolingo",
  "platformsSearched": "tiktok",
  "searchCountry": "us",
  "status": "ok",
  "platform": "tiktok",
  "platformName": "TikTok",
  "handle": "duolingo",
  "profileUrl": "https://www.tiktok.com/@duolingo",
  "displayName": "Duolingo",
  "followers": 18085172,
  "followersLabel": "followers",
  "followersAreRounded": false,
  "followersAsShown": null,
  "secondaryCount": 500318959,
  "secondaryCountLabel": "likes",
  "postCount": 1184,
  "postCountLabel": "videos",
  "isVerifiedOnPlatform": true,
  "accountType": null,
  "confidence": "high",
  "matchScore": 4,
  "matchReasons": [
    "the handle on the platform is exactly what you asked for",
    "the name the platform itself shows is exactly what you asked for"
  ],
  "foundVia": "handle-guess",
  "evidenceUrl": null,
  "evidencePageTitle": null,
  "searchResultPosition": null,
  "searchUrl": null,
  "profileCheckedAt": "2026-09-25T09:00:00.000Z"
}
```

Rows that are **not** charged, and what each one means:

| `status` | What happened |
|---|---|
| `no-profile-found` | every candidate handle for that entry was opened and none could be confirmed |
| `search-unavailable` | the web search could not be confirmed as the answer to your query, so nothing was taken from it |
| `platform-unavailable` | that platform answered with a bot check, refused the request, or the page could not be read; also used when a profile exists but publishes no count |
| `website-unreadable` | a page of your website could not be read, or robots.txt keeps this Actor out of it |
| `invalid-input` | an entry could not be used, with the reason |
| `duplicate` | the same entry appeared twice in one run and was used once |
| `not-checked` | entries or profiles left out because `maxResults` was reached, or beyond the 200-entry limit |
| `budget-reached` | the run hit the maximum total charge you set for it |
| `platforms-not-covered` | the one row every run ends with, naming which platforms were searched and which are not covered |

### How a profile is matched — and why some are not returned

Finding a handle that looks like the name is easy; being sure it is the right account is the hard part. Every returned profile is opened and read, and the match is scored from what the platform itself shows:

| Evidence | Points |
|---|---|
| the link is published on the website you gave | +3 |
| the handle on the platform is exactly the name you asked for | +2 |
| the same, but the platform shows no name at all for the account | +1 |
| the name the platform shows is exactly the name you asked for | +2 |
| the name the platform shows contains every word you asked for | +1 |
| the profile came up in the search results for your query | +1 |
| the name the platform shows says unofficial, parody or fan page | −1 |
| the name the platform shows shares no word with your query | −2 |

`confidence` is `high` at 3 points or more, `medium` at 2, `low` at 1. **Rows scoring 0 or less are never returned or charged**, and `low` rows are held back unless you switch on *Include weak matches*. `matchReasons` spells out every point in plain English, so you can disagree with the score without re-running anything.

**When the platform itself says the account is not the real one, that counts against it.** A display name containing *unofficial*, *parody*, *fan page*, *fan club* or *fake* costs a point, which is enough to push a bare handle match down to `low` and out of your results: `discord.gg/duolingo` calls itself "Unofficial Duolingo", and on 2026-09-23 it was being returned as a `medium` match until this rule was added. The word only counts when it is not part of what you asked for, so a company genuinely called *Fanatics* is not punished for it.

**`confidence` still measures the evidence, not officialness.** A fan channel that took the brand's exact name and says nothing about it scores like the brand's own account, because from the outside the two are identical. Where a platform publishes a verified badge — Telegram and Discord do — `isVerifiedOnPlatform` carries it, and `displayName` next to the follower count usually settles it at a glance: in that same run, `t.me/duolingo` came back as "DUOLINGO 💚" with 9,915 subscribers and `isVerifiedOnPlatform: false`, against Duolingo's own 18,087,039 on TikTok. This Actor hands you those facts; it does not decide for you which account a brand endorses.

This is deliberately strict, and the strictness is measured rather than guessed. In a run on 2026-09-23 for "MrBeast" and "Duolingo", every account that really belonged to them carried a display name on the platform; the three candidates where the platform showed **no** name at all — a GitHub account with 9 followers, and two Bluesky accounts with 219 and 84 — were all someone else holding the handle. So a bare handle match with no name behind it is worth one point, not two, and stays out of your results unless you ask for weak matches. Equally, `github.com/nike` existing does not make it Nike's account: a handle that matches while the display name has nothing in common with your query is scored down, not up.

**A profile is returned once, even when three different routes found it.** The identity used for that is the one the platform itself publishes — a YouTube channel ID, a Bluesky DID, a Discord server ID — not the text you typed or the handle this Actor guessed. Without it you would pay twice for one account: `youtube.com/channel/UCX6OQ3…` from a search result and `youtube.com/@mrbeast` from the name are the same channel, and `discord.gg/mrbeast` and `discord.gg/MrBeast` are the same server. When routes agree, their evidence is added together and the row's score goes up.

### Platforms, and what their numbers really are

| Platform | What is read | Number | Exact? |
|---|---|---|---|
| TikTok | `tiktok.com/@handle` | followers | **exact** |
| Twitch | `twitch.tv/handle` | followers | **exact** |
| Telegram | `t.me/handle` | subscribers or members (the label is in the row) | **exact** |
| Bluesky | the public AT Protocol profile API | followers | **exact** |
| GitHub | the official public REST API | followers | **exact** |
| YouTube | the channel's About panel | subscribers | rounded by YouTube to three significant figures (`518M subscribers`) |
| Snapchat | `snapchat.com/@handle` | subscribers | rounded to the nearest hundred by Snapchat; a profile showing `0` is not publishing a number, so this Actor returns no count rather than "0 subscribers" |
| Discord | the public invite endpoint | members, plus members online | Discord itself calls these *approximate* |

`followersAreRounded` says which of the two a given row is. Nothing here is estimated by this Actor: a number is either read from the page or left empty.

**Instagram, Facebook, LinkedIn, X/Twitter and Threads are not covered.** Measured on 2026-09-23: Instagram's profile endpoint answers a datacenter connection with HTTP 429, and a Threads profile page carries no follower number at all. Rather than work around a block or guess a number, this Actor leaves those platforms out and says so in a free row on every run. If those two are what you need, this is not the right Actor for you.

### How accounts are found

Three routes, and every row says which one (or ones) found it:

1. **`website-link`** — for an entry in *Company websites*, the site's own pages are read (home page first, then the contact, about, imprint or press pages it links to) and the social links published there are taken. A link the company publishes itself is the strongest single piece of evidence there is, but it is not proof on its own: company sites also link to partners, to staff and to the Discord of a tool they use, so a linked account whose name has nothing in common with the company is scored down, not up.
2. **`bing-result`** — the first page of Bing results for your query is read and any profile URL on it is taken. Only the first page: paging does not work on this endpoint and returns the same ten results.
3. **`handle-guess`** — the handle built from the name (`Mr. Beast` → `mrbeast`, `@mrbeast` on YouTube, `mrbeast.bsky.social` on Bluesky) is opened on every platform you picked. This is what finds accounts no search engine puts on the first page.

For an entry in *Company websites*, the name used for matching is the domain's own label — `duolingo.com` becomes `duolingo` — while the `query` column keeps the domain exactly as you typed it.

Every candidate from all three routes is opened and read before anything is returned, so a dead handle or a 404 never becomes a row.

#### One thing worth knowing about web search

Bing answers some automated visitors with **the results of a completely different query**, on a page whose title and search box still show your words. A results page is therefore used only when the search engine highlighted enough of your words inside the results themselves; pages that fail that check are discarded and a free row says so, and the handle-based checks still run. On top of that, anything taken from a search result still has to match the name the platform shows before it can be returned, so a wrong results page produces no rows rather than wrong rows.

### Rules this Actor follows

- **robots.txt is read for every website you hand it**, before any of its pages are opened. Pages it disallows are not opened; a free row says so instead, and a site whose robots.txt answers with a server error is treated as disallowed outright.
- **Every platform endpoint here was checked against that platform's own robots.txt before it was added.** Twitch's GraphQL endpoint disallows everything, so this Actor reads the public channel page instead; Discord allows `/api/v*/invite` but not the rest of `/api/`, so the invite endpoint is the only Discord window it uses. One exception, stated plainly: the optional web-search step reads `bing.com/search`, which Bing's robots.txt disallows and which every Bing scraper on this store reads. Turning *Also use web search* off skips it entirely, and the rest of the Actor works without it.
- **Bot checks, HTTP 403 and HTTP 429 are never worked around.** They are reported as "this platform could not be checked", which is not the same thing as "this account does not exist".
- **No personal data.** Rows carry public account links, public counts and the display name the platform publishes. Bios, avatars, locations, e-mail addresses and phone numbers are not collected or returned.
- **Nothing is guessed.** A number is read or it is `null`. An account is confirmed or it is a free row.
- No login, no cookies, no accounts of ours anywhere in the chain.

### Pricing

Pay per profile returned. Free rows — the ones explaining why something was not returned — are not charged, and neither are candidate handles that turned out not to exist. A run that finds nothing costs nothing.

### Limits and honest caveats

- Up to 200 entries per run across *Names* and *Company websites*. Split longer lists across runs; a free row tells you when something was left out.
- **GitHub's public API allows 60 requests an hour without a token**, which is one request per entry. Past that, GitHub answers with a rate-limit error and those entries come back as free `platform-unavailable` rows naming the limit — not as "no account". The other seven platforms have no published per-hour quota, and requests are spaced out to stay off them.
- A profile is charged **once per run**, even if you list the same company both by name and by website: the second time it is recognised by the identity the platform publishes and comes back as a free `duplicate` row naming the entry it was returned for.
- The handle is built from Latin letters and digits, so a name written only in another script cannot produce one; the web-search and website routes still work for those.
- Bing paging does not work, so only the first ten organic results are read.
- Platform pages change. Every parser here is pinned to a captured copy of the real page in the test suite, so a change shows up as a free "could not be read" row rather than as a wrong number.
- A brand with no presence on these eight platforms comes back with a free row and no charge. That is the honest answer, not a failure.

# Actor input Schema

## `names` (type: `array`):

One person, brand or company name per line. For each one the Actor opens the handle built from that name on every platform you picked, and reads the first page of web search results for it. Latin letters and digits are used to build the handle, so a name written only in other scripts is skipped with a free row saying so. A repeated name is used once and gets a free 'duplicate' row. If you leave both this field and 'Company websites' out, the example name 'MrBeast' is used and every row says so in its query column. Up to 200 entries per run across both fields.

## `domains` (type: `array`):

One website per line: a domain (example.com) or any URL on it. The Actor reads the site's own pages, takes the social links published there, and also tries the handle built from the domain's own label (duolingo.com is matched as 'duolingo'). A link published on the company's own website is the strongest single piece of evidence there is, but it is not proof on its own: company sites also link to partners, to staff and to the Discord of a tool they use, so a linked account whose name has nothing in common with the company is scored down, not up. robots.txt is read first and pages the site asks robots to stay out of are not opened.

## `platforms` (type: `array`):

tiktok, youtube, twitch, telegram, bluesky, snapchat, github, discord. Leave the list empty to search all eight; every row says which platforms were searched. Instagram, Facebook, LinkedIn, X/Twitter and Threads are not covered: their profile pages refuse automated reads from datacenter connections or publish no follower number, and this Actor does not claim to cover them. Counts are exact on TikTok, Twitch, Telegram, Bluesky and GitHub; YouTube, Snapchat and Discord publish only a rounded or approximate number, and every row says which it is.

## `searchTheWeb` (type: `boolean`):

On, the Actor also reads the first page of Bing results for each entry and takes any profile URL it finds there. A results page is used only when the search engine highlighted enough of your words in the results; pages that could not be confirmed as the answer to your query are discarded, because they are somebody else's search results. Turning this off makes runs faster and cheaper to your Apify proxy usage, and the Actor then relies on the handles built from the name and, for websites, on the links published on the site.

## `includeWeakMatches` (type: `boolean`):

Off, a profile is returned only when the platform itself shows a handle or a name that matches what you asked for, or when the link is published on the website you gave. On, profiles that rest on weaker evidence are returned too, marked confidence 'low', with the reasons in the matchReasons column. Weak matches are charged like any other row, so leave this off if you do not want to pay for accounts that may belong to somebody else.

## `country` (type: `string`):

Two-letter ISO country code used for the web search (us, gb, de, jp, ...; uk is read as gb). Search results differ by country. The profile pages themselves are read the same way whatever you pick. Left empty, us is used and every row says so in searchCountry.

## `maxResults` (type: `integer`):

The run stops once this many charged rows have been returned, and a free row says how many profiles and entries were left out.

## `maxPagesPerWebsite` (type: `integer`):

How many pages of each website in 'Company websites' to open: the home page first, then the contact, about, imprint or press pages it links to, which is where social links usually sit. Only pages on the same site are opened, and only ones robots.txt allows.

## Actor input object example

```json
{
  "names": [
    "MrBeast",
    "Duolingo"
  ],
  "domains": [
    "duolingo.com"
  ],
  "platforms": [
    "tiktok",
    "youtube",
    "twitch",
    "telegram",
    "bluesky",
    "snapchat",
    "github",
    "discord"
  ],
  "searchTheWeb": true,
  "includeWeakMatches": false,
  "country": "us",
  "maxResults": 1000,
  "maxPagesPerWebsite": 5
}
```

# Actor output Schema

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

One row per confirmed profile: platform, handle, profile URL, the display name the platform itself shows, the follower/subscriber/member count with a flag saying whether the platform rounds it, a second count and a post count where the platform publishes them, and the evidence the match rests on (confidence, match score, reasons, how it was found, the page or search result it came from). Entries with nothing confirmed, platforms that answered with a bot check, pages robots.txt keeps this Actor out of, and invalid input come back as free rows that say why.

# 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 = {
    "names": [
        "MrBeast",
        "Duolingo"
    ],
    "domains": [
        "duolingo.com"
    ],
    "platforms": [
        "tiktok",
        "youtube",
        "twitch",
        "telegram",
        "bluesky",
        "snapchat",
        "github",
        "discord"
    ],
    "country": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/social-media-profile-finder").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 = {
    "names": [
        "MrBeast",
        "Duolingo",
    ],
    "domains": ["duolingo.com"],
    "platforms": [
        "tiktok",
        "youtube",
        "twitch",
        "telegram",
        "bluesky",
        "snapchat",
        "github",
        "discord",
    ],
    "country": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/social-media-profile-finder").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 '{
  "names": [
    "MrBeast",
    "Duolingo"
  ],
  "domains": [
    "duolingo.com"
  ],
  "platforms": [
    "tiktok",
    "youtube",
    "twitch",
    "telegram",
    "bluesky",
    "snapchat",
    "github",
    "discord"
  ],
  "country": "us"
}' |
apify call neverempty/social-media-profile-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/social-media-profile-finder"
        }
    }
}
```

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/eaelxnYvmGi5KGUxM/builds/9yRDggKOnWlKM5qLx/openapi.json
