| name | posthog-rate-limits |
| description | Handle PostHog API rate limits with exponential backoff, request queuing,
and understanding PostHog's actual limit tiers (240/min analytics, 600/min flags).
Trigger: "posthog rate limit", "posthog throttling", "posthog 429",
"posthog retry", "posthog backoff", "posthog too many requests".
|
| allowed-tools | Read, Write, Edit |
| version | 1.12.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","posthog","api"] |
| compatibility | Designed for Claude Code, also compatible with Codex and OpenClaw |
PostHog Rate Limits
Overview
PostHog rate limits apply to private API endpoints authenticated with a personal API key (phx_...). Public capture endpoints (/capture/, /batch/, /decide/) are not rate limited. Understanding which endpoints have limits is critical to avoiding 429 errors.
Prerequisites
- PostHog personal API key (
phx_...) for admin endpoints
- Understanding of which endpoints you call and how often
posthog-node or direct API usage
PostHog Rate Limit Tiers
| Endpoint Category | Rate Limit | Examples |
|---|
Event capture (/capture/, /batch/) | No limit | posthog.capture(), batch ingestion |
Feature flag decide (/decide/) | No limit | Client-side flag evaluation |
| Analytics API (insights, persons, recordings) | 240/min, 1200/hour | Trend queries, person lookup |
HogQL query API (/api/projects/:id/query/) | 1200/hour | Custom SQL queries |
| Feature flag local evaluation polling | 600/min | Server SDK flag definition fetch |
| All other private endpoints | 240/min, 1200/hour | Feature flag CRUD, cohorts, annotations |
Instructions
Step 1: Implement Exponential Backoff with Retry-After
async function postHogApiCall<T>(
url: string,
options: RequestInit,
maxRetries = 5
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
response = (url, {
...options,
: {
: ,
: ,
...options.,
},
});
(response.) {
response.();
}
(response. === ) {
retryAfter = (response..() || );
backoffMs = retryAfter >
? retryAfter *
: .( * .(, attempt) + .() * , );
.();
( (r, backoffMs));
;
}
(response. >= && response. < ) {
body = response.();
();
}
(attempt < maxRetries) {
delay = * .(, attempt);
( (r, delay));
;
}
();
}
();
}