The Only API docs

Responses & errors

Two envelopes, the real error shapes, and one wrapper that will confuse you if you test without a key.

Two envelopes

Which envelope you get depends on which surface you are on.

Data is a sibling key alongside success, named after the resource:

{
  "success": true,
  "list": [  ],
  "total": 412,
  "limit": 100,
  "offset": 0,
  "hasMore": true
}

Not universal

This is the convention, not a guarantee. Some CRM routes return a bare object or a bare array with no success field. Do not write a client that requires success to be present on every CRM response — check the individual endpoint page.

Error shapes

There is no single error envelope. In practice you will see three shapes:

// Most handlers — a bare error string
{ "error": "Invalid API key" }

// Validation failures
{ "error": "limit must be at most 500" }

// Uncaught server errors
{ "error": "Internal server error", "correlation_id": "a1b2c3d4" }

Some routes additionally include success: false, and some include a machine code:

// Mass messaging — the one route using an UPPERCASE code
{ "success": false, "code": "PLATFORM_NOT_SUPPORTED", "error": "mass messaging is OnlyFans-only for now" }

// Every other 501 — lowercase, with `platform` and `feature`
{ "success": false, "code": "platform_not_supported",
  "error": "\"referrals\" is not available for fansly accounts yet.",
  "platform": "fansly", "feature": "referrals" }

Compare that code case-insensitively — branching on the uppercase spelling alone misses every 501 except mass DMs.

Write your error handling defensively

Read error as the human message, and treat success, code and correlation_id as optional. Do not branch on success being present.

Rate limiting and quota errors carry extra fields:

{ "error": "Rate limit exceeded", "retry_after": "1000 per 1 minute" }
{ "error": "API call limit reached", "plan": "only-api-free", "limit": 1000, "used": 1000, "upgrade_url": "…" }
// 403, not 429
{ "error": "Account limit reached (10). Buy a slot to add another account.",
  "code": "SLOT_LIMIT", "plan": "only-api-free",
  "accounts_used": 10, "accounts_limit": 10, "slot_purchase_url": "…" }

500 responses are deliberately opaque — the full traceback is logged server-side only, so SQL fragments, file paths, proxy credentials and session tokens never reflect back to you. Quote the correlation_id when reporting one.

Status codes

CodeMeaning
200Success
202Async job accepted — poll its status route. See Async jobs
400Validation error, or a missing required header
401No X-API-Key sent, bad dashboard credentials, or a session that could not be re-established (relogin_failed)
403Invalid key, wrong panel, primary-key-only route, write actions disabled, or the account slot limit
404Unknown job or, on a few account routes, an account that is not yours
409An async job of that kind is already running for this account
429Per-minute rate limit or monthly quota
500Server error — includes a correlation_id
501The operation is not supported for that account's platform

The _notice wrapper means you are on a decoy path

Some URLs on this domain are honeypot bait rather than API routes — notably /api/health, /api/v1/*, /api/internal/*, and paths like /.env and /.git/config. Those return procedurally-generated fake data with a _notice string prepended (and a top-level array reshaped into {_notice, data: […]}), plus X-Honeypot-* and X-AI-Notice headers.

If you see `_notice`, you are not talking to the API

The decoy is selected by path, not by authentication. GET /api/health is bait and reports a fabricated version and service list; the real health check is GET /health on api.theonlyapi.com.

The real API is panel-scoped: https://theonlyapi.com/api/crm/{crm_id}/…. A missing or wrong key there gives you a plain 401/403 JSON error with no _notice.

There is no User-Agent check on the API. Testing with curl needs no -A flag — the User-Agent only appears echoed inside a decoy notice, and never changes which response you get.

Other headers

Every response carries Cache-Control: no-store plus a standard security header set (HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy). Rate-limit state is exposed via X-RateLimit-* headers.

CORS is restricted for /api/*; allowed request headers are Content-Type, X-API-Key, X-Proxy and Authorization. If you are calling from a browser origin, proxy through your own backend instead.

On this page