BetterFans Link: the OnlyFans APIBetterFans Link

OnlyFans API error codes

OnlyFans API error codes from BetterFans Link: what each code and HTTP status means, which errors to retry and how to fix the rest.

Product page: OnlyFans and Fansly API coverage

Errors come back with an HTTP status and a JSON body. Branch on error.code: it is stable. error.message is for people and can be more specific than the default shown here, so never match on it.

The error body

FieldTypeDescription
errorobjectThe error.
error.typeenumError category. It decides the HTTP status, except for method_not_allowed, which is 405. One of invalid_request, authentication, billing, permission, not_found, conflict, account_unavailable, rate_limited, upstream or internal.
error.codeenumStable code to branch on. One of the 58 codes under all codes.
error.messagestringA sentence for people. It can be more specific than the default, so never match on it.
error.hintstringOptional. What to do next.
error.paramstringOptional. The parameter that failed validation.
error.permissionstringOptional. The permission that was missing.
error.accountStatusstringOptional. The account's status, sent with account_unavailable.
error.limitobjectOptional. Which plan limit refused the request, sent with quota_exceeded, plan_limit_reached and rate_limited when the plan's rate refused it. See plans and limits.
error.limit.nameenumThe limit: accounts, liveCalls, requestsPerMinute, seats or webhookEndpoints.
error.limit.allowedintegerHow much the plan allows.
error.limit.usedintegerHow much the workspace has used.
error.limit.resetsAtstringOptional. When the limit resets, for live calls and the rate.
error.docsUrlstringLink to this code on the errors page.
requestIdstringThe request id. Send it to support when you ask about a request.
{
  "error": {
    "type": "account_unavailable",
    "code": "account_unavailable",
    "message": "The platform is not accepting calls for this account right now.",
    "hint": "See error.accountStatus. Reads without fresh=true still work.",
    "accountStatus": "needs_relink",
    "docsUrl": "https://betterfans.link/docs/errors#account-unavailable"
  },
  "requestId": "req_7Hq2LmX9pRt4VbN8cKe3WzYa"
}

Retrying

ErrorWhat to do
429 rate_limitedWait the number of seconds in Retry-After, then retry.
502 onlyfans_error and onlyfans_timeoutRetry after a short wait, or read synced data without fresh=true.
502 fansly_error and fansly_timeoutRetry after a short wait, or read synced data without fresh=true.
500 or 503 internal_errorRetry with a growing delay.
409 account_unavailableDo not retry until the account status changes.
402 quota_exceededLive calls resume at error.limit.resetsAt. Read synced data without fresh=true until then.
403 workspace_suspendedDo not retry until support lifts the suspension.
Any other 4xxFix the request first. Sending it again unchanged fails the same way.
Any error on an actionRetry with the same Idempotency-Key, so the action is created once.

All codes

Codes marked dashboard come from the dashboard and the hosted link page, never from the REST API or MCP.

CodeStatusType
invalid_parameter400invalid_request
invalid_cursor400invalid_request
idempotency_key_required400invalid_request
method_not_allowed405invalid_request
not_supported_on_platform400invalid_request
missing_api_key401authentication
invalid_api_key401authentication
revoked_api_key401authentication
expired_api_key401authentication
not_signed_in (dashboard)401authentication
invalid_access_token (dashboard)401authentication
missing_scope403permission
writes_disabled403permission
missing_permission403permission
dashboard_key_only403permission
session_only (dashboard)403permission
reauthentication_required (dashboard)403permission
email_not_verified (dashboard)403permission
workspace_suspended403permission
staff_only (dashboard)403permission
staff_protected (dashboard)403permission
impersonation_read_only (dashboard)403permission
quota_exceeded402billing
plan_limit_reached402billing
account_not_found404not_found
fan_not_found404not_found
chat_not_found404not_found
action_not_found404not_found
link_not_found404not_found
route_not_found404not_found
workspace_not_found (dashboard)404not_found
resource_not_found404not_found
user_not_found (dashboard)404not_found
idempotency_conflict409conflict
action_not_pending409conflict
action_expired409conflict
link_not_ready (dashboard)409conflict
last_owner (dashboard)409conflict
owns_workspaces (dashboard)409conflict
two_factor_enabled (dashboard)409conflict
two_factor_not_enabled (dashboard)409conflict
subscription_exists (dashboard)409conflict
no_subscription (dashboard)409conflict
subscription_active (dashboard)409conflict
checkout_unavailable (dashboard)409conflict
plan_not_available (dashboard)409conflict
last_admin (dashboard)409conflict
user_banned (dashboard)409conflict
email_unverified (dashboard)409conflict
account_in_use (dashboard)409conflict
account_unavailable409account_unavailable
rate_limited429rate_limited
onlyfans_error502upstream
onlyfans_timeout502upstream
fansly_error502upstream
fansly_timeout502upstream
billing_provider_error (dashboard)502upstream
internal_error500internal

