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.
synced for synced data, live for a read straight from the platform, sandbox in test mode.
asOf
timestamp or null
When the data was last synced from the platform; null for live reads.
requestId
string
The request id, also sent in the x-request-id response header.
platform
enum
Optional. The account's platform, on every route that takes an accountId. One of onlyfans or fansly.
sideEffects
array of enums
Optional. Present only when a live read changed something on the platform. thread_marked_read means a fresh=true chat read on OnlyFans could not restore the unread state. Each one of thread_marked_read.
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://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;}
Every account lives on one platform, OnlyFans or Fansly. The same routes serve both. Each route lists the platforms it works for; an account on any other platform gets not_supported_on_platform. Routes that take an accountId say the account's platform in meta.platform.
Ids are strings, even when they look like numbers. An account id is the creator's user id on its platform; a fan id is the fan's user id on that platform and also the chat id. Fansly ids are too large for a JavaScript number to hold exactly, so never parse an id into a number.
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 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.