BetterFans Link: the OnlyFans APIBetterFans Link

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.

RouteMethod and path
List accountsGET /v1/accounts
Create linkPOST /v1/links
Get linkGET /v1/links/{linkId}
Get accountGET /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"
  }
}

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

FieldTypeDescription
notestringOptional. 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

CodeStatusWhen
invalid_parameter400note is longer than 200 characters.

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

NameDescription
linkIdThe 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

CodeStatusWhen
link_not_found404No 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

NameDescription
accountIdThe 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

CodeStatusWhen
account_not_found404The 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.

FieldTypeDescription
idstringOnlyFans user id of the creator.
usernamestringOnlyFans username, without the @.
namestring or nullDisplay name on OnlyFans.
avatarUrlstring or nullProfile picture URL.
statusenumPlain account status. See account status. One of healthy, syncing, needs_relink, awaiting_2fa, awaiting_selfie, restricted or disconnected.
statusReasonstring or nullPlain sentence explaining a non-healthy status.
writesEnabledbooleanWhether actions can be created for this account. An owner or admin switches it in the dashboard.
linkedAttimestamp or nullWhen the account was linked to this workspace.
lastSyncedAttimestamp or nullWhen this account last synced.

The AccountDetail object

An account with counts, 30 day revenue and the subscription price.

FieldTypeDescription
idstringOnlyFans user id of the creator.
usernamestringOnlyFans username, without the @.
namestring or nullDisplay name on OnlyFans.
avatarUrlstring or nullProfile picture URL.
statusenumPlain account status. See account status. One of healthy, syncing, needs_relink, awaiting_2fa, awaiting_selfie, restricted or disconnected.
statusReasonstring or nullPlain sentence explaining a non-healthy status.
writesEnabledbooleanWhether actions can be created for this account. An owner or admin switches it in the dashboard.
linkedAttimestamp or nullWhen the account was linked to this workspace.
lastSyncedAttimestamp or nullWhen this account last synced.
countsobjectTotals from synced data.
counts.activeFansintegerFans with an active subscription.
counts.expiredFansintegerFans whose subscription ended.
counts.chatsintegerChats on the account.
counts.postsintegerPosts on the account.
counts.vaultItemsintegerMedia items in the vault.
revenue30dobjectRevenue over the last 30 days.
revenue30d.grossMoneyWhat fans paid.
revenue30d.netMoneyWhat the creator keeps after the OnlyFans fee.
subscriptionPriceMoney or nullCurrent subscription price.

A page a creator opens to connect their OnlyFans account.

FieldTypeDescription
idstringlink_ id.
statusenumwaiting 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.
stepstring or nullPlain words for the current step, for example: Waiting for 2FA code.
urlstringSend this to the creator. Valid for 24 hours.
accountIdstring or nullSet once connected.
notestring or nullThe note sent when the link was created.
createdAttimestampWhen the link was created.
expiresAttimestampWhen the link stops working, 24 hours after it was created.

On this page