OnlyFans API Without Scraping
This is an infrastructure question, not a philosophy one. Both approaches drive a logged-in OnlyFans account — there is no official API, and ofapis is an unofficial, independent integration either way. What differs is the list of components running in your deployment, who is on call for each, and what shows up on the invoice.
So the useful way to read "without scraping" is as a parts list you delete. Below is that list, what each part actually costs to keep alive, and — because this only works if it is honest — the surfaces where a browser still reaches something this API does not.
The parts list you stop deploying
A production OnlyFans scraper is not a script, it is a small distributed system. Stripped to its components, a deployment looks roughly like this:
# a self-hosted scraper, in the shape it actually takes
services:
browser-worker: # one Chromium per creator, kept logged in
deploy: { replicas: 8 }
shm_size: 1gb
session-store: # cookies + local storage, per account
image: redis
refresh-cron: # re-login before the session lapses
totp-service: # two-factor codes, per account
proxy-router: # bought IPs, mapped account -> egress
parse-layer: # selectors + fixtures, versioned
challenge-queue: # the ones a human has to clear
media-uploader: # multipart to presigned storage, with retries
Every line is a thing to deploy, monitor, patch and pay for. The maintenance ledger behind it:
| Component you run | Scales with | Recurring work it creates |
|---|---|---|
| Browser workers | Linked accounts | Memory per instance, Chromium upgrades, crash recovery |
| Session/cookie store | Accounts × session lifetime | Encryption at rest, re-login scheduling, partial-state bugs |
| Two-factor retrieval | Accounts | Secret storage, a path for every creator who changes device |
| Parse layer | Surfaces you read | Fixture updates on every front-end change, silent empty results |
| Proxy inventory | Accounts | Procurement, per-IP billing, replacing addresses that go bad |
| Media path | File sizes | Multipart chunking, resumable retries, hash verification |
| Challenge handling | Unpredictable | Human on call; cannot be retried away |
None of this is the product you set out to build. It is the cost of standing where the platform's own client stands.
What replaces it
One HTTP client with a Bearer token. There is no browser in your deployment, no cookie jar, no refresh cron, no proxy contract, and no selector file. You link an account once — the creator signs into a browser session themselves, and the session material is held server-side encrypted — then your code talks to https://api.ofapis.com/api/public/v1 with a single Authorization: Bearer ofapis_sk_... header. No request signing, no HMAC, no per-call handshake.
The response contract is the other half of the deletion. Success is always {"data": ...}; a failure is a real HTTP status, with an {"error":{"code","message"}} body when the gateway refused it and a plain-text message when a handler raised it. Every response carries X-Request-Id plus X-RateLimit-Limit, -Remaining and -Reset. That matters more than it sounds: a parse layer's characteristic failure is a selector matching zero elements and the job reporting success, whereas a declared error code is something a monitor can page on. The full surface is in the OpenAPI spec.
Two pieces of plumbing that are disproportionately annoying to build yourself come included. Media upload is a single POST /accounts/{id}/media within documented bounds — 50 MB, image/jpeg, image/png, image/webp, video/mp4 — instead of your own presigned multipart with retry and hash checks; see media upload. And any POST accepts an Idempotency-Key, so a replay returns the original response with Idempotency-Replayed: true rather than sending a second DM. Building that exactly-once guarantee into a scraper is a project in itself.
Costing it properly
The comparison people usually run — monthly invoice versus zero — is the wrong one, because the DIY column is not zero, it is unpriced. Fill in your own numbers for compute across N browser workers, proxy inventory per account, storage for sessions and media, and engineer hours per month against the parse layer and the challenge queue. That last line is the one that bites: it never finishes.
The managed column is arithmetic you can do up front, because credit weights are published and are not flat. Most calls cost one credit; a chat message costs 2, a scheduled post or message 3, a media upload 5, and a mailing trigger costs nothing at the call but debits per recipient when the run executes. A three-creator agency, counted for one month:
replies = 3 * 1800 # POST /accounts/{id}/chats/{chatId}/messages @ 2
scheduled = 3 * 60 # POST /accounts/{id}/scheduled/posts @ 3
uploads = 3 * 40 # POST /accounts/{id}/media @ 5
reads = 3 * 900 # subscribers, chats, lists, notifications @ 1
campaigns = 3 * 2 * 1200 # two mailings each to 1,200 fans @ 1/recipient
total = replies*2 + scheduled*3 + uploads*5 + reads + campaigns
print(total) # 21840
Note what decides the plan here. 21,840 credits fits inside Pro's 120,000 with enormous room — but Starter allows one linked account, so three creators means Pro ($159/mo) regardless of volume. Account count, not credit burn, is the binding constraint for most agencies. Credits only take over when campaigns get big, because a blast is one request and recipientCount credits. Per-plan detail and the metering rules are in API cost; the plan table has the linked-account limits those three creators run into.
One honest caveat on the metering: credits are debited before the handler runs, and not every failure is free. 401, 402, 403 and 429 are refused before metering. 409, 424 and 5xx are debited and refunded. But 400, 404, 413, 415 and 422 are charged and stay charged, because the request was authorised and executed — the payload was the problem. Validate locally rather than probing the API to find out what it accepts.
What you build instead
With the plumbing gone, the work left is product: an auto-reply bot driven by the message.received webhook rather than a poll loop (webhook setup), a content calendar on scheduled posts and messages, or fan targeting through lists — the automation API is the full menu of what the surface supports. The webhook path is also the single biggest efficiency: polling one chat every minute is roughly 43,000 requests a month, and it is replaceable with deliveries you can dedupe on the X-Ofapis-Delivery header.
Where a scraper still reaches further
Be clear about the gaps, because they decide the choice for some teams.
- No aggregate financial surface. There is no statistics, transactions or earnings route, and no top-level posts endpoint. A browser session can open the creator's earnings screens; this API cannot.
- One webhook event type.
message.receivedis the only event that exists. Tips, new subscriptions and post state still have to be polled. - Bounded media. 50 MB and four MIME types. A browser is limited only by what the site itself accepts.
- Windowed reads.
GET /accounts/{id}/scheduled/postsrequires bothpublishDateandpublishDateEnd, so there is no "dump everything scheduled" call. - No spend on a subscriber record. A subscriber is
{id, username, name, avatar, subscribedOn}, andsubscribedOnis a boolean rather than a date — if your product ranks fans by lifetime spend, that number has to be assembled on your side.
If one of those is central to what you are building, say so early; a managed API will not cover it today. For the failure-mode side of the same decision, see OnlyFans API vs scraping; for the account-risk side, is the OnlyFans API safe?; and for the terms-and-authority side, is the OnlyFans API legal? — none of which changes because you deleted the browser workers.
FAQ
What infrastructure do I actually remove by not scraping?
The browser workers, the cookie and session store, the re-login scheduler, the two-factor retrieval path, the selector and fixture layer, the proxy inventory, and the custom media upload pipeline. What remains on your side is an HTTP client and whatever your product does with the data. The deletion is of components and on-call surface, not just of code.
Does ofapis run a scraper on my behalf?
The practical distinction is where the work lives and what contract you code against. You get documented JSON endpoints with declared error codes and versioned behaviour rather than HTML you parse yourself, and no browser or credential handling sits in your deployment. It remains an unofficial integration with an account that OnlyFans has not licensed.
How do I compare the cost honestly?
Price the DIY column instead of leaving it blank: compute per browser worker, proxy inventory per account, storage, and engineer hours per month on the parse layer. Then weight your monthly operation counts against the published credit weights, as in the example above. For most agencies the plan is chosen by how many accounts need linking, not by credit burn.
Will an OnlyFans redesign break my integration?
Front-end changes are absorbed on our side rather than in your parser, so the shape of {"data": ...} does not move when the website does. That is the main structural argument for the managed model. It is not a promise of zero downtime — it moves the maintenance, it does not abolish it.
When should I still write a scraper?
When you need something the API has no route for — aggregate earnings or transaction history are the common ones — or when you run a single account, have browser-automation skills in-house, and genuinely prefer maintenance work to a monthly invoice. Those are real cases. Everything else tends to end up rebuilding the parts list above.
OnlyFans API vs scraping · Read the docs · Account risk, honestly · Start free