# FanCentro Profile Scraper (`dirtycode/fancentro-profile-scraper`) Actor

Extract public FanCentro creator profile data: display name, bio, avatar/cover, follower and subscriber counts, post/video counts, and subscription perks. Public data only, no login.

- **URL**: https://apify.com/dirtycode/fancentro-profile-scraper.md
- **Developed by:** [Dirtycode](https://apify.com/dirtycode) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 94.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$25.00 / 1,000 creator profile scrapeds

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

## FanCentro Profile Scraper

Extract **public** FanCentro creator profile data at scale — no login required.

### What you get per creator

| Field | Description |
|---|---|
| `username`, `profileUrl` | Public handle and profile link |
| `displayName` | The creator's display name |
| `creatorId` | Stable numeric ID — safe to key on when a handle changes |
| `bio` | Profile text |
| `imageLinksExpireAt` | ISO-8601 deadline for `avatar` and `cover` — the earlier of the two. `null` if the URL format changed and we could not read one. This is the exact per-creator figure; the ~52-59 minute range below is the population, not your row. |
| `avatar`, `cover` | Image URLs — **see the note below, these expire** |
| `followers`, `subscribers` | Audience counts |
| `postsCount`, `videosCount` | Content counts |
| `hasSubscriptions` | Whether the creator offers a paid subscription |
| `perks` | What a subscription includes: `exclusiveContent`, `directMessages`, `clipDiscount` |
| `scrapedAt` | ISO-8601 timestamp of when this row was read |

**What varies and what does not**, measured across 120 distinct live creators so you can plan a
segment before paying for one: `exclusiveContent` split 85/35, `clipDiscount` 36/84, and
`directMessages` was **true for all 120** — real, but useless to filter on. `cover` was present
for 50 of 120; `bio` and `avatar` for all of them.

📏 **`bio` is short — plan for that before you build on it.** Measured on the same 120 creators:

| bio length | share |
|---|---|
| median | **35 words** |
| ≥ 25 words | 75% |
| ≥ 50 words | 23% |
| ≥ 120 words | 3% |

We return the bio exactly as FanCentro publishes it and never pad it. If your use case needs
long-form text — indexable landing pages, content generation, semantic search — **most profiles
will not carry enough on their own.** That is a property of the source, not of this Actor, and
you should know it before you pay per profile rather than after.

🤖 **About 1 in 4 bios is FanCentro's own template, not the creator's writing.** Measured on 120
live creators, 2026-07-30: **31 of 120 (26%)** open with stock phrasing — "Welcome to my VIP
space! I'm …", "✨ This is my only official Fancentro", "Step inside to unlock my premium feed".
They are genuine profile text and we return them verbatim, but they are auto-generated around the
creator's name and post count rather than written by them.

It matters if you are doing anything language-shaped — tone analysis, clustering, embeddings,
"find creators who describe themselves as X" — because a quarter of your corpus will cluster on
FanCentro's copywriting instead of on the creators. Filter on those phrases if that is your use
case. We deliberately do **not** ship a `bioIsTemplate` flag: the phrase list would go stale
silently as FanCentro adds variants, and a flag that quietly stops being true is worse than one
you maintain yourself against text you can see.

⏱️ **`avatar` and `cover` are short-lived links — treat them as valid for well under an hour.**
FanCentro serves images from a CDN behind a signed URL with a built-in expiry. The remaining
lifetime at the moment you receive a row **varies**: it is set when FanCentro renders the page,
not when we read it, so a cached page yields a shorter window. Observed in practice: roughly
**52 to 59 minutes** from `scrapedAt`, and it can be less. After expiry the link returns HTTP
**410 Gone** — not 403. Measured 2026-08-06 on a link whose deadline had passed 16 hours earlier:
HEAD and ranged GET both return 410. A **403** is a different answer: it is what the CDN says when
the token is *malformed*, verified by tampering with the epoch and with the signature separately on
a still-valid URL. So `410 = expired`, `403 = rejected signature`, and an ingest pipeline written
to "treat 403 as expired" will misfile every genuinely expired link. The signature cannot be
stripped or extended in either case.

⚠️ **Re-running within ~10 minutes does NOT get you a fresh link — after that it usually does.**
Measured across two days:

| gap between scrapes | what you get |
|---|---|
| 45 s – ~8 min | the **identical** signature and deadline — the render is cached upstream |
| ~10 min and beyond | a **new** signature with a full ~60 minutes of life |

So the honest rule is narrower than "re-scraping is not a workaround", which is what this section
said until 2026-08-06: a retry inside the cache window buys you nothing, a retry outside it does.

**Either way, mirror the image at ingest.** Re-scraping to refresh a link costs another paid row
per creator, and the cache window means it may silently do nothing. Storing the URL and fetching
it hours later is the one usage this field cannot support.

**How much time you actually have, measured on a 174-profile run:**

| | remaining life when you read the dataset |
|---|---|
| first row of the run | **51 min** |
| last row of the run | 59 min |
| shortest of all 174 | 49 min |

The first row is the one that matters — it was fetched earliest, and the deadline is absolute, so
a long run eats its own margin. At the observed 0.43 s/profile a 500-row run takes **3.6 minutes**
and its first row still has ~45 minutes left. That is why `maxResults` is capped at 500: a
single run cannot get long enough to outlive its own image links. Walking the whole catalogue
uses `discoverOffset` across several short runs instead of one long one — which keeps every
batch's links alive by construction rather than by luck.

**If you need the images, download them as part of the same run.** Do not store these URLs and
fetch them later. We would rather state this plainly than sell you links that quietly break.

**Not included, because FanCentro does not publish it:** subscription *price* is only
visible behind a login, so it is not in this dataset. Neither is a photo count or a
verified flag — FanCentro's public profile payload has no such fields. We would rather
omit a field than ship one that is always the same value.

### Input

```json
{
  "usernames": ["nicolkremers", "xev-bellringer"],
  "discover": true,
  "maxResults": 10,
  "proxy": ""
}
```

- **usernames** — FanCentro handles (the part after `fancentro.com/`).
- **discover** — also pull creators from FanCentro's public creator listing, paging through it
  until your `maxResults` is met. **Measured 2026-08-05: the catalogue is 8,185 creators**,
  reached in 17 runs of 500 by following `nextOffset`. Two independent full walks returned the
  identical set — so the sweep neither skips nor repeats anyone, and you do not need to dedupe.
  On by default, so a run with no input at all still returns creators.
  ⚠️ Every discovered creator is a **billable result**, up to your `maxResults`.
- **discoverOffset** — where in the listing discovery starts. **This is what lets a second run
  return different creators than the first.** Leave at 0 for the most trending creators; to sweep
  the catalogue, set it to the `nextOffset` the previous run reported in its log.
- **maxResults** — hard cap on profiles returned, and therefore on what the run costs.
  Defaults to **10** so a first run is cheap; the maximum is **500**. At $0.025 per profile,
  the default run costs **$0.25**.
- **proxy** — optional; your own proxy URL (a public HTTP/HTTPS proxy). Leave empty to use
  the default.

### Output

One dataset item per creator that was successfully read. Use it for market research, creator
discovery, and lead generation.

Handles that could not be read are **not** silently dropped: the run's status message and the
`RUN_SUMMARY` record report how many you requested, how many were delivered, how many were not
found or unreachable, and how many were skipped by the `maxResults` cap — so you can tell
"this creator does not exist" apart from "we capped your run".

If a run returns nothing at all, it **fails** rather than reporting success with an empty
dataset, and nothing is charged.

### Pricing

Pay per result: **$0.025 per profile scraped**. You only pay for profiles actually delivered
to your dataset — failed handles and capped handles cost nothing.

### Notes

Public data only. Read-only. No login, no messaging, no account actions.

### Privacy

This dataset describes real people, so it is personal data under the GDPR and you become an
independent controller of any copy you keep.

- **What it is** — public profile information that FanCentro shows to logged-out visitors.
  Nothing behind a login or a paywall, and no media files are stored by us.
- **Retention** — every row carries `scrapedAt`. Please apply a retention limit rather than
  keeping profiles indefinitely, and re-run instead of relying on stale copies.
- **Removal requests** — a creator who wants to be excluded can write to the address on our
  Apify developer profile, or open an issue on this Actor. We maintain a denylist: once a
  handle is on it, this Actor stops returning that creator on any future run, for any buyer.
  Please honour such a request in copies you already hold.

***

# Actor input Schema

## `usernames` (type: `array`):

FanCentro creator handles to scrape (e.g. the part after fancentro.com/). Repeated handles are ignored — "@Luna" and "luna" are one creator, scraped and charged once.

## `discover` (type: `boolean`):

Also include creators listed on FanCentro's public discovery page. On by default so a run with no input still returns creators — leave it on to explore, turn it off when you only want the handles you submitted.

## `proxy` (type: `string`):

Optional proxy used for the requests, e.g. http://user:pass@host:port. Leave empty to use the default egress.

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

Hard cap on profiles returned by the WHOLE run — across every handle you submit, not per request — and therefore on what the run costs. Once it is reached the remaining handles are not scraped, and the run log says how many were skipped. Defaults to 10 so a first run is cheap and quick; raise it up to 500 once you know what you want.

## `discoverOffset` (type: `integer`):

Where in FanCentro's trending list creator discovery starts. Leave at 0 for the most trending creators. To walk the whole catalogue, run again with this set to the "nextOffset" the previous run reported — without it every run returns the same people. Measured 2026-08-05: the full catalogue is 8,185 creators, reached in 17 runs of 500. Two independent full walks returned the identical set, so following nextOffset does not skip or repeat anyone.

## Actor input object example

```json
{
  "usernames": [
    "example-creator"
  ],
  "discover": true,
  "maxResults": 10,
  "discoverOffset": 0
}
```

# 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 = {
    "usernames": [
        "example-creator"
    ],
    "discoverOffset": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("dirtycode/fancentro-profile-scraper").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 = {
    "usernames": ["example-creator"],
    "discoverOffset": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("dirtycode/fancentro-profile-scraper").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 '{
  "usernames": [
    "example-creator"
  ],
  "discoverOffset": 0
}' |
apify call dirtycode/fancentro-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dirtycode/fancentro-profile-scraper"
        }
    }
}

```

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/q0gqFUIE5Sg8mHjrd/builds/gm8MgK45KvRr1DHYe/openapi.json
