Connect an account
Session paste or credentials, 2FA, and the proxy requirement.
Before you can read or write anything for a creator, that account has to be connected to your panel. Each connected account occupies one slot.
There are two ways in. Session paste is more reliable and is what we recommend.
Option A — session paste (recommended)
Open a browser logged into OnlyFans, then DevTools → Application → Cookies → onlyfans.com and copy sess and auth_id (and fp if present).
curl -X POST "$BASE/api/crm/$CRM/accounts/login/cookies" \
-H "X-API-Key: $KEY" \
-H "X-Proxy: http://user:pass@host:port" \
-H "Content-Type: application/json" \
-d '{
"platform": "onlyfans",
"sess": "<sess cookie value>",
"auth_id": "<your OF user id>",
"fp": "<optional fingerprint cookie>"
}'Strongly recommended for OnlyFans, but not enforced
Omitting X-Proxy does not return an error. The login proceeds and egresses on
our shared server IP, and the saved session records no proxy — so every later
request for that account also goes out direct. The failure surfaces later, as a
session that keeps breaking. A malformed proxy is silently downgraded to none rather
than rejected, so a typo degrades to direct egress with no signal.
OnlyFans ties sessions to the IP that created them. See Proxies.
The session is stored server-side. You never pass cookies or tokens again — from
here on, the of_user_id is enough.
Option B — credentials
We run the whole login flow for you: Cloudflare init, Turnstile solve, signed login request, and a 2FA prompt if the platform asks for one.
curl -X POST "$BASE/api/crm/$CRM/accounts/login" \
-H "X-API-Key: $KEY" \
-H "X-Proxy: http://user:pass@host:port" \
-H "Content-Type: application/json" \
-d '{
"platform": "onlyfans",
"email": "[email protected]",
"password": "…",
"use_captcha": true
}'For platform: "fansly" the email field accepts a username or an email,
X-Proxy is optional, and use_captcha is ignored — Fansly's login has no captcha
step.
Two-factor authentication
If the platform demands 2FA, the response includes requires_2fa: true (Fansly
additionally returns twofa_type). Submit the code to
POST /accounts/login/verify-otp, passing the same platform, the same
identifier you logged in with. You do not need to resend X-Proxy — the proxy
stored with the parked 2FA challenge is reused, and the header is ignored here:
curl -X POST "$BASE/api/crm/$CRM/accounts/login/verify-otp" \
-H "X-API-Key: $KEY" \
-H "X-Proxy: http://user:pass@host:port" \
-H "Content-Type: application/json" \
-d '{"platform": "onlyfans", "email": "[email protected]", "otp_code": "123456"}'The field is `otp_code`, not `code`
This route reads otp_code only — sending code returns
400 OTP code required. (The unrelated
bulk-import OTP route accepts either spelling, which makes
the inconsistency easy to trip over.)
Connecting is limited to 20 attempts per minute
Each of the three connection routes gets its own allowance of 20 requests per minute. They are counted separately, so in one minute you could paste 20 sessions and attempt 20 credential logins and submit 20 OTP codes.
The count is per API key, so it is your panel's budget alone — other customers cannot use it up.
Going over returns 429 until the minute rolls over. The practical consequence: do
not retry a failed login in a loop. Twenty quick retries and you have spent the
allowance and locked yourself out of connecting anything for the rest of that minute.
Connecting many at once
For more than a handful of accounts, paste them all and let the importer work through them — including parking rows that need a 2FA code until you supply one. See Bulk account import.
After connecting
GET /accounts lists connected accounts and their IDs:
curl "$BASE/api/crm/$CRM/accounts" -H "X-API-Key: $KEY"Add ?include_session=true and each OnlyFans entry also carries a session
block with sess, auth_id and proxy — present only when a stored session file
exists; a read failure yields session_error instead. Fansly accounts never get one;
use GET /accounts/{of_user_id}/fansly-credentials for those. You rarely need either —
passing of_user_id and letting us resolve the session is the point.
Disconnecting
curl -X DELETE "$BASE/api/crm/$CRM/accounts/$OFUID" -H "X-API-Key: $KEY"This frees the slot. Add ?purge=true to also delete the account's retained
data rather than keeping it.
Write actions are off until you enable them
Connecting an account does not by itself allow us to act as it. Every write
performed as the account is gated by the allow_of_write_actions polling setting:
curl -X PATCH "$BASE/api/crm/$CRM/accounts/$OFUID/polling" \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"allow_of_write_actions": true}'Without it, writes return 403 with code: "WRITES_DISABLED". Match on the code,
not the message.
The gate now covers every OnlyFans or Fansly write performed as the account:
single DM/PPV sends (both platforms), the mass-DM real send, the send_dm
automation action, payout requests, subscription-price changes, creating a campaign,
the raw POST /accounts/{of_user_id}/request proxy when its body method is not
GET, and any non-GET request through the /api2/v2/* passthrough. Reads are
never gated.