Sep 3, 2026 · 8 min read
Designing reporting around Klaviyo's API rate limits
This is the one article in this series written from the inside of a real integration, not from the outside looking in — we run this pipeline in production, against real agency accounts, every day.
Where the limits actually bite
Klaviyo's flow reporting endpoints — flow-values-reports and flow-series-reports — carry the tightest limits in the entire API: 1 request per second, 2 per minute, and 225 per day, per account (see Klaviyo's rate limits documentation). That's far stricter than most of Klaviyo's other endpoints, and it's the ceiling that decides how any reporting tool has to be built, not an afterthought to work around later.
The fan-out that kills you
The naive way to build a "simple" cross-account dashboard is to loop: for each connected account, for each flow, for each period you want to show — call the reporting endpoint. Ten accounts, twelve flows each, comparing three periods, is 360 calls. Against a 225-per-day ceiling, that single dashboard refresh has already burned more than a day's entire budget for one account, and this was supposed to be the simple version.
Inventory → aggregate → shortlist → enrich
The pattern that actually respects the limit has four steps, in order:
- Inventory, paginated. List what exists — accounts, flows — using cheap, high-limit endpoints, never the reporting ones.
- One aggregated request. Pull the reporting data in as few calls as the API allows, covering the full flow list at once rather than one call per flow.
- Shortlist locally. Filter and rank in your own code, against data you already have in memory — this costs nothing against the API.
- Enrich only the shortlist. If a specific flow needs a deeper follow-up call, make it only for the handful that made the shortlist, never for the full inventory.
The expensive step happens once per account per scan, not once per flow per period — that's the entire difference between a design that scales past a handful of clients and one that doesn't.
Budget before you call, not after you fail
Reacting to a 429 (rate-limited) response is too late when the ceiling is a daily count — by the time a request fails, the day's budget may already be gone for hours. The alternative is a persistent ledger, per account, checked before every reporting call: how many requests has this account used today, is there room for one more, and if not, stop before making the call at all rather than after it's rejected.
Retries that a user cannot weaponise
A "refresh" button that lets a user trigger a fresh reporting call on demand is a direct path to burning the daily budget on repeated clicks. Whatever the ledger tracks, a manual retry has to check and consume from the same budget as an automated scan — a user retry is not a special case that gets to skip the queue.
Snapshots instead of live queries
The last piece is accepting that most reporting doesn't need a live query at all. A monthly report doesn't need up-to-the-second data — a snapshot taken during a scheduled sync, stored, and served from there for every subsequent view, satisfies the actual freshness requirement while making the expensive API call exactly once per scan rather than once per page view.
Inteleve connects your clients' Klaviyo accounts read-only, finds flow periods that stand out, and turns them into a shareable proof page — with the period, the metric and the source on the page, and no causal claim anywhere.
Related: Managing multiple Klaviyo accounts · Which Klaviyo flow metrics actually matter · How Inteleve works