PO

posthog-rate-limits

Covers PostHog rate limit tiers and implementation strategies for handling errors and throttling.

Install

mkdir -p .claude/skills/posthog-rate-limits && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4849" && unzip -o skill.zip -d .claude/skills/posthog-rate-limits && rm skill.zip

Installs to .claude/skills/posthog-rate-limits

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Handle PostHog API rate limits with exponential backoff, request queuing,
73 charsno explicit “when” trigger
Intermediate

Key capabilities

  • →Implement exponential backoff for 429 errors
  • →Queue requests to enforce rate limits
  • →Cache API responses to reduce frequency
  • →Monitor rate limit headers

How it works

Uses a request queue to enforce throughput limits and implements exponential backoff logic that respects the Retry-After header provided by the API.

Inputs & outputs

You give it
API request parameters
You get back
Throttled or cached API response

When to use posthog-rate-limits

  • →Handle API rate limit exceptions
  • →Implement exponential backoff for requests
  • →Monitor API throughput against usage limits
  • →Optimize request patterns for admin endpoints

About this skill

PostHog Rate Limits

Overview

PostHog rate limits apply to private API endpoints authenticated with a personal API key, project secret key where available, or OAuth. Public POST-only capture and flag endpoints have no PostHog request-level rate limit, but capture can still report billing limits in a successful response.

Prerequisites

  • A least-privilege personal API key, project secret key where available, or OAuth token for private endpoints
  • Understanding of which endpoints you call and how often
  • posthog-node or direct API usage

PostHog Rate Limit Tiers

Endpoint CategoryRate LimitExamples
Public event capture (/e, /i/v0/e, /batch/)No request-level limitSDK capture and batch ingestion
Public feature flags (/flags)No request-level limitClient-side flag evaluation
Analytics API (insights, persons, recordings)240/min, 1200/hourTrend queries, person lookup
Events values (/events/values)60/min, 300/hourEvent-property value lookup
Query API (/api/projects/:id/query/)2400/hourHogQL and structured queries
Feature flag local evaluation polling600/minServer SDK flag definition fetch
Other private CRUD endpoints480/min, 4800/hourFeature flag CRUD, cohorts, annotations

These budgets apply to the whole PostHog team, not to each key or process. Coordinate workers through a shared limiter when more than one caller uses the same endpoint class.

Instructions

Tool discipline

Use Read to inspect the relevant configuration and implementation before proposing changes. Use Write only for a new, explicitly requested artifact inside the target project. Use Edit for minimal changes to existing project files after the evidence pass.

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';

// Conservative analytics budget: 20/min averages to 1200/hour.
// A shared limiter is required when multiple workers use the same PostHog team.
const posthogQueue = new PQueue({
  concurrency: 2,       // Max parallel requests
  interval: 60_000,
  intervalCap: 20,
});

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://us.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://us.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

ErrorCauseSolution
HTTP 429 on insights>240 req/min on analyticsQueue requests, cache results
429 on flag polling>600 req/min local eval fetchIncrease featureFlagsPollingInterval
429 on query API>2400 req/hour across the teamCache query results, reduce frequency, or use an export product
429 on events values>60 req/min or >300 req/hourCache dimensions and avoid interactive polling
429 on other CRUD>480 req/min or >4800 req/hourCoordinate callers through a team-wide limiter
Thundering herd on retryAll clients retry simultaneouslyAdd random jitter to backoff

Key Points

  • Public capture and /flags have no PostHog request-level limit — still inspect successful capture responses for quota_limited and check ingestion warnings.
  • Private endpoints are limited by class — authentication may be a personal key, project secret key where available, or OAuth.
  • Limits are team-wide — separate keys do not create separate budgets.
  • 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
  • Endpoint-class request queue that respects both minute and hour budgets
  • In-memory cache for API responses
  • Rate limit header monitoring

Examples

For a scheduled HogQL query job, budget against the documented query limit, serialize retries from Retry-After, add jitter, and stop after a bounded attempt count. Do not apply private-API limits to public capture endpoints; inspect quota_limited and ingestion warnings separately.

Resources

See official PostHog references for current authority and verification boundaries.

Next Steps

For security configuration, see posthog-security-basics.

When not to use it

  • →Do not apply rate limiting to public capture endpoints
  • →Avoid retrying client errors other than 429

Prerequisites

Personal API key (phx_...)posthog-node or direct API access

Limitations

  • →Analytics API limited to 240 requests per minute
  • →HogQL query API limited to 1200 requests per hour

How it compares

This method explicitly handles 429 responses and enforces client-side throttling, whereas standard implementations may fail repeatedly during bursts.

Compared to similar skills

posthog-rate-limits side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
posthog-rate-limits (this skill)12moCautionIntermediate
mcp-builder1365moReviewAdvanced
copilot-sdk75moReviewIntermediate
openrouter-function-calling52moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore →

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

mcp-builder

anthropics

Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).

136215

copilot-sdk

github

Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.

763

openrouter-function-calling

jeremylongshore

Implement function/tool calling with OpenRouter models. Use when building agents or structured outputs. Trigger with phrases like 'openrouter functions', 'openrouter tools', 'openrouter agent', 'function calling'.

539

chatgpt-app-builder

mcp-use

Build ChatGPT apps with interactive widgets using mcp-use and OpenAI Apps SDK. Use when creating ChatGPT apps, building MCP servers with widgets, defining React widgets, working with Apps SDK, or when user mentions ChatGPT widgets, mcp-use widgets, or Apps SDK development.

535

create-mcp-app

modelcontextprotocol

This skill should be used when the user asks to "create an MCP App", "add a UI to an MCP tool", "build an interactive MCP View", "scaffold an MCP App", or needs guidance on MCP Apps SDK patterns, UI-resource registration, MCP App lifecycle, or host integration. Provides comprehensive guidance for building MCP Apps with interactive UIs.

331

windsurf-mcp-integration

jeremylongshore

Manage integrate MCP servers with Windsurf for extended capabilities. Activate when users mention "mcp integration", "model context protocol", "external tools", "mcp server", or "cascade tools". Handles MCP server configuration and integration. Use when working with windsurf mcp integration functionality. Trigger with phrases like "windsurf mcp integration", "windsurf integration", "windsurf".

14

Search skills

Search the agent skills registry