Blog/Guides
Guides

OnlyFans API Rate Limits and Safe Polling: Backoff, Jitter, and Caching

Design a safe OnlyFans polling loop with cache-first reads, bounded concurrency, exponential backoff, jitter, checkpoints, and webhook fallbacks.

J
Jordan H. · Co-Founder & CEO, The Only API·Sep 23, 2026·10 min read·Updated Sep 23, 2026
OnlyFans API Rate Limits and Safe Polling: Backoff, Jitter, and Caching

A high API limit is not permission to poll as fast as possible. The hosted service, your plan quota, and the upstream creator platform are different systems with different constraints. A safe client respects all three.

The goal is freshness with the fewest upstream calls: use cached data for dashboards, webhooks for changes, and scheduled reconciliation for correctness.

Three limits to model separately

Service flood limits. The deployed API publishes separate tiers for ordinary reads, sensitive writes, and login routes. Treat response headers and HTTP 429 as authoritative because deployment configuration can change.

Monthly plan quota. The free plan includes a finite monthly call allowance; paid slots are unlimited at the monthly layer. GET /usage reports api_calls_used and api_calls_limit, where -1 represents unlimited monthly calls.

Platform safety. A request accepted by the hosted API can still reach OnlyFans or Fansly. Bursting against one creator account is riskier than distributing the same volume across cached local reads. The documentation recommends cache-first access and conservative per-account pacing.

Calculate the real cost of a polling loop

One request every minute is 43,200 calls in a 30-day month. Multiply by the number of endpoints and connected accounts. A job reading chats, transactions, and subscribers once per minute across ten accounts can create more than 1.2 million scheduled reads before retries.

Most of those calls return no change. Webhooks plus periodic reconciliation usually provide better freshness at a fraction of the traffic.

Use a per-account scheduler

Do not put every account in one global setInterval. Maintain independent next-run timestamps and limit concurrent upstream work. Add random jitter so a deployment restart does not cause every account to fire on the same second.

javascript
function nextDelay(baseMs, attempt) {
  const capped = Math.min(baseMs * 2 ** attempt, 15 * 60_000);
  return capped * (0.75 + Math.random() * 0.5);
}

async function pollAccount(account, attempt = 0) {
  try {
    await syncChangedData(account);
    schedule(account, nextDelay(5 * 60_000, 0));
  } catch (error) {
    if (error.status === 401 || error.status === 403) {
      pauseAndAlert(account, error);
      return;
    }
    schedule(account, nextDelay(5_000, attempt + 1));
  }
}

Bound the retry count or escalation time. A permanently disconnected account should become an operator task, not a worker that retries forever.

Honor 429 correctly

When a response is 429:

  • Parse Retry-After when present.
  • Stop new work for the same credential or account until the delay passes.
  • Do not retry all failed requests simultaneously.
  • Reduce concurrency after repeated limits.
  • Preserve the checkpoint so the next run resumes safely.

A 429 is a control signal, not a reason to rotate IPs or evade the limit.

Prefer cached endpoints

The CRM exposes cache-backed routes for synced subscribers and transactions. Cache reads avoid repeating platform requests and provide stable limit/offset pagination with a total. Use live routes only when the product requires current upstream data.

A practical dashboard pattern is:

  1. 1.Render cached data immediately.
  2. 2.Show its last-synced timestamp.
  3. 3.Start a refresh job only when stale or requested.
  4. 4.Listen for refresh completion through events.
  5. 5.Re-read the cache.

Async refresh endpoints can return 202. Poll the matching status endpoint or use the event stream; do not resubmit the refresh every few seconds. A 409 or already_running response means work already exists and should be treated as success.

Use conditional work inside your database

Before calling the platform, ask whether anything is due. Store per-account fields such as last_success_at, next_due_at, consecutive_failures, cursor, and status. Claim due rows transactionally so two workers cannot poll the same account at once.

After a successful page, commit data and cursor together. If the process dies, the cursor still points at the last durable page.

Webhooks for speed, reconciliation for truth

Use signed webhooks for low-latency new-message, tip, purchase, and subscriber events. Run a slower reconciliation job to catch downtime, disabled webhooks, or out-of-order delivery.

This hybrid design is more reliable than either approach alone. A pure polling system wastes calls; a pure webhook system can miss an operational outage unless you monitor and reconcile it.

Platform-aware scheduling

Do not assume OnlyFans and Fansly have identical real-time behavior. Fansly polling and WebSocket collection can be gated by deployment while on-demand normalized reads still work. The Fansly quickstart explains the supported read path, and the Fansly automation guide documents the event caveat.

Use the Python client tutorial or Node.js client tutorial for bounded request helpers. Check current service behavior in the documentation, confirm your monthly model on pricing, and alert on sustained 429 rates rather than hiding them.

Sources · last verified Sep 23, 2026

Published service limits can change. The client patterns in this guide deliberately adapt to response status and Retry-After instead of assuming one permanent number.