Blog

OnlyFans webhooks: a practical guide

OnlyFans has no official public API and no official webhooks, so the only way to get push notifications for events like new messages or sales is through a third party layer such as CreatorAPI webhooks. CreatorAPI sits on top of a connected creator account, watches for changes through the same authenticated endpoints the platform apps use, and delivers signed events to your server. This guide walks through why webhooks beat polling, how to register one, how delivery works, and how to write a handler that survives production.

Why webhooks beat polling

Webhooks beat polling on two axes: credits and freshness. With CreatorAPI every native read costs 1 credit, so a poller that checks the message list for ten creators once a minute burns over 14,000 credits a day before anything has even happened. A webhook write costs 1 credit to set up, and after that events arrive on their own: your server only does work when something actually changed.

Freshness is the second win. A poll loop is always as stale as its interval, and shortening the interval just multiplies the credit burn. A webhook fires when the event occurs, which matters when the event is a whale sending a message and the value of a reply decays by the minute. Keep a slow reconciliation poll if you want a safety net, but let webhooks carry the live traffic.

Registering a webhook

You register a webhook with one call: POST /v1/{account}/webhooks, passing your endpoint URL and the events you want. The {account} segment is the creator id CreatorAPI returned when you connected the account, so subscriptions are scoped per creator. The event catalog lives at GET /v1/webhooks/events and covers things like new messages, sales, renewals and subscribers. Query the catalog rather than hardcoding names, and subscribe only to the events you handle.

# register a webhook. take real event names from GET /v1/webhooks/events
curl -X POST \
  -H "X-API-Key: $CREATOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/creatorapi", "events": ["message.new", "sale.new"]}' \
  "https://api.creator-api.com/v1/{account}/webhooks"

That single write costs 1 credit. The same endpoint pattern works for Fansly accounts too, since one CreatorAPI key covers both platforms.

How delivery works

Every delivery is an HTTPS POST to your URL, signed with HMAC SHA256 so you can verify it really came from CreatorAPI. Compute the HMAC of the raw request body with your webhook secret and compare it to the signature header before you trust a single byte of the payload. Anyone can POST JSON to a public URL; the signature is what separates a real event from a forgery.

Failed deliveries are retried with backoff. If your endpoint is down or returns an error, CreatorAPI tries again with increasing delays instead of dropping the event, which is exactly why your handler must tolerate the same event arriving twice.

You do not have to wait for real traffic to test any of this. CreatorAPI lets you trigger a test delivery against your endpoint and inspect past deliveries in a delivery log, so you can confirm your signature check and response codes before a real sale is on the line.

Handler patterns that hold up

A production webhook handler does three things in order: verify, queue, respond. First verify the HMAC SHA256 signature against the raw body and reject anything that fails. Then push the verified event onto a queue or job table instead of processing it inline. Then return a 2xx immediately.

Idempotency by event id. Because retries exist, your consumer will eventually see a duplicate. Record each event id when you process it and skip ids you have already seen. A unique constraint on the event id in your job table is the simplest correct implementation.

Respond fast. Slow handlers get treated as failures, which triggers retries, which creates more duplicates and more load. Keep everything expensive (database writes to your CRM, follow up reads against the API, notifications) behind the queue. If your worker needs more context than the event carries, fetch it afterwards with a native read for 1 credit.

If you build the consumer in Python, the Python guide shows the request patterns; the webhook side is just a small Flask or FastAPI route that verifies and enqueues.

Where to go from here

The fastest path is to register one webhook on a test account, fire a test delivery, and watch it land. The quickstart gets you a key and a connected account; the free trial includes 1,000 credits with no card required, which is far more than enough since webhook writes cost 1 credit and tag writes are free. Full event details live on the webhooks page.

Ship your first webhook today

Start free with 1,000 credits, no card required. Register a webhook, fire a test delivery, and watch events land.

Start free with 1,000 credits · no card