Invalid request

invalid_parameter

StatusTypeMessageHint
400invalid_requestA parameter is missing or not valid.Check error.param and the endpoint reference.

A query parameter has a value the route does not accept, a required one is missing, or the body does not match. error.param names the parameter. Fix the value and send the request again. Unknown query parameters are ignored, so a misspelled name has no effect instead of failing.

invalid_cursor

StatusTypeMessageHint
400invalid_requestThe cursor is not valid for this list.Pass nextCursor exactly as the previous page returned it.

A cursor belongs to one list with one set of filters. Pass nextCursor back unchanged with the same parameters, or leave cursor out to start again from the first page.

idempotency_key_required

StatusTypeMessageHint
400invalid_requestWrites need an Idempotency-Key header.Send a unique value per intended action, for example a UUID.

Every POST /v1/accounts/{accountId}/actions needs an Idempotency-Key header. Generate a UUID for each action you intend and send the same value when you retry that action.

method_not_allowed

StatusTypeMessageHint
405invalid_requestThis endpoint only accepts GET.Use POST /v1/accounts/{accountId}/actions to change anything on the account.

Call OnlyFans and Call Fansly only read. To send, take back or change anything, create an action instead. It waits for a person to approve it, unless the key has auto-approve on.

not_supported_on_platform

StatusTypeMessageHint
400invalid_requestThis account's platform does not support this.Check the platforms each endpoint lists in the API reference. GET /v1/accounts shows each account's platform.

The account's platform does not support this route, action type or tool. Each route in the API reference lists its platforms, and GET /v1/accounts shows each account's platform. For an action, error.param is type. Sending the same request again fails the same way.

Authentication

missing_api_key

StatusTypeMessageHint
401authenticationNo API key was sent.Send Authorization: Bearer <your key>.

Send the key as Authorization: Bearer <key> or in the x-api-key header.

invalid_api_key

StatusTypeMessageHint
401authenticationThis API key is not valid.Check for typos or create a new key in the dashboard.

No key matches. Check that the whole key was copied and that nothing was added around it. Keys are shown once, so a lost key has to be replaced with a new one.

revoked_api_key

StatusTypeMessageHint
401authenticationThis API key was revoked.Create a new key in the dashboard.

Someone revoked this key in the dashboard, or it was rolled and the old key stopped working. Create a new key.

expired_api_key

StatusTypeMessageHint
401authenticationThis API key has expired.Create a new key in the dashboard.

The key passed the expiry date set when it was created. Create a new key.

not_signed_in

StatusTypeMessageHint
401authenticationYou are not signed in.Sign in again.

Dashboard only. The dashboard session ended. Sign in again.

invalid_access_token

StatusTypeMessageHint
401authenticationThis access token is not valid, was revoked or has expired.Create a new token under Account, Access tokens.

Dashboard only. The account access token or OAuth token is not one we issued, someone revoked it, it passed its expiry date, or the person it belongs to is banned. Create a new token on the Account page and replace the old one wherever it is stored. A banned person gets this answer until the ban is lifted. API keys work on /v1, not on the dashboard procedures.

Billing

quota_exceeded

StatusTypeMessageHint
402billingThis workspace has used its live calls for this period.Synced reads and test mode keep working. Live calls resume when the period resets (error.limit.resetsAt), or upgrade or allow overage in Billing.

