BetterFans Link: the OnlyFans APIBetterFans Link

API overview

Base URL, authentication, responses, pagination and every route.

Every route, grouped the way the sidebar groups them. This page covers what all routes share.

Base URL

https://app.betterfans.link/v1

Authentication

Send your key as a bearer token in the Authorization header. The x-api-key header works too. Keys that start with bfl_live_ read linked accounts; keys that start with bfl_test_ read the sandbox creators. See keys and scopes.

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

Responses

A single object comes back in data, with meta next to it:

{
  "data": {
    "id": "412345678",
    "username": "jessrivers"
  },
  "meta": {
    "source": "synced",
    "asOf": "2026-09-28T13:58:40Z",
    "requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
  }
}

A list adds hasMore and nextCursor:

{
  "data": [
    {
      "id": "38291045"
    },
    {
      "id": "51820377"
    }
  ],
  "hasMore": true,
  "nextCursor": "q8ZtR2vN5xWcL7mK4pBd",
  "meta": {
    "source": "synced",
    "asOf": "2026-09-28T13:58:40Z",
    "requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
  }
}

Meta

FieldTypeDescription
sourceenumsynced for synced data, live for a read straight from OnlyFans, sandbox in test mode. One of synced, live or sandbox.
asOftimestamp or nullWhen the data was last synced from OnlyFans; null for live reads.
requestIdstringThe request id, also sent in the x-request-id response header.
sideEffectsarray of enumsOptional. Present only when a live read changed something on OnlyFans. thread_marked_read means a fresh=true chat read could not restore the unread state. Each one of thread_marked_read.

Lists

FieldTypeDescription
dataarrayThe items on this page.
hasMorebooleanWhether another page follows.
nextCursorstring or nullPass it as cursor to get the next page. null on the last page.

Pagination

Routes that take cursor and limit return one page at a time. limit is 25 by default. A limit above 100 is treated as 100; a limit below 1, or one that is not a whole number, is invalid_parameter. Pass nextCursor back as cursor until hasMore is false. A cursor belongs to one list with one set of filters: keep the other parameters the same while you page.

async function allFans(accountId: string) {
  const fans = [];
  let cursor: string | null = null;
  do {
    const url = new URL(`https://app.betterfans.link/v1/accounts/${accountId}/fans`);
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.BFL_KEY}` } });
    const body = await res.json();
    if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
    fans.push(...body.data);
    cursor = body.hasMore ? body.nextCursor : null;
  } while (cursor);
  return fans;
}

Conventions

  • Ids are strings, even when they look like numbers. An account id is the creator's OnlyFans user id; a fan id is the fan's OnlyFans user id and also the chat id.
  • Timestamps are ISO 8601 strings in UTC. from and to take a date (2026-09-01) or a timestamp.
  • Money is an object with integer cents and a currency. See money.
  • Unknown query parameters are ignored. A known parameter with a bad value is invalid_parameter.
  • Every response has an x-request-id header. Errors repeat it as requestId.

Errors

Errors come back with an HTTP status and a JSON body with a stable error.code. Every route can also return the authentication errors, rate_limited and internal_error. Each route lists its own errors. The errors page explains every code.

Routes

Workspace

RouteMethod and pathScope
Who am IGET /v1/meread

Accounts

RouteMethod and pathScope
List accountsGET /v1/accountsread
Create linkPOST /v1/linksread
Get linkGET /v1/links/{linkId}read
Get accountGET /v1/accounts/{accountId}read

Fans

RouteMethod and pathScope
List fansGET /v1/accounts/{accountId}/fansread
Get fanGET /v1/accounts/{accountId}/fans/{fanId}read
Fan listsGET /v1/accounts/{accountId}/listsread
Online fansGET /v1/accounts/{accountId}/online-fansread

Chats

RouteMethod and pathScope
List chatsGET /v1/accounts/{accountId}/chatsread
List messagesGET /v1/accounts/{accountId}/chats/{fanId}/messagesread
Search messagesGET /v1/accounts/{accountId}/messages/searchread

Money

RouteMethod and pathScope
Revenue summaryGET /v1/accounts/{accountId}/revenueread
List transactionsGET /v1/accounts/{accountId}/transactionsread

Content

RouteMethod and pathScope
Mass messagesGET /v1/accounts/{accountId}/mass-messagesread
Top contentGET /v1/accounts/{accountId}/postsread
Tracking linksGET /v1/accounts/{accountId}/linksread
VaultGET /v1/accounts/{accountId}/vaultread

Call OnlyFans

RouteMethod and pathScope
Call OnlyFansGET /v1/accounts/{accountId}/onlyfans/{path}read

Actions

RouteMethod and pathScope
Create actionPOST /v1/accounts/{accountId}/actionswrite
Get actionGET /v1/actions/{actionId}read
List actionsGET /v1/actionsread

On this page