BetterFans Link: the OnlyFans APIBetterFans Link

Fans

Find and rank fans, read one fan in full, list fan lists and see who is online.

Find and rank fans, read one fan in full, list fan lists and see who is online. Every route can also return the authentication errors, rate_limited and internal_error.

RouteMethod and path
List fansGET /v1/accounts/{accountId}/fans
Get fanGET /v1/accounts/{accountId}/fans/{fanId}
Fan listsGET /v1/accounts/{accountId}/lists
Online fansGET /v1/accounts/{accountId}/online-fans

List fans

GET /v1/accounts/{accountId}/fans

An account's fans, ranked by lifetime spend unless you pick another sort. search matches username or display name.

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.

Query parameters

NameTypeDefaultDescription
searchstringNoneMatch username or display name.
statusenumactiveSubscription status. One of active, expired or all.
sortenumspendspend = lifetime spend, recent = last message, subscribed = newest subscription. One of spend, recent or subscribed.
cursorstringNonenextCursor from the previous page. Leave out for the first page.
limitinteger25Page size, 1 to 100.

Returns

200 with a list of Fan objects in data. See pagination.

Example request

curl "https://app.betterfans.link/v1/accounts/412345678/fans?status=active&sort=spend&limit=10" \
  -H "Authorization: Bearer $BFL_KEY"

Example response