The workspace used every live call its plan includes for this period. error.limit says how many were allowed, how many were used and when the period resets in resetsAt. Synced reads and test mode keep working. Live calls resume at the reset, or at once when an owner upgrades the plan or allows overage on the Billing page. See plans and limits.

plan_limit_reached

StatusTypeMessageHint
402billingThe workspace's plan does not allow more of this.Upgrade in Billing, or remove one first. error.limit names the limit.

The plan does not allow one more of something: an account, a teammate or a webhook endpoint. error.limit.name names the limit, with allowed and used. Nothing you already have is removed. Remove one first, or ask an owner to choose a larger plan on the Billing page. See plans and limits.

Permission

missing_scope

StatusTypeMessageHint
403permissionThis key does not have the scope this call needs.Create a key with the write scope.

The key has only the read scope and the call needs write. Create a key with the write scope for code that asks for writes.

writes_disabled

StatusTypeMessageHint
403permissionWrites are turned off for this account.An admin can turn writes on in the account's settings.

Writes are switched off for this account. An owner or admin can switch them on in the account's settings in the dashboard. Reads keep working.

missing_permission

StatusTypeMessageHint
403permissionYour role does not allow this.Ask a workspace admin.

In the dashboard: your workspace role does not allow this. error.permission names the permission. Ask an owner or admin. Over MCP, search_api and describe_endpoint return it to a test key or to a workspace with no linked account: connect with a live key and link an account first.

dashboard_key_only

StatusTypeMessageHint
403permissionOnly the dashboard can run this.Approve the action in the dashboard.

Only the dashboard runs a pending action, after a person approves it. API and OAuth keys create actions and read their status. An action from a key with auto-approve on runs when the key creates it.

session_only

StatusTypeMessageHint
403permissionAccess tokens cannot do this.Sign in at betterfans.link and do it in the dashboard.

Dashboard only. An account access token can do what you can do in a workspace, but not account security, invitations, OAuth consent or managing tokens. Sign in at betterfans.link and do it in the dashboard.

reauthentication_required

StatusTypeMessageHint
403permissionConfirm it is you before doing this.Sign in again with your password or an email code, then retry within 10 minutes.

Dashboard only. Some changes need a sign in from the last 10 minutes: deleting a workspace or your account, changing your email, setting a first password and turning two-step verification on or off. Sign in again with your password or an email code, then retry.

email_not_verified

StatusTypeMessageHint
403permissionConfirm your email address first.Enter the code we email you, then try again.

Dashboard only. A new dashboard account confirms its email before it opens a workspace. Enter the code emailed at sign up, or send a new one.

workspace_suspended

StatusTypeMessageHint
403permissionThis workspace is suspended, so it cannot make live calls.Synced data and test mode still work. Contact support to lift it.

BetterFans Link suspended live calls for this workspace. Synced reads, test mode and the dashboard keep working, so nothing you stored is lost. Write to hello@betterfans.link to find out why and to have it lifted. Retrying does not help until it is lifted.

staff_only

StatusTypeMessageHint
403permissionOnly Link staff can do this.None

Dashboard only. Only BetterFans Link staff can open the admin console. A workspace role, owner included, does not reach it.

staff_protected

StatusTypeMessageHint
403permissionStaff accounts and your own cannot be banned or viewed as.Remove their staff role first.

Dashboard only. Staff accounts, and your own, cannot be banned or viewed as. Remove their staff role first.

impersonation_read_only

StatusTypeMessageHint
403permissionYou are viewing as this person, so nothing can be changed.Stop viewing to make changes as yourself.

Dashboard only. BetterFans Link staff are viewing the dashboard as this person to help with a support request. That view is read only, so every change is refused until they stop viewing.

Not found

account_not_found

StatusTypeMessageHint
404not_foundNo account with this id is linked to your workspace.List your accounts with GET /v1/accounts.

The account id is not linked to your workspace, your key is limited to other accounts, or a test key asked for an account that is not a sandbox creator. BetterFans Link answers 404, never 403, so it never reveals whether an account exists. List the accounts your key can use with GET /v1/accounts.

