OnlyFans PPV Automation
Putting a price on a message is one boolean and one integer. Doing it at scale without blowing a credit budget or burning the list is a system — and one of the few OnlyFans automation jobs with a complete set of endpoints behind it. This page is the second part: which price goes to which segment, how the send paces out, what you can measure afterwards, and where the API stops helping.
Three places a price can be set — and they disagree on field names
Every priced send reduces to a vault media id, an integer price in cents and an audience. The three endpoints that accept one spell those differently, which is the commonest source of a silently free blast.
| Endpoint | Price field | Media field | Audience | Also takes |
|---|---|---|---|---|
POST /accounts/{id}/mailings |
priceCents |
mediaIds |
listId |
lockedText |
POST /accounts/{id}/chats/{chatId}/messages |
price |
mediaFiles |
the chat id in the path | lockedText, previews |
POST /accounts/{id}/scheduled/messages |
price |
mediaFiles |
userIds and/or userLists |
scheduledAt |
All three default the price to 0, and 0 means a free message rather than an error — misname the field and your paid drop goes out as a giveaway with no way to recall it. lockedText: true hides the text as well as the media until the fan pays.
Only the one-to-one send accepts previews: a separate list of vault media ids shown as the visible thumbnail behind the lock. A mailing has no equivalent field, so a broadcast PPV shows whatever OnlyFans generates from the attachment. If the preview frame matters to conversion, the drop has to go out as individual sends — and that path is weighted at 2 credits a message against the mailing's 1 per recipient, so control over the thumbnail doubles the cost of the campaign.
Stage the asset once through the vault media API and reuse the id. Upload is the heaviest call in the API at 5 credits, capped at 50 MB and limited to jpeg, png, webp and mp4.
curl -X POST https://api.ofapis.com/api/public/v1/accounts/42/scheduled/messages \
-H "Authorization: Bearer ofapis_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: drop-2026-10-24-tier-a" \
-d '{"text":"8 minutes, unlocked below",
"mediaFiles":[5521],
"price":1499,
"userLists":["118"],
"scheduledAt":"2026-10-24T18:00:00Z"}'
Price tiers against segments
The API gives you segments and send mechanics, not a conversion rate, so treat each (segment, price) pair as an experiment you instrument yourself. A plausible split for one video:
| Segment | Fans | Price | Credits to send |
|---|---|---|---|
| Bought in the last 30 days | 180 | $24.99 | 180 |
| Active, never bought | 1,400 | $9.99 | 1,400 |
| Expired subscribers | 2,600 | $4.99 | 2,600 |
The right-hand column is the whole point: through a mailing the send costs 1 credit per recipient regardless of price, so a $24.99 drop to 180 fans and a free teaser to 180 fans cost the same 180 credits. Price has no cost side inside this API — the two levers that change what a campaign costs are how many fans are in the segment and which endpoint you send through (1 credit per recipient via a mailing, 2 per message one-to-one). That is why tiering is cheaper than one blast at one price, not just better targeted.
A fourth tier sits outside that table: fans who subscribed minutes ago. A priced first message is covered on welcome message automation, because detecting the subscription is the hard and expensive part there, not the send.
Build each tier with POST /accounts/{id}/lists and add members with POST /accounts/{id}/lists/{listId}/users and a {"userIds":[…]} body; removal is the only place a per-user path exists. When you enumerate a segment, note the lists endpoints default to a limit of 10 rather than the 20 used everywhere else — a loop written against the usual default looks mysteriously slow. The fan segments page covers the scoring side.
Preview is the only budget gate
For a mailing, POST /mailings/{id}/preview returns recipientCount and estimatedCost before anything is debited, for one credit. Gate the trigger on that number; the mass message tool page works through what a blast does to a monthly allowance. The PPV-specific trap is that preview returns -1 for a mailing with no listId, so an untargeted priced drop is the one send you cannot cost in advance. Always set a listId on a PPV mailing.
Pacing
Dispatch throttles to one message every 500 ms, so the audience does not receive a drop simultaneously — a 2,400-fan priced mailing lands over roughly 20 minutes, and the last fan sees it 20 minutes after the first. Any copy that says "next hour only" needs that spread built into it.
Splitting a drop into tranches by list buys a kill switch, not throughput: trigger the first segment, read the run, then decide whether to trigger the second. It does not make the send faster — a worker drives one run to completion before claiming the next.
For time-of-day control, the scheduled-message call above hands delivery to OnlyFans' own queue. scheduledAt must be an ISO-8601 instant in the future — a naive or past timestamp is rejected with a 400 before any upstream call, but not for free: the call's 3 credits are debited before the handler runs and a 400 is not refunded. Validate the timestamp on your side. There is no edit either: cancel with DELETE /accounts/{id}/scheduled/messages/{queueId} and create a replacement. Creating one costs 3 credits, deleting one costs 1. More in the scheduling API.
Measuring what a price did
Be honest about the ceiling here: there is no earnings, transactions or statistics endpoint, so nothing reports revenue per campaign. What you can assemble:
- The denominator. A mailing run record carries
recipientCount,successCountandskippedCount. Delivered equalssuccessCount, and that is the only number you should put under a conversion rate. - The numerator, approximately. Read the thread back with
GET /accounts/{id}/chats/{chatId}/messages. Each message returnsprice,isFree,lockedText,isOpenedandcanPurchase. For a PPV you sent,canPurchasedescribes whether that fan can still buy it — the closest thing to a purchase flag the API exposes. Treat it as an indicator and reconcile against OnlyFans statements before reporting revenue off it. - Sampling, not sweeping. Each thread read is one credit. Checking 200 of 2,400 threads gives a usable estimate; checking all of them costs as much as the blast did.
What you cannot do is rank fans by what they have spent. A subscriber record is {id, username, name, avatar, subscribedOn} with no spend field, and no per-fan read adds one, so a "whale" tier has to come from purchases you recorded yourself in a fan database of your own — see revenue analytics for what those records can honestly be totalled into.
Where to stop
Skipped recipients — blocked, unsubscribed, DMs closed — are refunded when the run completes, so the trigger debit is a ceiling. That refund is the only automatic protection; everything else is judgement.
Move fans who unlocked into a bought list and exclude them from the follow-up; a second identical paywall to someone who already paid reads as a mistake. A mailing sends one identical string with no merge fields, so a broadcast PPV cannot be personalised — if a message needs to look written, it needs the one-to-one endpoint. And the 500 ms throttle is not a limit to engineer around; it is what keeps a roster from looking like a bot farm. Field-level detail is on the PPV API page, allowances on pricing.
FAQ
Why is the price field called priceCents on a mailing but price elsewhere?
Because the mailing resource is modelled by ofapis while the chat and scheduled-message bodies mirror the upstream OnlyFans shape. Both are integer cents — 1499 is $14.99 — but the key differs, and sending price to the mailing endpoint does not fail: the unknown key is ignored and priceCents stays at its default of 0.
How do I tell which fans unlocked a PPV?
There is no purchase event and no revenue endpoint. The workable approach is to read the thread back and inspect canPurchase on the message you sent: a fan who can no longer purchase it is the best available signal that they bought. Sample a subset of threads rather than all of them, since each read costs a credit.
Can I change the price of a queued PPV?
No. Neither scheduled messages nor mailings expose an update verb, so the only route is to delete the queued item or the mailing and create a replacement with the new price. Deleting a mailing does not stop a run that has already started dispatching.
Should a priced drop go to the whole list?
Rarely. The send costs the same per recipient whatever the price, so the cost of a bad blast is fixed while the damage to the list is not — an expired-subscriber tier at $4.99 and a recent-buyer tier at $24.99 are two cheap experiments where one untargeted blast is a single expensive guess.
What does a PPV automation cost to run per campaign?
One media upload at 5 credits, one preview at 1, the per-recipient debit for the blast itself, and whatever sampling you do afterwards at 1 credit a thread. For a 1,500-fan segment with a 200-thread sample, that is about 1,706 credits before refunds for skipped recipients.
The next step that follows from this page: stage one asset, build one 150-fan tier, preview it, trigger it, then sample 30 threads for canPurchase. That rehearsal runs about 190 credits — inside the free tier's 250 — and it tells you whether the measurement side is good enough for your reporting before you pay for volume. Create an API key · what a blast does to a monthly allowance · campaign runner patterns