Skip to main content
Every endpoint on /v1, the current URL version of the Fanvue API, follows the conventions below, and /v1 is the version to build new work on. Every request needs an access token and the version header, and Build your first app gets you a first authenticated call if you haven’t made one. The API speaks JSON over HTTPS. Every /v1 path carries the prefix:
A chat list is GET https://api.fanvue.com/v1/chats. The previous version drops the prefix and answers on https://api.fanvue.com/chats. The idempotency and agency scoping sections are shared with the previous version, so their paths omit the prefix; add /v1 when you call them. The OpenAPI 3.1 document for /v1 is served at /openapi-v1.json for client generators and request tools.

Authentication

Every endpoint requires a Bearer access token obtained through the OAuth 2.0 flow. Send it on every request.
A missing or expired token returns 401. A token that lacks the scope an endpoint needs returns 403 with { "error": "Insufficient scopes" }. The authentication overview explains how your app obtains a token, and Scopes lists what each scope unlocks.

API versioning

Always send the X-Fanvue-API-Version header. The current version is 2025-06-26.
The header is a date string that pins the response contract. A request without the header is served as 2025-06-26. A value the API does not recognise returns 400 with { "error": "Unsupported API version", "message": "..." }. A sunset version returns 410 with a nextVersion field naming the version to move to. The URL prefix (/v1/ or none) is a separate axis that selects the current or previous resource shapes. See API versioning.
App Store endpoints (/apps/*) exist only on the unversioned paths, so call them without a prefix even from a /v1 integration. The self-scoped /account-health and /account-health/flagged-media are also unversioned only; their creator-scoped counterparts exist at /v1/creators/{creatorUserUuid}/account-health.

Pagination

Every /v1 list is cursor-based. You pass an opaque cursor and a page-size parameter, and the response returns a nextCursor for the following page. Every list wraps its items in a top-level data array.
Send the page-size parameter the endpoint declares. The API ignores the other name and returns the default page size. Dynamic list members take cursor only, with no page-size parameter, and /v1/creators/{creatorUserUuid}/subscribers/online takes limit (default 50) with no cursor and returns { data, count }.
The standard envelope is { data, nextCursor, total }. nextCursor is null on the last page. total is present on about half the lists and is null where a count is not computed, so treat it as a hint and never page against it. The two message endpoints return { data, dateFilter } and page by sentBefore and receivedBefore instead of a cursor.
To walk the full set, repeat the request with cursor set to the previous nextCursor until nextCursor is null. Keep the page size constant for the whole walk: a cursor is valid for the page size it was issued with.
No /v1 endpoint accepts page. Moving to v1 shows how to convert a paging loop.

Error responses

Errors use HTTP status codes with a JSON body. There is no single error envelope. The body shape depends on the status and the route family. Error responses lists every body shape the API emits and what to do with each. Batch endpoints return 200 even when some items fail. A failed key carries a PerKeyError with error set to forbidden, not_found or internal, so a batch keeps its partial results instead of failing the whole request.

Rate limit headers

Each user your app acts for has a budget of 200 requests per 60 seconds, counted per OAuth client. A second 429 body, { "error": "Too many concurrent requests" }, caps in-flight requests at 6 per caller. Rate limits has the per-endpoint limits, the concurrency cap and the retry pattern. Polling list endpoints for new messages or sales is the main cause of 429 responses. Subscribe to webhooks for new events instead, and use Efficient chat sync to catch up after downtime.

Idempotency

Three write endpoints are safe to retry, each in its own way. Pick a new key for every distinct send or charge. A UUID generated when the user confirms the action is a good key: it survives a retry of the same action and never collides with the next one.

Money figures and timestamps

Amounts are integers in minor units, USD cents unless a field says otherwise. Each money-bearing resource carries its own currency field. On earnings rows, gross is the creator’s price the earning is calculated from, net is the creator’s cut after Fanvue fees, and total is what the fan was charged including tax and the fan-side transaction fee. Whether a refunded or charged-back purchase is still counted is decided per field, not per endpoint. A reversal is written as its own row for the full original amount, never as an edit to the payment it reverses, and reversedTransactionOrderId pairs the two. Insights metrics holds the payment-source table and the field-by-field reversal reference. Timestamps are UTC in ISO 8601 format. Date-range query parameters such as startDate and endDate accept an ISO 8601 datetime with or without a timezone offset, and statistics aggregate by UTC day.
Some response fields use a date-only format (date) and others a full datetime (date-time). Each field documents its own format on the endpoint page. The underlying instant is UTC in both cases.

Agency creator scoping

Agency team members act on a managed creator through the creator-scoped twin of each endpoint under /creators/{creatorUserUuid}/.... A twin takes the same body, returns the same shape and needs the same scopes as the self-serve route, plus access to that creator. The creator-scoped routes are the high-volume path for agencies: each creator has its own rate-limit bucket on them. Agency-wide list endpoints under /agencies/* cover every creator the agency manages in one response. Every row carries a creatorUuid field naming the creator it belongs to, so you can group rows without a second lookup. Pass creatorUuids, a comma-separated list of up to 50 creator UUIDs, to narrow the response. A creatorUuids value that does not belong to the agency fails the whole request with 403. The /agencies/* endpoints need read:agency on top of the data scope, and read:agency is grantable by agency admins only.