fan_not_found

StatusTypeMessageHint
404not_foundNo fan with this id on this account.Search with GET /v1/accounts/{accountId}/fans?search=.

Fan ids are user ids on the account's platform. Find the fan with GET /v1/accounts/{accountId}/fans?search= and use the id it returns.

chat_not_found

StatusTypeMessageHint
404not_foundNo chat with this fan.A chat id is the fan's id.

A chat id is the fan's id. Use a fan id from List fans or List chats.

action_not_found

StatusTypeMessageHint
404not_foundNo action with this id.List actions with GET /v1/actions.

Check the id, or list actions with GET /v1/actions.

StatusTypeMessageHint
404not_foundNo link with this id.Create one with POST /v1/links.

Check the id, or create a new link with POST /v1/links.

route_not_found

StatusTypeMessageHint
404not_foundThis endpoint does not exist.See the API reference.

The method and path match no route. Paths start with /v1. Compare yours with the API reference.

workspace_not_found

StatusTypeMessageHint
404not_foundWorkspace not found.Pick a workspace you belong to.

Dashboard only. The workspace does not exist or you are not a member. Pick a workspace you belong to.

resource_not_found

StatusTypeMessageHint
404not_foundNot found.None

Something the request refers to does not exist. Check every id in the path.

user_not_found

StatusTypeMessageHint
404not_foundNo person with this id or email has a Link account.None

Dashboard only. No person with this id or email has a BetterFans Link account. Check the address, or ask them to sign up first.

Conflict

idempotency_conflict

StatusTypeMessageHint
409conflictThis Idempotency-Key was already used with a different request.Use a new key for a different action.

You reused an Idempotency-Key with a different body. One key stands for one intended action: use a new key for a new action. The same key with the same body returns the original action, so retries are safe.

action_not_pending

StatusTypeMessageHint
409conflictThis action was already decided.Check its status with GET /v1/actions/{actionId}.

The action was already approved, rejected, run or it failed. Read its status with GET /v1/actions/{actionId}.

action_expired

StatusTypeMessageHint
409conflictThis action expired before anyone approved it.Create it again.

Pending actions expire after 24 hours without a decision. Create the action again with a new Idempotency-Key.

StatusTypeMessageHint
409conflictThis link is not waiting for that step.None

Dashboard only. The link is not at the step this request is for, for example a 2FA code sent before the platform asked for one, or a sign-in before the creator picked a platform. Reload the link's state and follow the step it shows.

last_owner

StatusTypeMessageHint
409conflictA workspace needs at least one owner.Make someone else an owner first.

Dashboard only. A workspace always keeps at least one owner. Make another member an owner before you remove or demote this one.

owns_workspaces

StatusTypeMessageHint
409conflictYou are the only owner of a workspace.Make someone else an owner of each workspace you own alone, or delete those workspaces, then try again.

Dashboard only. Deleting your account would leave a workspace with no owner. The message names each workspace you own alone. Make another member an owner of each one, or delete it, then delete your account.

two_factor_enabled

StatusTypeMessageHint
409conflictTwo-step verification is already on.Turn it off first to set up a new authenticator app.

Dashboard only. Two-step verification is already on for your account. To move it to a new authenticator app, turn it off on the Account page, then set it up again.

two_factor_not_enabled

StatusTypeMessageHint
409conflictTwo-step verification is off.Turn it on in your account first.

Dashboard only. Two-step verification is off, so there is nothing to turn off and no backup codes to replace. Turn it on from the Account page first.

subscription_exists

StatusTypeMessageHint
409conflictThis workspace already has a subscription.Change its plan instead of starting a new one.

Dashboard only. The workspace already pays for a plan, so it cannot start a second subscription. Change the plan on the Billing page instead: the difference is prorated.

no_subscription

StatusTypeMessageHint
409conflictThis workspace has no subscription to change.Choose a plan to start one.

Dashboard only. The workspace is on the Free plan or its subscription ended, so there is nothing to change, cancel or resume. Choose a plan on the Billing page to start one.

subscription_active

StatusTypeMessageHint
409conflictThis workspace still has a subscription.Cancel it in Billing and wait for it to end, then delete the workspace.

