Rate Limits

Himetrica applies rate limits at multiple layers to protect the service and ensure fair usage for all projects. This page covers every limit you may encounter.

Overview

Rate limits are enforced using a sliding window counter backed by Redis. There are three types of limits:

  • Request rate limits — Per API key or per IP, applied to each endpoint
  • Per-visitor limits — Prevents a single visitor from flooding custom events
  • Plan limits — Monthly quotas for page views and custom events based on your subscription

All limits fail open — if the rate limiting infrastructure is temporarily unavailable, requests are allowed through rather than blocked.

Server API Limits

These limits apply per secret key (hm_sk_...) to the server-to-server API endpoints.

EndpointLimitWindow
POST /track1,000 requests1 minute
POST /identify500 requests1 minute
GET /user-id100 requests1 minute
/revenue/*500 requests1 minute

Client-Side Tracking Limits

The browser tracker scripts are rate-limited both per API key and per visitor IP address.

EndpointPer API keyPer IP
Event tracking (global)10,000 / min
Page views10 / min
Custom events30 / min
Web Vitals1,000 / min20 / min
Error tracking1,000 / min20 / min
Beacon (unload)10 / min
Identify5 / min

Per-Visitor Limits

In addition to the per-IP limits above, custom events have a per-visitor rate limit to prevent a single visitor from flooding your event quota:

LimitDetails
10 events / minPer visitor per project. Events beyond this are silently dropped (the client receives a 202 to prevent retry loops).

This limit protects against misbehaving scripts or bots that fire events in tight loops (e.g. ad impressions, scroll events). Legitimate visitor interactions rarely exceed 10 custom events per minute.

Silent drops

Dropped events return 202 Accepted instead of 429 to prevent the client from retrying and amplifying the problem.

Plan Limits

Each plan has monthly quotas for page views and custom events. These are tracked per organization and reset at the start of each billing cycle.

PlanPage views / moCustom events / mo
Free30,000150,000
Starter200,0001,000,000
Pro500,0002,500,000
Business2,000,00010,000,000
Scale10,000,00050,000,000

A small grace period is applied before hard-blocking requests. You'll receive an email notification when you reach 100% of your quota. Requests are blocked at 115% of quota (200% for Business and Scale plans).

Response Headers

When a rate limit is exceeded, the API returns 429 Too Many Requests with the following headers:

HeaderDescription
Retry-AfterSeconds to wait before retrying
X-RateLimit-LimitYour request limit for the current window
X-RateLimit-RemainingRemaining requests in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Project Suspension

If a project accumulates more than 50 dropped events within a 5-minute window (from per-visitor rate limiting), all custom events for that project are temporarily suspended.

SuspensionDuration
First suspension5 minutes
Repeat suspension (within 1 hour)15 minutes

During suspension, all custom event requests return 202 without being processed. Page views, vitals, and error tracking are not affected. After the suspension expires with no further abuse, the counter resets.

Common causes

Suspensions are typically caused by misbehaving third-party scripts, infinite loops in event handlers, or bot traffic. Check your event names in the dashboard to identify the source.

Handling 429 Responses

When using the Server API, implement exponential backoff with the Retry-After header:

typescript
async function trackWithBackoff(event, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch("https://app.himetrica.com/api/v1/track", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.HIMETRICA_SECRET_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(event),
    });

    if (res.status !== 429) return res.json();

    const retryAfter = res.headers.get("Retry-After");
    const waitMs = (retryAfter ? parseInt(retryAfter) : Math.pow(2, i)) * 1000;
    await new Promise((r) => setTimeout(r, waitMs));
  }

  throw new Error("Rate limited after retries");
}

Batch when possible

If you're sending many events from a webhook handler or background job, consider batching them with a short delay between requests to stay within limits.
Himetrica - Analytics That Actually Matter