DEV Community

HaoPeng Zhang
HaoPeng Zhang

Posted on

I Stopped Scheduling My n8n Workflows on the Hour — My 429s Disappeared

I Stopped Scheduling My n8n Workflows on the Hour — My 429s Disappeared

Part of my build-in-public series on running LLM workflows in n8n in production. This week's incident was embarrassing in retrospect: three workflows, one API key, all firing at 09:00 sharp — and the API's rate limiter doing exactly what it was paid to do.

The morning three workflows agreed to DDoS me

Last Tuesday I woke up to a dead-letter queue with three entries, all timestamped within the same minute. Three of my production workflows — the content research sweep, the SEO draft generator, and the inbox triage run — had all failed at roughly 09:00. The error was identical across all three:

429 Too Many Requests
Enter fullscreen mode Exit fullscreen mode

Three workflows, one shared API key, all calling the same LLM endpoint at the top of the hour. The first one through got its tokens. The second one squeaked by. The third ate the rate limit. The API wasn't down. My scheduling was just rude.

The investigation took ten minutes and one execution log

n8n's execution list told the whole story at a glance: three executions, start times 09:00:03, 09:00:11, 09:00:19. All three opened with a burst of parallel LLM calls — five to eight requests each, fired as fast as the nodes could go. That's ~20 requests inside a single per-minute rate window, against a key capped well below that.

The API dashboard confirmed it: a vertical cliff of requests at 09:00, then silence. Classic thundering herd. I had built a small, self-inflicted DDoS and aimed it at my own API key.

Here's the uncomfortable part: I set every one of those schedules to :00 myself, on different days, for different reasons. 09:00 for the research sweep because "mornings." 09:00 for the SEO drafts because "mornings too." 09:00 for inbox triage because — you guessed it — "mornings." Every schedule was individually reasonable. Together they were a pileup I had carefully assembled one workflow at a time.

Why :00 is a trap

Scheduling on the hour is the cron equivalent of everyone leaving the stadium through the same exit. It's the default mental model: round numbers feel clean, and n8n's Schedule Trigger makes 0 9 * * * the path of least resistance. But APIs don't bill in hours — they rate-limit in minutes (sometimes seconds). Your mental model is hourly; their enforcement window is minutely. That mismatch is the whole bug.

And it's not just your own workflows colliding. Every other customer of that API also scheduled at :00. The provider's busiest minute of every hour is the top of the hour. Scheduling at 09:00 means competing with the entire world's 09:00. Scheduling at 09:07 means competing with almost nobody.

The fix, in three levels

Level 1: stagger by hand (five minutes of work). I moved the three schedules to 09:07, 09:23, and 09:41 in the n8n UI. No code, no nodes, no redeploy. Prime-ish numbers, deliberately un-round, each in a different quarter-hour. This alone killed the pileup: the three workflows' request bursts no longer share a rate window.

Level 2: jitter for anything bursty. For the research sweep — the one that fires 8 parallel LLM calls the moment it starts — I added a small Wait node at the head with a random delay of 0–90 seconds ({{ Math.floor(Math.random() * 90) }}s). Even if two workflows ever land in the same minute again, their bursts won't align to the second. Randomness is cheap insurance against my future self scheduling another :00.

Level 3: treat 429 as expected, not exceptional. Staggering reduces collisions; it doesn't eliminate them — APIs have bad days, and shared keys have other users. So the HTTP nodes now treat 429 like any transient failure: exponential backoff (2s → 8s → 32s), max three retries, then the dead-letter queue — not a red execution. I wrote about the retry budget discipline separately; the point here is that 429 handling belongs in the workflow's design, not in your incident response.

The honest numbers

Before: ~6–10 429 rejections per week across the three workflows, each one a dead-letter entry I had to manually re-run. After two weeks of staggered schedules: zero. Not "fewer" — zero. The manual re-run time went from ~20 minutes a week to nothing.

The cost of the fix was one settings change per workflow and one Wait node. The cost of not fixing it was a weekly ritual of re-running failed executions and quietly resenting an API that was behaving perfectly reasonably.

The one-line rule

Never schedule on the hour. Pick un-round minutes. Spread across the quarter-hours. Add jitter to anything that bursts. It is the cheapest reliability improvement in my entire n8n setup, and I learned it by aiming three workflows at the same minute and watching the rate limiter do its job.

If you're running multiple n8n workflows against one API key, go check your Schedule Triggers right now. I'd bet at least two of them say :00.


I'm building AI Automation Lab — production-ready n8n workflow templates. The free AI Hook Generator is where most people start; the AI Content Repurposing Engine ($29) is the paid pipeline I tore down last week. My open-source patterns live at github.com/zhp910318/n8n-ai-automation-lab.

Top comments (0)