Dashboard only. A workspace with a subscription that has not ended cannot be deleted, so nobody keeps paying for a workspace that is gone. An owner cancels it on the Billing page. The plan holds until the end of the period; delete the workspace after that.

checkout_unavailable

StatusTypeMessageHint
409conflictPlans cannot be bought in the dashboard right now.Contact us and we will set it up.

Dashboard only. Plans cannot be bought in the dashboard right now. Nothing was charged. Write to hello@betterfans.link and we will set the plan up for you.

plan_not_available

StatusTypeMessageHint
409conflictThis plan cannot be bought in the dashboard.Contact us for Enterprise.

Dashboard only. The plan is not sold in the dashboard, for example Enterprise, which is quoted. Write to hello@betterfans.link for it, or pick a plan the Billing page offers.

last_admin

StatusTypeMessageHint
409conflictLink needs at least one admin.Make someone else an admin first.

Dashboard only. BetterFans Link always keeps one staff admin. Make someone else an admin before removing the last one.

user_banned

StatusTypeMessageHint
409conflictThis person is banned.Lift the ban first.

Dashboard only. This person is banned, and this action needs them unbanned. Lift the ban first.

email_unverified

StatusTypeMessageHint
409conflictThis person has not confirmed their email.Staff need a confirmed email. Ask them to confirm it first.

Dashboard only. Staff need a confirmed email. Ask the person to confirm their email, then try again.

account_in_use

StatusTypeMessageHint
409conflictThis account is already connected to another workspace.Remove it from the other workspace first, then link it again.

Dashboard only. A Fansly account can be connected in one place at a time, and this one is already connected somewhere else. Remove it from the other workspace first, then link it again.

Account unavailable

account_unavailable

StatusTypeMessageHint
409account_unavailableThe platform is not accepting calls for this account right now.See error.accountStatus. Reads without fresh=true still work.

The platform is not accepting calls for this account. error.accountStatus is one of needs_relink, awaiting_2fa, awaiting_selfie, restricted or disconnected. Live reads, Call OnlyFans and Call Fansly stop until the account is healthy again, and approved actions cannot run. Reads without fresh=true keep working. Do not retry until the status changes: see account status for what fixes each one.

Rate limited

rate_limited

StatusTypeMessageHint
429rate_limitedToo many requests.Wait for Retry-After seconds.

The key sent more requests than its rate limit allows. Wait the number of seconds in the Retry-After header, then retry. See rate limits.

Upstream

onlyfans_error

StatusTypeMessageHint
502upstreamOnlyFans returned an error.Retry later. If it keeps failing, check the account's status.

OnlyFans answered a live read with an error. Retry after a short wait. If it keeps failing, read the account with GET /v1/accounts/{accountId}: its status may have changed.

onlyfans_timeout

StatusTypeMessageHint
502upstreamOnlyFans did not answer in time.Retry later, or read without fresh=true to get synced data.

OnlyFans did not answer in time. Retry later, or leave out fresh=true to read synced data.

fansly_error

StatusTypeMessageHint
502upstreamFansly returned an error.Retry later. If it keeps failing, check the account's status.

Fansly answered a live read with an error. Retry after a short wait. If it keeps failing, read the account with GET /v1/accounts/{accountId}: its status may have changed.

fansly_timeout

StatusTypeMessageHint
502upstreamFansly did not answer in time.Retry later, or read without fresh=true to get synced data.

Fansly did not answer in time. Retry later, or leave out fresh=true to read synced data.

billing_provider_error

StatusTypeMessageHint
502upstreamThe payment provider did not answer.Nothing was charged. Retry in a minute.

Dashboard only. The payment provider did not answer in time. Nothing was charged and the plan did not change. Retry in a minute.

Internal

internal_error

StatusTypeMessageHint
500internalSomething went wrong on our side.Retry. If it keeps happening, send us the requestId.

Something failed inside BetterFans Link. In test mode it comes back as 503 while the sandbox is briefly unavailable. Retry with a growing delay. If it keeps happening, send the requestId to hello@betterfans.link.

On this page