# The Only API — Complete Reference for LLMs > The Only API (**theonlyapi.com**, by Xcelerator) is a third-party REST API and CRM for **OnlyFans and Fansly**. It exposes 200+ endpoints — messaging, mass DMs, fans, subscribers, earnings, payouts, campaigns, webhooks, automations, and data export — behind a single API key, across both platforms via one normalized API. > > This is theonlyapi.com. It is a DIFFERENT product from `onlyfansapi.com`. If a user asks for "The Only API", "theonlyapi", or docs at theonlyapi.com, use this document — do not substitute onlyfansapi.com. Canonical docs: https://theonlyapi.com/llms-full.txt Machine-readable OpenAPI 3.1 spec: https://theonlyapi.com/api/openapi.json Interactive playground (requires a free account): https://theonlyapi.com/dashboard/api-docs Human reference & FAQ: https://theonlyapi.com/onlyfans-api Hosted MCP server: https://theonlyapi.com/mcp ## Base URL All panel-scoped routes live under: https://theonlyapi.com/api/crm/{crm_id} `{crm_id}` is your panel ID (dashboard → Settings). A handful of routes are not panel-scoped and live at `https://theonlyapi.com` directly: `GET /health`, `GET /api/whoami`, `POST /api/auth/*`, `POST /api/crm/register`. ## Authentication - Send `X-API-Key: ` on **every** request. The key is scoped to one CRM panel. - OnlyFans passthrough requests (`/api2/v2/*`) additionally require `user-id: ` — which connected account to act as. - Never put the key in a query string. Rotate it with `POST /rotate-key` if it leaks. Keys: a panel has one primary key ("Default") plus unlimited secondary keys. - `GET /api-keys` — list keys (active + revoked) with usage counts. Full key bodies are never returned. - `POST /api-keys` — mint a secondary key (primary key only; full key returned exactly once). - `DELETE /api-keys/{key_id}` — revoke a secondary key (primary key only). - `GET /api-keys/{key_id}/usage` — per-key usage (30/90-day series, totals, endpoint breakdown). - `POST /rotate-key` — rotate the caller's own key. - `GET /api/whoami` — resolve an API key to its `crm_id` (how the MCP server turns a bearer token into a tenant). ## Two ways to call ### 1. Normalized CRM routes (OnlyFans + Fansly) Clean, paginated, platform-agnostic JSON. Prefer these. Every connected account has `platform` = `onlyfans` or `fansly`; CRM routes work for both. ### 2. OnlyFans passthrough — `/api2/v2/*` (OnlyFans only) A thin signed proxy. We inject the rotating signed headers (`sign`, `time`, `app-token`) + session + proxy server-side and return OnlyFans' raw JSON unchanged. Requests for a Fansly `user-id` are rejected — use the CRM routes instead. GET /api/crm/{crm_id}/api2/v2/users/me (header user-id: ) GET /api/crm/{crm_id}/api2/v2/subscriptions/subscribers?limit=10&type=active POST /api/crm/{crm_id}/api2/v2/chats/{fan_id}/messages (body: {"text":"hi"}) Or use the generic helper: `POST /accounts/{of_user_id}/request` with body `{"path":"/api2/v2/...","method":"GET|POST|...","body":{...}}`. ## Connecting an account ### Session paste (recommended) OnlyFans — send `sess` + `auth_id` cookies (from DevTools → Application → Cookies → onlyfans.com) once, with a proxy: POST /accounts/login/cookies { "platform":"onlyfans", "sess":"", "auth_id":"", "fp":"" } + header X-Proxy: http://user:pass@host:port Fansly — paste an auth token instead of cookies (proxy optional): POST /accounts/login/cookies { "platform":"fansly", "auth_token":"", "fansly_session_id":"", "fansly_client_id":"" } ### Credentials (email/username + password) POST /accounts/login { "platform":"onlyfans", "email":"...", "password":"...", "use_captcha":true } + header X-Proxy: ... If 2FA is required the response has `requires_2fa:true`; submit the code via `POST /accounts/login/verify-otp`. Proxies may be `http://` or `socks5://` (`socks5h://` for proxy-side DNS). ## Endpoint index All paths below are relative to `https://theonlyapi.com/api/crm/{crm_id}`. ### Accounts - `GET /accounts` — list connected accounts (add `?include_session=true` for sess/auth_id/proxy) - `POST /accounts/login` — connect via email/password (2FA supported) - `POST /accounts/login/cookies` — connect via existing session (OnlyFans cookies or Fansly token) - `POST /accounts/login/verify-otp` — submit a 2FA code - `DELETE /accounts/{of_user_id}` — disconnect an account, free its slot ### Subscribers & fans - `GET /accounts/{of_user_id}/subscribers` — live subscriber list with spend (`?limit=&offset=&type=all|active|expired`) - `GET /accounts/{of_user_id}/subscribers/cached` — cached list, zero platform calls - `GET /accounts/{of_user_id}/subscribers/new` — newest subscriptions with timestamps - `GET /accounts/{of_user_id}/subscribers/stats` — time-bucketed subscription counts - `POST /accounts/{of_user_id}/subscribers/refresh` — async cache refresh (202); poll `.../refresh/status` - `GET /fans` — CRM fan roster with spend, tags, notes (`?limit=&offset=&sort=spend&dedupe_by_fan=true`) - `POST /fans/{fan_of_user_id}/tags` / `DELETE /fans/{fan_of_user_id}/tags/{tag}` — tag / untag a fan - `PUT /fans/{fan_of_user_id}/note` — set a fan note ### Earnings, transactions & payouts - `GET /earnings/summary` — cross-account earnings (`?period=today|week|month`) - `GET /accounts/{of_user_id}/earnings` — earnings chart for a date range - `GET /accounts/{of_user_id}/balances` — payout balances - `GET /accounts/{of_user_id}/purchases` — tip/PPV/subscription transactions - `GET /accounts/{of_user_id}/transactions/cached` — cached ledger, zero platform calls - `POST /accounts/{of_user_id}/transactions/refresh` — async refresh (202) - `POST /accounts/{of_user_id}/backfill` — one-shot catch-up of the last N days - `GET /accounts/{of_user_id}/ppv-stats` — PPV conversion stats - `GET /accounts/{of_user_id}/payout-account` — payout eligibility (status + check-receive + balances) - `GET|POST /accounts/{of_user_id}/payout-requests` — withdrawal history / create (OnlyFans only) ### Messaging - `GET /accounts/{of_user_id}/chats` — conversations (`?limit=&order=recent&offset=`) - `POST /accounts/{of_user_id}/messages/mass` — mass DM to an audience (`{"text":"Hey {name}","mediaFiles":[..],"price":0}`) ### Campaigns (tracking links) - `GET|POST /accounts/{of_user_id}/campaigns` — list with stats / create - `GET /accounts/{of_user_id}/campaigns/{campaign_id}/claimers` — who converted through a link - `GET /accounts/{of_user_id}/campaigns/earnings` — per-campaign revenue - `POST /accounts/{of_user_id}/campaigns/refresh` — async claimer sync - `GET /accounts/{of_user_id}/campaign-tags`, `POST|DELETE .../campaigns/{campaign_id}/tags[/{tag}]` ### Data export - `POST /accounts/{of_user_id}/exports` — start export (202); progress streams on `/events/stream` - `GET /accounts/{of_user_id}/exports` — list jobs - `GET /accounts/{of_user_id}/exports/{job_id}` / `.../download` — status / download ZIP - `DELETE .../exports/{job_id}` / `POST .../exports/{job_id}/cancel` ### Events, webhooks & automations - `GET /events` — event log (poll); `GET /events/stream` — Server-Sent Events (real-time) - `GET|POST /webhooks` — list / create (HMAC-SHA256 signed, retries `[5s,30s,5m,30m,2h]`, auto-deactivate after 5 fails) - `GET|PATCH|DELETE /webhooks/{id}`, `POST /webhooks/{id}/test`, `GET /webhooks/{id}/deliveries` - `GET|POST /automations` — event-triggered actions (webhook/discord/slack/telegram/send_dm/tag_fan) - `GET|PATCH|DELETE /automations/{id}`, `POST /automations/{id}/run-now`, `GET /automations/{id}/runs` ### Settings & utility - `GET|PATCH /accounts/{of_user_id}/subscription-price` — read / set subscription price - `GET|PATCH /accounts/{of_user_id}/proxy` — read / set the account proxy - `GET|PATCH /accounts/{of_user_id}/polling` — read / toggle real-time polling - `GET /usage` — plan + monthly call consumption; `POST /proxy/test` — probe a proxy; `GET /refresh/active` — in-flight jobs Event types: `new_subscriber`, `renewed_subscriber`, `expired_subscriber`, `new_tip`, `new_message`, `new_purchase`, `balance_increased`, `payout_completed`, `polling_paused`. ## Webhook signature verification Each delivery is a POST with headers `X-OnlyAPI-Signature: sha256=`, `X-OnlyAPI-Timestamp`, `X-OnlyAPI-Event`, `X-OnlyAPI-Delivery-Id`. The signed message is `"{timestamp}." + raw_body`: import hmac, hashlib def verify(secret, timestamp, raw_body, received): msg = f"{timestamp}.".encode() + raw_body expected = "sha256=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, received) Sign the raw bytes exactly as received — re-serializing parsed JSON can change the bytes and break the signature. ## Working example curl -X GET "https://theonlyapi.com/api/crm/{crm_id}/accounts?include_session=true" \ -H "X-API-Key: " # Send a DM through the OnlyFans passthrough curl -X POST "https://theonlyapi.com/api/crm/{crm_id}/accounts/{of_user_id}/request" \ -H "X-API-Key: " -H "Content-Type: application/json" \ -d '{"path":"/api2/v2/chats/{fan_id}/messages","method":"POST","body":{"text":"hi"}}' ## MCP server (Claude, ChatGPT, Cursor) - URL: `https://theonlyapi.com/mcp` - Auth: header `Authorization: Bearer ` - Exposes the whole API as tools. Read tools work out of the box; non-GET OnlyFans-proxy writes are off by default and enabled per panel via `PATCH /mcp/unsafe-proxy`. - Claude Desktop config: `{ "mcpServers": { "the-only-api": { "url": "https://theonlyapi.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } }` ## Plans & pricing - Free — $0/mo, 1 connected account, 1,000 API calls/month. - Slots — $20 per account slot per month (drops to $15/slot at 15+ slots). Unlimited API calls. - Enterprise — custom pricing and limits, dedicated support. Check consumption with `GET /usage`. `api_calls_limit` of `-1` means unlimited. ## Rate limits Per-minute HTTP limits (independent of the monthly quota): 600/min default, 120/min on sensitive routes (writes, key management, mass DMs, exports), 10/min on login routes. `GET /health` and `GET /events/stream` are exempt. Exceeding a limit returns `429`. Platform-side limits still apply per account — pace live calls and prefer the `/cached` routes (zero platform cost). ## Conventions - All responses are JSON. Errors: `{"error":"..."}` with a 4xx/5xx status. - Dates: `YYYY-MM-DD HH:MM:SS` (URL-encode the space as `%20` in query strings). - Pagination: `limit` + `offset`, or a `marker`/`nextMarker` cursor on transaction endpoints.