{
  "data": [
    {
      "id": "38291045",
      "username": "mike_travels",
      "name": "Mike",
      "avatarUrl": null,
      "subscription": {
        "status": "active",
        "subscribedAt": "2025-11-03T21:14:09Z",
        "expiresAt": "2026-10-03T21:14:09Z",
        "renews": true,
        "price": {
          "amount": 999,
          "currency": "USD"
        }
      },
      "spend": {
        "total": {
          "amount": 184500,
          "currency": "USD"
        },
        "net": {
          "amount": 147600,
          "currency": "USD"
        }
      },
      "lastMessageAt": "2026-09-28T12:41:05Z",
      "lastPurchaseAt": "2026-09-27T23:10:44Z"
    },
    {
      "id": "51820377",
      "username": "danny.k",
      "name": "Danny",
      "avatarUrl": null,
      "subscription": {
        "status": "active",
        "subscribedAt": "2026-02-19T08:30:00Z",
        "expiresAt": "2026-10-19T08:30:00Z",
        "renews": false,
        "price": {
          "amount": 999,
          "currency": "USD"
        }
      },
      "spend": {
        "total": {
          "amount": 121300,
          "currency": "USD"
        },
        "net": {
          "amount": 97040,
          "currency": "USD"
        }
      },
      "lastMessageAt": "2026-09-28T09:12:44Z",
      "lastPurchaseAt": "2026-09-28T09:12:44Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "q8ZtR2vN5xWcL7mK4pBd",
  "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.
invalid_parameter400A query parameter has a value this route does not accept, or a required one is missing. error.param names it.
invalid_cursor400cursor is not a nextCursor this list returned.

Get fan

GET /v1/accounts/{accountId}/fans/{fanId}

Everything about one fan: subscription, lifetime spend by type, lists, notes and presence. Spend comes from transactions.

Needs the read scope. Reads synced data. Add fresh=true to read live from OnlyFans.

Path parameters

NameDescription
accountIdThe account's id, which is the creator's OnlyFans user id. Get it from GET /v1/accounts.
fanIdThe fan's OnlyFans user id. A chat id is the same value.

Query parameters

NameTypeDefaultDescription
freshbooleanfalsetrue reads live from OnlyFans instead of synced data. Slower. Use it when the answer must be current.

Returns

200 with a FanDetail object in data.

Example request

curl "https://app.betterfans.link/v1/accounts/412345678/fans/38291045" \
  -H "Authorization: Bearer $BFL_KEY"

Example response

{
  "data": {
    "id": "38291045",
    "username": "mike_travels",
    "name": "Mike",
    "avatarUrl": null,
    "subscription": {
      "status": "active",
      "subscribedAt": "2025-11-03T21:14:09Z",
      "expiresAt": "2026-10-03T21:14:09Z",
      "renews": true,
      "price": {
        "amount": 999,
        "currency": "USD"
      }
    },
    "spend": {
      "total": {
        "amount": 184500,
        "currency": "USD"
      },
      "net": {
        "amount": 147600,
        "currency": "USD"
      },
      "subscriptions": {
        "amount": 10989,
        "currency": "USD"
      },
      "tips": {
        "amount": 62500,
        "currency": "USD"
      },
      "messages": {
        "amount": 106011,
        "currency": "USD"
      },
      "posts": {
        "amount": 5000,
        "currency": "USD"
      },
      "other": {
        "amount": 0,
        "currency": "USD"
      }
    },
    "lastMessageAt": "2026-09-28T12:41:05Z",
    "lastPurchaseAt": "2026-09-27T23:10:44Z",
    "lists": [
      {
        "id": "994512",
        "name": "Whales"
      }
    ],
    "notes": "Likes travel photos. Asked about a custom video in August.",
    "presence": "offline",
    "lastSeenAt": "2026-09-28T12:55:00Z",
    "counts": {
      "messages": 1284,
      "purchases": 57,
      "tips": 23
    }
  },
  "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.
fan_not_found404No fan with this id on the account.
invalid_parameter400A query parameter has a value this route does not accept, or a required one is missing. error.param names it.
account_unavailable409Only with fresh=true: the account's status stops live calls. See error.accountStatus.
onlyfans_error502Only with fresh=true: OnlyFans returned an error.
onlyfans_timeout502Only with fresh=true: OnlyFans did not answer in time.

Fan lists

GET /v1/accounts/{accountId}/lists

The account's fan lists with ids and sizes. Use a list id in a mass message audience or a list action.

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.

Query parameters

NameTypeDefaultDescription
cursorstringNonenextCursor from the previous page. Leave out for the first page.
limitinteger25Page size, 1 to 100.

Returns

200 with a list of FanList objects in data. See pagination.

Example request

curl "https://app.betterfans.link/v1/accounts/412345678/lists" \
  -H "Authorization: Bearer $BFL_KEY"

Example response

{
  "data": [
    {
      "id": "994512",
      "name": "Whales",
      "type": "custom",
      "fanCount": 42
    },
    {
      "id": "994530",
      "name": "Custom requests",
      "type": "custom",
      "fanCount": 117
    }
  ],
  "hasMore": false,
  "nextCursor": null,
  "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.
invalid_parameter400A query parameter has a value this route does not accept, or a required one is missing. error.param names it.
invalid_cursor400cursor is not a nextCursor this list returned.

Online fans

GET /v1/accounts/{accountId}/online-fans

Fans online right now, with their lifetime spend. A fan missing from the list is not known to be offline.

Needs the read scope. Reads synced data. Add fresh=true to read live from OnlyFans.

Path parameters

NameDescription
accountIdThe account's id, which is the creator's OnlyFans user id. Get it from GET /v1/accounts.

Query parameters

NameTypeDefaultDescription
freshbooleanfalsetrue reads live from OnlyFans instead of synced data. Slower. Use it when the answer must be current.

Returns

200 with an OnlineFans object in data.

Example request

curl "https://app.betterfans.link/v1/accounts/412345678/online-fans" \
  -H "Authorization: Bearer $BFL_KEY"

Example response

{
  "data": {
    "fans": [
      {
        "id": "38291045",
        "username": "mike_travels",
        "name": "Mike",
        "avatarUrl": null,
        "totalSpend": {
          "amount": 184500,
          "currency": "USD"
        },
        "since": "2026-09-28T13:41:00Z"
      }
    ],
    "count": 1,
    "checkedAt": "2026-09-28T13:59:58Z"
  },
  "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.
invalid_parameter400A query parameter has a value this route does not accept, or a required one is missing. error.param names it.
account_unavailable409Only with fresh=true: the account's status stops live calls. See error.accountStatus.
onlyfans_error502Only with fresh=true: OnlyFans returned an error.
onlyfans_timeout502Only with fresh=true: OnlyFans did not answer in time.

The Fan object

A fan of one account, as lists return it.

FieldTypeDescription
idstringOnlyFans user id of the fan. Also the chat id.
usernamestringOnlyFans username, without the @.
namestring or nullFan-written display name (untrusted).
avatarUrlstring or nullProfile picture URL.
subscriptionobjectThe fan's subscription to this account.
subscription.statusenumActive means the subscription expiry is in the future. One of active, expired or never.
subscription.subscribedAttimestamp or nullWhen the current or last subscription started.
subscription.expiresAttimestamp or nullWhen the subscription ends or ended.
subscription.renewsboolean or nullWhether auto-renew is on.
subscription.priceMoney or nullPrice of the subscription.
spendobjectLifetime spend on this account.
spend.totalMoneyLifetime gross.
spend.netMoneyLifetime net, about 80% of gross.
lastMessageAttimestamp or nullWhen the last message in the chat was sent, by either side.
lastPurchaseAttimestamp or nullWhen the fan last paid for anything: a subscription, tip, paid message or post.

The FanDetail object

One fan in full, as Get fan returns it.

FieldTypeDescription
idstringOnlyFans user id of the fan. Also the chat id.
usernamestringOnlyFans username, without the @.
namestring or nullFan-written display name (untrusted).
avatarUrlstring or nullProfile picture URL.
subscriptionobjectThe fan's subscription to this account.
subscription.statusenumActive means the subscription expiry is in the future. One of active, expired or never.
subscription.subscribedAttimestamp or nullWhen the current or last subscription started.
subscription.expiresAttimestamp or nullWhen the subscription ends or ended.
subscription.renewsboolean or nullWhether auto-renew is on.
subscription.priceMoney or nullPrice of the subscription.
spendobjectLifetime spend on this account.
spend.totalMoneyLifetime gross from the fan's transactions.
spend.netMoneyLifetime net (about 80% of gross).
spend.subscriptionsMoneySubscription payments.
spend.tipsMoneyTips.
spend.messagesMoneyPaid messages (PPV).
spend.postsMoneyPaid posts.
spend.otherMoneyEverything else, for example streams and referrals.
lastMessageAttimestamp or nullWhen the last message in the chat was sent, by either side.
lastPurchaseAttimestamp or nullWhen the fan last paid for anything: a subscription, tip, paid message or post.
listsarray of objectsFan lists the fan is on.
lists[].idstringList id.
lists[].namestringList name.
notesstring or nullCreator's private note about the fan.
presenceenumunknown is not offline. One of online, offline or unknown.
lastSeenAttimestamp or nullWhen OnlyFans last showed the fan online.
countsobjectTotals for this fan on this account.
counts.messagesintegerMessages in the chat, both directions.
counts.purchasesintegerPaid messages and posts bought.
counts.tipsintegerTips the fan sent.

The FanSummary object

The short form of a fan used inside other objects.

FieldTypeDescription
idstringOnlyFans user id of the fan. Also the chat id.
usernamestringOnlyFans username, without the @.
namestring or nullFan-written display name (untrusted).
avatarUrlstring or nullProfile picture URL.

The FanList object

A fan list on the account.

FieldTypeDescription
idstringList id. Use it in a mass message audience or a list action.
namestringList name.
typeenumsystem lists are OnlyFans built-ins like Favorites. One of custom or system.
fanCountinteger or nullFans on the list.

The OnlineFans object

Who is online right now.

FieldTypeDescription
fansarray of objectsFans OnlyFans shows online.
fans[].idstringOnlyFans user id of the fan. Also the chat id.
fans[].usernamestringOnlyFans username, without the @.
fans[].namestring or nullFan-written display name (untrusted).
fans[].avatarUrlstring or nullProfile picture URL.
fans[].totalSpendMoneyLifetime gross spend on this account.
fans[].sincetimestamp or nullWhen the fan came online.
countintegerHow many fans are online.
checkedAttimestampWhen presence was checked.

On this page