Pacing posts like a human: the rate rules behind a Pinterest scheduler
I build PinBoostr, a Pinterest scheduler for bloggers. The hard part was never calling Pinterest's API. It was deciding how many pins to send, when, and when to back off. A scheduler that posts too fast looks like a bot;
I build PinBoostr, a Pinterest scheduler for bloggers. The hard part was never calling Pinterest's API. It was deciding how many pins to send, when, and when to back off. A scheduler that posts too fast looks like a bot; one that panics and posts too little starves the account.
Here are the rules we ended up with, the bugs that taught us each one, and the numbers we use. Everything below runs in plain PHP on a cron-style tick, with a small Node worker doing the actual API calls.
Rule 1: never jump, climb at most +2 a day
A new account, or one coming back from a pause, ramps up slowly. The daily target can rise by at most 2 per calendar day.
Our first version applied "+2" per call. The volume function runs every minute, so an account could climb from 4 to 10 pins a day in a single afternoon. The fix was to measure from where the day started, not from the last call:
// The +2 step is per calendar day: measured from where today started, not from the last call.
$day_base = min($start_of_day_value, $last_value);
if (! $risk && $day_base > 0 && $max > $day_base + 2) {
$max = $day_base + 2;
}
Lesson: any "per step" limit inside a function that runs on a timer needs an explicit time unit.
Rule 2: a velocity cap based on what the account actually did
The second guard ignores settings and looks at history: the daily target can't exceed 1.5x the average of the last 14 days, plus one, with a floor of 3.
function velocity_cap(int $posted_last_14_days): int {
return max(3, (int) ceil(($posted_last_14_days / 14) * 1.5) + 1);
}
If someone sets "20 a day" on a week-old account, the cap still lets it grow only as fast as its real posting history. An account that has posted 28 pins in two weeks (2 a day) gets a cap of 4, not 20.
Rule 3: per-link limits, checked before you spend money
Pinterest favors fresh pins, and the same URL pinned again and again reads as spam. So each link has caps: at most one pin per link per day, plus 7-day and 30-day limits, with a minimum gap before the same post comes back.
The bug here was ordering. We generated the pin (AI copy, sometimes an AI image) and only then checked the link caps, so we paid for drafts we were going to throw away. Now the guard runs on the queue before any AI call, and it counts earlier copies of the same link inside the same batch, not only pins already posted.
Rule 4: when reach collapses, slow down, don't speed up
This one came from our own recipe account. Reach dropped hard in early September, and our first health logic cut posting to 25% "to be safe". That starved the account of fresh pins, kept it looking unhealthy, and the throttle never lifted. A trap.
The replacement is a recovery mode with explicit thresholds:
const RECOVERY_RATIO = 0.35; // last 7 days below 35% of the best 14 days
const RECOVERY_MIN_PEAK = 60; // ignore tiny accounts: the peak must average 60+ impressions/day
const RECOVERY_MAX_PINS = 3; // while recovering: at most 3 pins a day, own photos only
It switches itself off when reach comes back. Two details mattered:
- Skip the last 2 days when measuring. Pinterest's analytics for the most recent days are still filling in, so including them made every account look like it was crashing.
- Compare against 1 to 3 months ago, not last week. Week-over-week comparison called our collapsed account "stable" a month later, because it was stable: stably low.
The rule that isn't a rate limit: a human approves every pin
None of the above decides what gets posted. A person does. Pinterest's developer guidelines say that if an app schedules Pin publishing, "the end user must choose each Pin to be published", so every pin waits for approval, and the pacing rules only decide when approved pins go out.
To keep that from becoming a chore, the system drafts about a week ahead, so people approve a week in one sitting. Unapproved pins whose time passed roll forward to the next open slot instead of expiring, and drafting stops once a week's worth is waiting.
What I'd tell anyone building a scheduler
- Put time units on every limit that runs inside a loop or a timer.
- Base caps on observed history, not on what the user typed into a settings box.
- Run cheap checks before expensive ones (link caps before AI generation).
- Make throttles self-releasing, or they become traps.
- Exclude incomplete data at the edges of your measurement window.
If you're curious how it fits together in the product, here's how the PinBoostr scheduler works. And if you've built rate logic for another platform, I'd like to hear which rule bit you hardest.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.