Accounts and hosted links
OnlyFans and Fansly accounts API: list the creator accounts your key can use, check each one's status, and link new creators with a hosted link.
Product page: OnlyFans agency API
Start here. Most other routes take an accountId, and List accounts gives you each one with its platform and status. Every route can also return the authentication errors, rate_limited and internal_error.
| Route | Method and path | Platforms |
|---|---|---|
| List accounts | GET /v1/accounts | OnlyFans and Fansly |
| Create link | POST /v1/links | OnlyFans and Fansly |
| Get link | GET /v1/links/{linkId} | OnlyFans and Fansly |
| Get account | GET /v1/accounts/{accountId} | OnlyFans and Fansly |
List accounts
GET /v1/accounts
The OnlyFans and Fansly accounts your key can use, with their platform and status. A key limited to some accounts sees only those. platform=fansly or platform=onlyfans lists one platform. In test mode this lists the sandbox creators.
Needs the read scope.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
platform | enum | all | Only accounts on this platform. One of all, onlyfans or fansly. |
Returns
200 with a list of Account objects in data. See pagination.
Example request
curl "https://betterfans.link/v1/accounts" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "412345678",
"platform": "onlyfans",
"username": "jessrivers",
"name": "Jess Rivers",
"avatarUrl": null,
"status": "healthy",
"statusReason": null,
"writesEnabled": true,
"linkedAt": "2026-08-14T16:02:11Z",
"lastSyncedAt": "2026-09-28T13:58:40Z"
},
{
"id": "398776120",
"platform": "onlyfans",
"username": "mayablue",
"name": "Maya Blue",
"avatarUrl": null,
"status": "needs_relink",
"statusReason": "The OnlyFans session expired. Link the account again to resume syncing and live reads.",
"writesEnabled": false,
"linkedAt": "2026-07-02T10:15:00Z",
"lastSyncedAt": "2026-09-26T21:40:12Z"
},
{
"id": "583912047736512512",
"platform": "fansly",
"username": "raevale",
"name": "Rae Vale",
"avatarUrl": null,
"status": "healthy",
"statusReason": null,
"writesEnabled": true,
"linkedAt": "2026-09-21T11:30:42Z",
"lastSyncedAt": "2026-09-28T13:45:03Z"
}
],
"hasMore": false,
"nextCursor": null,
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
}
}Errors
| Code | Status | When |
|---|---|---|
invalid_parameter | 400 | A query parameter has a value this route does not accept, or a required one is missing. error.param names it. |
Create link
POST /v1/links
Creates a hosted link. Send its url to the creator: they open it, sign in to OnlyFans or Fansly and the account is linked to your workspace. The link works for 24 hours.
Needs the read scope.
Set platform to onlyfans or fansly to choose for the creator. Leave it out and the creator picks on the link page; the link's platform is null until they do.
Nothing is linked until the creator finishes. Check progress with Get link, or listen for the account.connected and link.failed webhook events.
Request body
| Field | Type | Description |
|---|---|---|
note | string | Optional. Shown in the dashboard next to the link, for example the creator's name. Up to 200 characters. |
platform | enum | Optional. onlyfans or fansly. Leave out to let the creator pick on the link page. |
Returns
201 with a HostedLink object in data.
Example request
curl -X POST "https://betterfans.link/v1/links" \
-H "Authorization: Bearer $BFL_KEY" \
-H "Content-Type: application/json" \
-d '{
"note": "Jess Rivers"
}'Example response
{
"data": {
"id": "link_9QwE4rTy6UiO2pAs8DfG1hJk",
"platform": null,
"status": "waiting",
"step": "Waiting for the creator",
"url": "https://betterfans.link/link/x7Hq2LmX9pRt4VbN8cKe3WzYaQ5sD1fG6jK0lZ2cV4b",
"accountId": null,
"note": "Jess Rivers",
"createdAt": "2026-09-28T14:00:00Z",
"expiresAt": "2026-09-29T14:00:00Z"
},
"meta": {
"source": "synced",
"asOf": null,
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
}
}Errors
| Code | Status | When |
|---|---|---|
invalid_parameter | 400 | platform is not onlyfans or fansly, or note is longer than 200 characters. |
Get link
GET /v1/links/{linkId}
The progress of a hosted link, with the current step in plain words. Once status is connected, accountId holds the new account.
Needs the read scope.
Path parameters
| Name | Description |
|---|---|
linkId | The link_ id from POST /v1/links. |
Returns
200 with a HostedLink object in data.
Example request
curl "https://betterfans.link/v1/links/link_9QwE4rTy6UiO2pAs8DfG1hJk" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": {
"id": "link_9QwE4rTy6UiO2pAs8DfG1hJk",
"platform": "onlyfans",
"status": "in_progress",
"step": "Waiting for 2FA code",
"url": "https://betterfans.link/link/x7Hq2LmX9pRt4VbN8cKe3WzYaQ5sD1fG6jK0lZ2cV4b",
"accountId": null,
"note": "Jess Rivers",
"createdAt": "2026-09-28T14:00:00Z",
"expiresAt": "2026-09-29T14:00:00Z"
},
"meta": {
"source": "synced",
"asOf": null,
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
}
}Errors
| Code | Status | When |
|---|---|---|
link_not_found | 404 | No link with this id in your workspace. |
Get account
GET /v1/accounts/{accountId}
One account with its counts, 30 day revenue and last sync. Read it before a long task, or after a call fails with account_unavailable.
Needs the read scope. Reads synced data. Works for OnlyFans and Fansly accounts.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's user id on its platform. Get it from GET /v1/accounts. |
Returns
200 with an AccountDetail object in data.
Example request
curl "https://betterfans.link/v1/accounts/412345678" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": {
"id": "412345678",
"platform": "onlyfans",
"username": "jessrivers",
"name": "Jess Rivers",
"avatarUrl": null,
"status": "healthy",
"statusReason": null,
"writesEnabled": true,
"linkedAt": "2026-08-14T16:02:11Z",
"lastSyncedAt": "2026-09-28T13:58:40Z",
"counts": {
"activeFans": 1842,
"expiredFans": 5310,
"chats": 6120,
"posts": 486,
"vaultItems": 2210
},
"revenue30d": {
"gross": {
"amount": 2184050,
"currency": "USD"
},
"net": {
"amount": 1747240,
"currency": "USD"
}
},
"subscriptionPrice": {
"amount": 999,
"currency": "USD"
}
},
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa",
"platform": "onlyfans"
}
}Errors
| Code | Status | When |
|---|---|---|
account_not_found | 404 | The account is not linked to your workspace, your key's account list leaves it out, or, with a test key, it is not a sandbox creator. |
The Account object
A creator account your workspace can use, on OnlyFans or Fansly. Its id is the creator's user id on that platform.
| Field | Type | Description |
|---|---|---|
id | string | The creator's user id on its platform. Every account route takes it as accountId. |
platform | enum | The platform the account lives on: onlyfans or fansly. |
username | string | Username on its platform, without the @. |
name | string or null | Display name on its platform. |
avatarUrl | string or null | Profile picture URL. |
status | enum | Plain account status. See account status. One of healthy, syncing, needs_relink, awaiting_2fa, awaiting_selfie, restricted or disconnected. |
statusReason | string or null | Plain sentence explaining a non-healthy status. |
writesEnabled | boolean | Whether actions can be created for this account. An owner or admin switches it in the dashboard. |
linkedAt | timestamp or null | When the account was linked to this workspace. |
lastSyncedAt | timestamp or null | When this account last synced. |
The AccountDetail object
An account with counts, 30 day revenue and the subscription price.
| Field | Type | Description |
|---|---|---|
id | string | The creator's user id on its platform. Every account route takes it as accountId. |
platform | enum | The platform the account lives on: onlyfans or fansly. |
username | string | Username on its platform, without the @. |
name | string or null | Display name on its platform. |
avatarUrl | string or null | Profile picture URL. |
status | enum | Plain account status. See account status. One of healthy, syncing, needs_relink, awaiting_2fa, awaiting_selfie, restricted or disconnected. |
statusReason | string or null | Plain sentence explaining a non-healthy status. |
writesEnabled | boolean | Whether actions can be created for this account. An owner or admin switches it in the dashboard. |
linkedAt | timestamp or null | When the account was linked to this workspace. |
lastSyncedAt | timestamp or null | When this account last synced. |
counts | object | Totals from synced data. |
counts.activeFans | integer | Fans with an active subscription. |
counts.expiredFans | integer | Fans whose subscription ended. |
counts.chats | integer | Chats on the account. |
counts.posts | integer | Posts on the account. |
counts.vaultItems | integer | Media items in the vault. |
revenue30d | object | Revenue over the last 30 days. |
revenue30d.gross | Money | What fans paid. |
revenue30d.net | Money | What the creator keeps after the platform fee. |
subscriptionPrice | Money or null | Current subscription price. |
The HostedLink object
A page a creator opens to connect their OnlyFans or Fansly account.
| Field | Type | Description |
|---|---|---|
id | string | link_ id. |
platform | enum or null | null until the creator picks a platform on the link page. One of onlyfans or fansly. |
status | enum | waiting until the creator starts, in_progress while they sign in, then connected, failed, expired or cancelled. |
step | string or null | Plain words for the current step, for example: Waiting for 2FA code. |
url | string | Send this to the creator. Valid for 24 hours. |
accountId | string or null | Set once connected. |
note | string or null | The note sent when the link was created. |
createdAt | timestamp | When the link was created. |
expiresAt | timestamp | When the link stops working, 24 hours after it was created. |