Accounts
List the creator accounts your key can use, check their health and link new ones.
List the creator accounts your key can use, check their health and link new ones with a hosted link. Every route can also return the authentication errors, rate_limited and internal_error.
| Route | Method and path |
|---|---|
| List accounts | GET /v1/accounts |
| Create link | POST /v1/links |
| Get link | GET /v1/links/{linkId} |
| Get account | GET /v1/accounts/{accountId} |
List accounts
GET /v1/accounts
The OnlyFans accounts your key can use, with their status. A key limited to some accounts sees only those. In test mode this lists the sandbox creators.
Needs the read scope.
Returns
200 with a list of Account objects in data. See pagination.
Example request
curl "https://app.betterfans.link/v1/accounts" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "412345678",
"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",
"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"
}
],
"hasMore": false,
"nextCursor": null,
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
}
}Create link
POST /v1/links
Creates a hosted link. Send its url to the creator: they open it, sign in to OnlyFans and the account is linked to your workspace. The link works for 24 hours.
Needs the read scope.
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. |
Returns
201 with a HostedLink object in data.
Example request
curl -X POST "https://app.betterfans.link/v1/links" \
-H "Authorization: Bearer $BFL_KEY" \
-H "Content-Type: application/json" \
-d '{
"note": "Jess Rivers"
}'Example response
{
"data": {
"id": "link_9QwE4rTy6UiO2pAs8DfG1hJk",
"status": "waiting",
"step": "Waiting for the creator",
"url": "https://app.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 | 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://app.betterfans.link/v1/links/link_9QwE4rTy6UiO2pAs8DfG1hJk" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": {
"id": "link_9QwE4rTy6UiO2pAs8DfG1hJk",
"status": "in_progress",
"step": "Waiting for 2FA code",
"url": "https://app.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.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's OnlyFans user id. Get it from GET /v1/accounts. |
Returns
200 with an AccountDetail object in data.
Example request
curl "https://app.betterfans.link/v1/accounts/412345678" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": {
"id": "412345678",
"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"
}
}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. Its id is the creator's OnlyFans user id.
| Field | Type | Description |
|---|---|---|
id | string | OnlyFans user id of the creator. |
username | string | OnlyFans username, without the @. |
name | string or null | Display name on OnlyFans. |
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 | OnlyFans user id of the creator. |
username | string | OnlyFans username, without the @. |
name | string or null | Display name on OnlyFans. |
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 OnlyFans fee. |
subscriptionPrice | Money or null | Current subscription price. |
The HostedLink object
A page a creator opens to connect their OnlyFans account.
| Field | Type | Description |
|---|---|---|
id | string | link_ id. |
status | enum | waiting until the creator starts, in_progress while they sign in, then connected, failed, expired or cancelled. One of waiting, in_progress, 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. |