Posts, vault, tracking links and mass message stats
OnlyFans vault, posts and tracking links API: reach and sales of mass messages, top posts, subscribers and revenue per link, and vault media.
Product page: OnlyFans content API
Use them to compare what a creator sends and posts, and to find media ids to attach to a message. Every route can also return the authentication errors, rate_limited and internal_error.
| Route | Method and path | Platforms |
|---|---|---|
| Mass messages | GET /v1/accounts/{accountId}/mass-messages | OnlyFans |
| Top content | GET /v1/accounts/{accountId}/posts | OnlyFans and Fansly |
| Tracking links | GET /v1/accounts/{accountId}/links | OnlyFans and Fansly |
| Vault | GET /v1/accounts/{accountId}/vault | OnlyFans and Fansly |
Mass messages
GET /v1/accounts/{accountId}/mass-messages
Mass messages with how many fans got, opened and bought each one, and what each earned.
Needs the read scope. Reads synced data. Works for OnlyFans accounts only. Other accounts get not_supported_on_platform.
A mass message's numbers are totals for the whole send. Never add them to per-fan message numbers: each fan's copy of a mass message is part of the same total.
revenue comes from OnlyFans' own mass message stats, not from transactions, so net is 80% of gross.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's user id on its platform. Get it from GET /v1/accounts. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
from | date or timestamp | 30 days ago | Start, ISO 8601 date or timestamp (UTC). |
to | date or timestamp | now | End, ISO 8601 date or timestamp (UTC). |
sort | enum | recent | recent = newest first. One of recent or revenue. |
cursor | string | None | nextCursor from the previous page. Leave out for the first page. |
limit | integer | 25 | Page size, 1 to 100. |
Returns
200 with a list of MassMessage objects in data. See pagination.
Example request
curl "https://betterfans.link/v1/accounts/412345678/mass-messages?limit=10" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "118823004",
"sentAt": "2026-09-26T18:00:00Z",
"text": "Weekend special: the full beach video is in your inbox",
"price": {
"amount": 1200,
"currency": "USD"
},
"media": [
{
"id": "3399120045",
"type": "video",
"thumbnailUrl": null,
"durationSeconds": 94
}
],
"sentCount": 1742,
"viewedCount": 903,
"purchasedCount": 128,
"revenue": {
"gross": {
"amount": 153600,
"currency": "USD"
},
"net": {
"amount": 122880,
"currency": "USD"
}
},
"state": "sent"
}
],
"hasMore": false,
"nextCursor": null,
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa",
"platform": "onlyfans"
}
}Errors
| Code | Status | When |
|---|---|---|
account_not_found | 404 | The 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. |
not_supported_on_platform | 400 | The account is not on OnlyFans. |
invalid_parameter | 400 | A query parameter has a value this route does not accept, or a required one is missing. error.param names it. |
invalid_cursor | 400 | cursor is not a nextCursor this list returned. |
Top content
GET /v1/accounts/{accountId}/posts
Posts with price, likes, comments and tips, sorted by recent or likes. revenue is null when the platform does not say which post a purchase was for. tips is null on OnlyFans accounts, which do not say which post a tip was for.
Needs the read scope. Reads synced data. Works for OnlyFans and Fansly accounts.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's user id on its platform. Get it from GET /v1/accounts. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
from | date or timestamp | 30 days ago | Start, ISO 8601 date or timestamp (UTC). |
to | date or timestamp | now | End, ISO 8601 date or timestamp (UTC). |
sort | enum | recent | recent = newest first, likes = most liked first. One of recent or likes. |
cursor | string | None | nextCursor from the previous page. Leave out for the first page. |
limit | integer | 25 | Page size, 1 to 100. |
Returns
200 with a list of Post objects in data. See pagination.
Example request
curl "https://betterfans.link/v1/accounts/412345678/posts?sort=likes&limit=10" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "1788201934",
"postedAt": "2026-09-20T17:30:00Z",
"text": "Golden hour at the beach",
"price": null,
"media": [
{
"id": "3399120101",
"type": "photo",
"thumbnailUrl": null,
"durationSeconds": null
}
],
"likes": 412,
"comments": 38,
"tips": null,
"revenue": null,
"url": "https://onlyfans.com/1788201934/jessrivers"
}
],
"hasMore": false,
"nextCursor": null,
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa",
"platform": "onlyfans"
}
}Errors
| Code | Status | When |
|---|---|---|
account_not_found | 404 | The 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_parameter | 400 | A query parameter has a value this route does not accept, or a required one is missing. error.param names it. |
invalid_cursor | 400 | cursor is not a nextCursor this list returned. |
Tracking links
GET /v1/accounts/{accountId}/links
Tracking and free trial links with clicks, subscribers and revenue. url is null on Fansly accounts, which do not state the address fans open.
Needs the read scope. Reads synced data. Works for OnlyFans and Fansly accounts.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's user id on its platform. Get it from GET /v1/accounts. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
sort | enum | revenue | revenue = most earned first. One of revenue, subscribers or recent. |
cursor | string | None | nextCursor from the previous page. Leave out for the first page. |
limit | integer | 25 | Page size, 1 to 100. |
Returns
200 with a list of TrackingLink objects in data. See pagination.
Example request
curl "https://betterfans.link/v1/accounts/412345678/links?sort=subscribers" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "2210044",
"kind": "tracking",
"name": "Instagram bio",
"url": "https://onlyfans.com/jessrivers/c12",
"createdAt": "2026-03-02T15:00:00Z",
"clicks": 18244,
"subscribers": 1310,
"revenue": {
"gross": {
"amount": 486000,
"currency": "USD"
},
"net": {
"amount": 388800,
"currency": "USD"
}
}
}
],
"hasMore": false,
"nextCursor": null,
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa",
"platform": "onlyfans"
}
}Errors
| Code | Status | When |
|---|---|---|
account_not_found | 404 | The 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_parameter | 400 | A query parameter has a value this route does not accept, or a required one is missing. error.param names it. |
invalid_cursor | 400 | cursor is not a nextCursor this list returned. |
Vault
GET /v1/accounts/{accountId}/vault
Media in the account's vault, with the vault lists each item is in. Put a media id in mediaIds to attach it to a message.
Needs the read scope. Reads synced data. Works for OnlyFans and Fansly accounts.
timesSent and revenue are null for every item.
A url, when set, is a public link to the file that never expires. Anyone with the URL can fetch the file, so treat it like a secret link: never publish it or put it anywhere a fan or a stranger could see it.
Path parameters
| Name | Description |
|---|---|
accountId | The account's id, which is the creator's user id on its platform. Get it from GET /v1/accounts. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
folder | string | None | Vault list id. Leave out for all media. |
type | enum | all | Media type. One of all, photo, video, audio or gif. |
cursor | string | None | nextCursor from the previous page. Leave out for the first page. |
limit | integer | 25 | Page size, 1 to 100. |
Returns
200 with a list of VaultItem objects in data. See pagination.
Example request
curl "https://betterfans.link/v1/accounts/412345678/vault?type=video&limit=10" \
-H "Authorization: Bearer $BFL_KEY"Example response
{
"data": [
{
"id": "3399120045",
"type": "video",
"createdAt": "2026-09-19T16:20:00Z",
"folders": [
{
"id": "771203",
"name": "Beach shoot"
}
],
"thumbnailUrl": null,
"url": null,
"durationSeconds": 94,
"timesSent": null,
"revenue": null
}
],
"hasMore": true,
"nextCursor": "q8ZtR2vN5xWcL7mK4pBd",
"meta": {
"source": "synced",
"asOf": "2026-09-28T13:58:40Z",
"requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa",
"platform": "onlyfans"
}
}Errors
| Code | Status | When |
|---|---|---|
account_not_found | 404 | The 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_parameter | 400 | A query parameter has a value this route does not accept, or a required one is missing. error.param names it. |
invalid_cursor | 400 | cursor is not a nextCursor this list returned. |
The MassMessage object
One mass message and its totals.
| Field | Type | Description |
|---|---|---|
id | string | Mass message id. |
sentAt | timestamp | When it was sent. |
text | string | Message text. |
price | Money or null | Set when it is a paid message (PPV). |
media | array of Media | Media attached. |
sentCount | integer | Fans it was sent to. |
viewedCount | integer or null | Fans who opened it. |
purchasedCount | integer or null | Fans who bought it. |
revenue | object | What it earned, as totals for the whole mass message. |
revenue.gross | Money | Gross. |
revenue.net | Money | Net. |
state | enum | unsent means the creator took it back. One of sent or unsent. |
The Post object
One post.
| Field | Type | Description |
|---|---|---|
id | string | Post id. |
postedAt | timestamp | When it was posted. |
text | string | Post text. |
price | Money or null | Set for a paid post. |
media | array of Media | Media in the post. |
likes | integer | Likes. |
comments | integer | Comments. |
tips | Money or null | Tips on the post. null when the platform does not say which post a tip was for (OnlyFans). |
revenue | object or null | null when the platform does not say which post a purchase was for. |
revenue.gross | Money | Gross. |
revenue.net | Money | Net. |
url | string or null | Link to the post on its platform. |
The TrackingLink object
A tracking or free trial link.
| Field | Type | Description |
|---|---|---|
id | string | Link id. |
kind | enum | tracking links count clicks and subscribers, trial links give a free trial. |
name | string | Name the creator gave the link. |
url | string or null | The address fans open. null when the platform does not state it (Fansly links). |
createdAt | timestamp or null | When the link was made. |
clicks | integer or null | Clicks on the link. |
subscribers | integer | Fans who subscribed through the link. |
revenue | object | Revenue from fans who subscribed through the link. |
revenue.gross | Money | Gross. |
revenue.net | Money | Net. |
The VaultItem object
One media item in the vault.
| Field | Type | Description |
|---|---|---|
id | string | Media id. Use it in mediaIds. |
type | enum | Media type. One of photo, video, audio, gif or other. |
createdAt | timestamp or null | When it was added to the vault. |
folders | array of objects | Vault lists the item is in. |
folders[].id | string | Vault list id. Use it as the folder parameter. |
folders[].name | string | Vault list name. |
thumbnailUrl | string or null | Preview image URL. |
url | string or null | Archived copy; null until archived. |
durationSeconds | number or null | Length of a video or audio clip. |
timesSent | integer or null | How many times it was sent. |
revenue | Money or null | What it earned. |
Money
OnlyFans transactions and earnings API: every sale with its fan, type, gross, net and fee, and revenue summaries by day, week or month.
Call OnlyFans
Read an OnlyFans API endpoint that no other route covers, for a linked OnlyFans account and with a live key. Reads only. Writes go through actions.