The two surfaces
A thin signed passthrough and a normalized CRM layer, with different response shapes.
The API has two surfaces with genuinely different contracts. Picking the wrong one is the most common source of confusion.
1. Transparent passthrough — /api2/v2/*
OnlyFans only. A thin, signed proxy. OnlyFans' own response body is passed through unchanged, but always wrapped in a small envelope:
{
"success": true,
"status_code": 200,
"data": { "…OnlyFans' actual payload…" }
}Check success first, then read the OnlyFans payload — including list and
hasMore on paginated endpoints — from data. On error, success is false,
status_code carries the upstream HTTP status, and data holds an error
object with code and message. A relogin: true field appears when the
session was refreshed mid-request.
Each request needs:
| Header | Required | Purpose |
|---|---|---|
X-API-Key | yes | Your panel key |
user-id | yes | Which connected account to act as. Omitting it returns 400 user-id header is required |
X-Proxy | no | Overrides the proxy saved at login time |
curl "$BASE/api/crm/$CRM/api2/v2/users/me" \
-H "X-API-Key: $KEY" \
-H "user-id: 482687148"What we do for you
- Load the saved session for that account (cookies,
x-bc,x-hash) - Route the request through the account's proxy, so OnlyFans sees the same IP that logged in
- Generate fresh signed headers server-side using the current OnlyFans signing algorithm
- Forward to
onlyfans.com/api2/v2/… - Return the JSON response
Two different failures are worth telling apart:
| Code | Meaning |
|---|---|
403 Account not found or does not belong to this CRM panel | The of_user_id is not connected to your panel |
401 No session found. Provide sess and auth_id cookies (-b) on first request, or login via /accounts/login/cookies | It is your account, but there is no stored session |
The passthrough does have a cookie bootstrap for that second case — send sess and
auth_id as cookies on the first request and a session is created. Prefer
POST /accounts/login/cookies, which stores it once and properly.
2. Normalized CRM layer
OnlyFans + Fansly. Here responses are normalized: both platforms' rows are projected to the same shape, and several routes are server-side aggregations with no platform equivalent.
- Cached reads that cost zero platform requests —
/subscribers/cached,/subscribers/new,/subscribers/stats,/transactions/cached,/fans/{fan_id}/transactions/cached,/campaigns/{campaign_id}/claimers/cached - Async refresh and backfill jobs —
POST /subscribers/refresh,/transactions/refresh,/campaigns/refresh,/backfill. Each returns202; poll the matching/refresh/statusroute or listen on/events/stream - Cross-account aggregation —
GET /earnings/summary,GET /balances/summary - Fan CRM —
GET /fanswith tags, notes and per-fan cached transactions - Messaging —
/chats,/chats/{with_user_id}/messages,POST /messages/mass,/ppv-stats - Data exports —
/exports, producing a downloadable ZIP - Webhooks —
/webhookswith a delivery log and test fire - Automations — event-triggered actions,
/run-now, run history - Realtime —
GET /events(poll) andGET /events/stream(SSE)
The response envelope is different: data is a sibling key, not nested under
data.
{
"success": true,
"list": [ … ],
"total": 412,
"limit": 100,
"offset": 0,
"hasMore": true
}See Responses & errors for the full picture, including the routes that return a bare object with no envelope at all.
Which should I use?
Use the CRM layer
Default choice. Works on both platforms, cheaper (cached reads cost no platform requests), stable shapes, and it is where events, webhooks, automations and exports live.
Use the passthrough
When you need an OnlyFans capability the CRM layer does not expose — vault media, stories, streams, promotions, granular post management.
Non-GET passthrough requests need write actions enabled
GET is always allowed. Any other method through /api2/v2/* is a write performed as
the creator, so it requires allow_of_write_actions on that account and otherwise
returns 403 with code: "WRITES_DISABLED" — the same gate as the CRM write routes.
See Connect an account.
Pace the passthrough yourself
Passthrough calls hit OnlyFans directly and count against their rate limits,
which we do not enforce for you. Roughly 1 request/second in bursts is fine;
sustained traffic above ~5 req/s risks getting the account flagged. Prefer the
/cached routes. See Rate limits & quotas.