posthog-rate-limits
Implement PostHog rate limiting, backoff, and idempotency patterns. Use when handling rate limit errors, implementing retry logic, or optimizing API request throughput for PostHog. Trigger with phrases like "posthog rate limit", "posthog throttling", "posthog 429", "posthog retry", "posthog backoff".
Install
mkdir -p .claude/skills/posthog-rate-limits && curl -L -o skill.zip "https://mcp.directory/api/skills/download/4849" && unzip -o skill.zip -d .claude/skills/posthog-rate-limits && rm skill.zipInstalls to .claude/skills/posthog-rate-limits
About this skill
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-nodeor 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++) {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}`,
...options.headers,
},
});
if (response.ok) {
return response.json();
}
if (response.status === 429) {
// Honor the Retry-After header from PostHog
const retryAfter = parseInt(response.headers.get('Retry-After') || '0');
const backoffMs = retryAfter > 0
? retryAfter * 1000
: Math.min(1000 * Math.pow(2, attempt) + Math.random() * 500, 32000);
console.warn(`PostHog 429: retrying in ${Math.round(backoffMs)}ms (attempt ${attempt + 1}/${maxRetries})`);
await new Promise(r => setTimeout(r, backoffMs));
continue;
}
// Don't retry client errors (except 429)
if (response.status >= 400 && response.status < 500) {
const body = await response.text();
throw new Error(`PostHog API ${response.status}: ${body}`);
}
// Retry server errors (500+)
if (attempt < maxRetries) {
const delay = 1000 * Math.pow(2, attempt);
await new Promise(r => setTimeout(r, delay));
continue;
}
throw new Error(`PostHog API failed after ${maxRetries} retries: ${response.status}`);
}
throw new Error('Unreachable');
}
Step 2: Request Queue for Burst Protection
import PQueue from 'p-queue';
// Enforce 240 requests/minute = 4 requests/second
const posthogQueue = new PQueue({
concurrency: 2, // Max parallel requests
interval: 1000, // Per second
intervalCap: 4, // Max 4 requests per second
});
async function queuedPostHogCall<T>(
url: string,
options: RequestInit
): Promise<T> {
return posthogQueue.add(() => postHogApiCall<T>(url, options));
}
// Usage: all calls are automatically throttled
const insights = await queuedPostHogCall(
`https://app.posthog.com/api/projects/${PROJECT_ID}/insights/trend/`,
{ method: 'GET' }
);
Step 3: Cache Frequently Accessed Data
// Cache insight results to reduce API calls
class PostHogCache {
private cache = new Map<string, { data: any; expiry: number }>();
async get<T>(key: string, fetcher: () => Promise<T>, ttlMs = 300000): Promise<T> {
const cached = this.cache.get(key);
if (cached && Date.now() < cached.expiry) {
return cached.data as T;
}
const data = await fetcher();
this.cache.set(key, { data, expiry: Date.now() + ttlMs });
return data;
}
invalidate(key: string) {
this.cache.delete(key);
}
}
const phCache = new PostHogCache();
// Cache trend data for 5 minutes
const trends = await phCache.get('weekly-pageviews', () =>
queuedPostHogCall(`https://app.posthog.com/api/projects/${PROJECT_ID}/insights/trend/?events=[{"id":"$pageview"}]&date_from=-7d`, { method: 'GET' })
);
Step 4: Monitor Rate Limit Headers
class RateLimitMonitor {
private remaining = Infinity;
private resetAt = 0;
update(headers: Headers) {
const remaining = headers.get('X-RateLimit-Remaining');
const reset = headers.get('X-RateLimit-Reset');
if (remaining) this.remaining = parseInt(remaining);
if (reset) this.resetAt = parseInt(reset) * 1000;
}
shouldThrottle(): boolean {
return this.remaining < 10 && Date.now() < this.resetAt;
}
waitTime(): number {
return Math.max(0, this.resetAt - Date.now());
}
log() {
console.log(`PostHog rate limit: ${this.remaining} remaining, resets in ${Math.round(this.waitTime() / 1000)}s`);
}
}
const rateLimits = new RateLimitMonitor();
// After each API call, update the monitor
const response = await fetch(url, options);
rateLimits.update(response.headers);
if (rateLimits.shouldThrottle()) {
console.warn(`Approaching PostHog rate limit — waiting ${rateLimits.waitTime()}ms`);
await new Promise(r => setTimeout(r, rateLimits.waitTime()));
}
Error Handling
| Error | Cause | Solution |
|---|---|---|
| HTTP 429 on insights | >240 req/min on analytics | Queue requests, cache results |
| 429 on flag polling | >600 req/min local eval fetch | Increase featureFlagsPollingInterval |
| 429 on HogQL | >1200 req/hour | Cache query results, reduce frequency |
| Thundering herd on retry | All clients retry simultaneously | Add random jitter to backoff |
Key Points
- Capture endpoints are NOT rate limited —
posthog.capture()calls will never 429 - Only private API calls are limited — endpoints requiring
Authorization: Bearer phx_... - Cache aggressively — insight data rarely needs real-time refresh
- Honor Retry-After — PostHog tells you exactly how long to wait
Output
- Exponential backoff with Retry-After header support
- Request queue enforcing 4 req/sec
- In-memory cache for API responses
- Rate limit header monitoring
Resources
Next Steps
For security configuration, see posthog-security-basics.
More by jeremylongshore
View all skills by jeremylongshore →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversUnlock seamless Figma to code: streamline Figma to HTML with Framelink MCP Server for fast, accurate design-to-code work
Access official Microsoft Docs instantly for up-to-date info. Integrates with ms word and ms word online for seamless wo
Integrate Feishu (Lark) for seamless document retrieval, messaging, and collaboration via TypeScript CLI or HTTP server
Reddit Buddy offers powerful Reddit API tools for browsing, searching, and data annotation with secure access, rate limi
Reddit Buddy offers clean access to Reddit API, advanced reddit tools, and seamless data annotation reddit with smart ca
Explore Magic UI, a React UI library offering structured component access, code suggestions, and installation guides for
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.