BetterFans Link: the OnlyFans APIBetterFans Link

Moving from the SDK

How each part of the BetterFans Link SDK and the previous API maps to v1, what works differently, and what v1 does not have.

v1 replaces the BetterFans Link SDK (@betterfans/link-sdk) and the previous API. It is a REST API and an MCP server. There is no client library to install: any HTTP client works, and every route returns the same data and meta envelope. This guide maps each thing you used before to its v1 equivalent, and says plainly what v1 does not have.

What changed

  • Writes need approval. The previous API sent writes to OnlyFans as soon as you called it. In v1, a write is an action that an owner or admin approves or rejects in the dashboard. API writes are also off for each account until an owner or admin turns them on.
  • Reads come from synced data. Most routes read data synced from OnlyFans and say how fresh it is in meta.asOf. A few take fresh=true to read live. See synced and live reads.
  • Shapes are stable. v1 routes return documented objects, such as Fan and Transaction, instead of raw OnlyFans responses. Money is always an object in integer cents. Account and fan ids are the OnlyFans user ids.
  • Errors have one shape. Every error is { "error": { "type", "code", "message", "hint", "docsUrl" }, "requestId" } with a lowercase code, instead of an [error, data] pair with uppercase codes. See errors.
  • There is a test mode. A bfl_test_ key reads sandbox creators and never reaches OnlyFans. See test mode.

Keys

Keys from the SDK and the previous API do not work on v1. Create a new key under Developers, then API keys, in the dashboard. Live keys start with bfl_live_ and test keys with bfl_test_. Send it as Authorization: Bearer <key>. See keys and scopes.

How each part maps

BeforeIn v1
client.request() for OnlyFans readsThe v1 routes for fans, chats, money and content. For anything they do not cover, Call OnlyFans runs a read-only OnlyFans GET with the account's session. It needs a live key.
client.request() for OnlyFans writesFive action types: send_message, send_mass_message, unsend_message, add_fan_to_list and remove_fan_from_list, each approved by a person. Other writes are not in v1.
Scoped clients (client.for(accountId))Put the account id in the path: /v1/accounts/{accountId}/.... To limit a key to some accounts, give it an account allow-list.
The realtime plugin and websocket eventsWebhooks. message.received, transaction.created, subscriber.new and account.status_changed tell you when something happens. There is no websocket.
Revenue pullList transactions pages through sales, up to 100 at a time, and Revenue summary gives totals for a period. Refunds and chargebacks are not in v1.
Revenue pushThe transaction.created webhook, signed with the endpoint's secret. See verify signatures.
PresenceOnline fans, with each fan's lifetime spend. Add fresh=true for a live read.
Vault filtersVault filters by folder (a vault list id) and type. For other filters, use Call OnlyFans.
Signed media linksMessage, post and vault media carry a thumbnailUrl. A vault item carries a url to the file once it has been archived. That url never expires, so treat it like a secret link. See vault file URLs.
Media uploadsNot in v1. Put media in the creator's vault on OnlyFans, then attach it to a message by its id in mediaIds.
Batch requestsNot in v1. Send separate requests within your key's rate limit.
SDK keys made in the dashboardbfl_live_ and bfl_test_ keys made under Developers, then API keys.

A read, before and after

Before, with the SDK:

before.ts
const jess = client.for("412345678");
const [error, me] = await jess.request("GET /users/me", {});

In v1, the same OnlyFans read goes through Call OnlyFans:

after.ts
const res = await fetch("https://app.betterfans.link/v1/accounts/412345678/onlyfans/users/me", {
  headers: { Authorization: `Bearer ${process.env.BFL_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.requestId})`);
console.log(body.data);

Prefer a v1 route when one covers what you need. Get account returns the account with its counts, 30 day revenue and last sync, and it works with a test key.

What is not in v1

These parts of the SDK and the previous API have no v1 equivalent.

  • Websocket and realtime events. Use webhooks.
  • Batch requests.
  • Media uploads.
  • Writes to OnlyFans other than the five action types, and any write that skips approval.
  • Write requests through Call OnlyFans. It runs GET only.
  • Refunds and chargebacks in transactions.

If your integration depends on one of these, email hello@betterfans.link and say what you use it for.

Moving over

  1. Create a bfl_test_ key and build against the sandbox creators first. See the API quickstart.
  2. Replace each SDK call with the v1 route from the table above. Branch on error.code, and quote requestId when you report a problem.
  3. Replace realtime listeners with a webhook endpoint, and verify every signature.
  4. Change each write into an action, and decide who on your team approves them. See writes and approvals.
  5. Link your creators to the workspace if they are not linked yet. See link a creator.
  6. Create a bfl_live_ key, turn on API writes for the accounts that need them, and go through going live.

On this page