Claude
Skills
Sign in
Back

sentry-rate-limits

Included with Lifetime
$97 forever

Manage Sentry rate limits, quotas, and event volume optimization. Use when hitting 429 errors, tuning sampleRate/tracesSampleRate, filtering noisy browser errors with beforeSend, configuring inbound data filters, setting per-key rate limits, or monitoring quota usage via the Sentry stats API. Trigger: "sentry rate limit", "sentry quota", "reduce sentry events", "sentry 429", "sentry spike protection", "sentry sampling".

Backend & APIssaassentrycost-optimizationrate-limitingquotasobservability

What this skill does

# Sentry Rate Limits & Quota Optimization

## Overview

Manage Sentry rate limits, sampling strategies, and quota usage to control costs without losing visibility into critical errors. Covers client-side sampling, `beforeSend` filtering, server-side inbound filters, per-key rate limits, spike protection, and the usage stats API.

## Prerequisites

- Sentry account with a project DSN configured
- `SENTRY_AUTH_TOKEN` with `org:read` and `project:write` scopes (Settings > Auth Tokens)
- `SENTRY_ORG` and `SENTRY_PROJECT` slugs known
- SDK installed: `@sentry/node` (npm) or `sentry-sdk` (pip)
- Current event volume visible at `sentry.io/stats/`

## Instructions

### Step 1 — Understand Rate Limit Behavior

When your project exceeds its quota, Sentry returns `429 Too Many Requests` with a `Retry-After` header. The SDK automatically stops sending events until the cooldown expires. Events generated during this window are permanently lost — there is no replay mechanism.

**Rate limit tiers by plan:**

| Plan | API Rate Limit | Notes |
|------|---------------|-------|
| Developer | 50 RPM | Shared quota, no reserved volume |
| Team | 1,000 RPM | Per-organization, includes spike protection |
| Business | 10,000 RPM | Per-organization, custom quotas available |
| Enterprise | Custom | Negotiated per contract |

**Quota categories (billed separately):**

- **Errors** — exceptions and log messages
- **Transactions** — performance monitoring spans
- **Replays** — session replay recordings
- **Attachments** — file uploads (crash dumps, minidumps)
- **Profiles** — continuous profiling data
- **Cron monitors** — scheduled job check-ins

Rate limit headers returned on 429:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-Sentry-Rate-Limit-Limit: 50
X-Sentry-Rate-Limit-Remaining: 0
X-Sentry-Rate-Limit-Reset: 1711324800
```

### Step 2 — Configure Client-Side Sampling

Sampling is the first line of defense. Set `sampleRate` for errors and `tracesSampleRate` for performance transactions.

**TypeScript / Node.js:**

```typescript
import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,

  // Error sampling: 0.0 (drop all) to 1.0 (capture all)
  sampleRate: 0.25, // Capture 25% of errors

  // Transaction sampling: 0.0 to 1.0
  tracesSampleRate: 0.1, // Capture 10% of transactions

  // Dynamic transaction sampling — route-aware cost control
  tracesSampler: (samplingContext) => {
    const { name, parentSampled } = samplingContext;

    // Respect parent sampling decision in distributed traces
    if (parentSampled !== undefined) return parentSampled;

    // Drop health checks and readiness probes entirely
    if (name === 'GET /health' || name === 'GET /readiness') return 0;
    if (name?.includes('/health')) return 0;

    // High-value: payment and auth flows at 100%
    if (name?.includes('/api/payment') || name?.includes('/api/auth')) return 1.0;

    // Medium-value: API routes at 20%
    if (name?.startsWith('GET /api/') || name?.startsWith('POST /api/')) return 0.2;

    // Low-value: static assets — never trace
    if (name?.startsWith('GET /static/') || name?.startsWith('GET /assets/')) return 0;

    // Default fallback: 5%
    return 0.05;
  },
});
```

**Python:**

```python
import sentry_sdk

def traces_sampler(sampling_context):
    tx_name = sampling_context.get("transaction_context", {}).get("name", "")

    # Drop health checks
    if "/health" in tx_name or "/readiness" in tx_name:
        return 0

    # High-value flows
    if "/api/payment" in tx_name or "/api/auth" in tx_name:
        return 1.0

    # API routes
    if tx_name.startswith(("GET /api/", "POST /api/")):
        return 0.2

    # Static assets
    if tx_name.startswith(("GET /static/", "GET /assets/")):
        return 0

    return 0.05

sentry_sdk.init(
    dsn=os.environ["SENTRY_DSN"],
    sample_rate=0.25,          # 25% of errors
    traces_sample_rate=0.1,    # 10% of transactions (fallback if no sampler)
    traces_sampler=traces_sampler,
)
```

### Step 3 — Filter Noisy Errors with beforeSend

Use `beforeSend` to drop events before they count against your quota. This runs client-side, so filtered events never reach Sentry.

**TypeScript / Node.js:**

```typescript
Sentry.init({
  dsn: process.env.SENTRY_DSN,

  beforeSend(event, hint) {
    const error = hint?.originalException as Error | undefined;

    // Drop browser extension errors (common in frontend SDKs)
    if (event.exception?.values?.some(e =>
      e.stacktrace?.frames?.some(f =>
        f.filename?.includes('extensions://') ||
        f.filename?.includes('moz-extension://') ||
        f.filename?.includes('chrome-extension://')
      )
    )) {
      return null; // Drop the event
    }

    // Drop known noisy browser errors
    if (error?.message?.match(/ResizeObserver loop/)) return null;
    if (error?.message?.match(/Non-Error promise rejection/)) return null;
    if (error?.name === 'AbortError') return null;
    if (error?.message?.match(/Load failed/)) return null;

    // CRITICAL: Always capture payment errors regardless of sampleRate
    if (error?.message?.includes('PaymentError') ||
        event.tags?.['transaction.type'] === 'payment') {
      return event; // Force capture
    }

    return event;
  },

  // Pattern-based error filtering (faster than beforeSend for known strings)
  ignoreErrors: [
    'ResizeObserver loop completed with undelivered notifications',
    'Non-Error promise rejection captured',
    /Loading chunk \d+ failed/,
    'Network request failed',
    'Failed to fetch',
    'AbortError',
    /^Script error\.?$/,
    'TypeError: cancelled',
    'TypeError: NetworkError when attempting to fetch resource',
  ],

  // Block errors originating from third-party scripts
  denyUrls: [
    /extensions\//i,
    /^chrome:\/\//i,
    /^chrome-extension:\/\//i,
    /^moz-extension:\/\//i,
    /hotjar\.com/,
    /google-analytics\.com/,
    /googletagmanager\.com/,
    /intercom\.io/,
  ],
});
```

**Python:**

```python
def before_send(event, hint):
    if "exc_info" in hint:
        exc_type, exc_value, _ = hint["exc_info"]

        # Drop known noisy exceptions
        if exc_type.__name__ in ("ConnectionResetError", "BrokenPipeError"):
            return None

        # Drop health check 404s
        msg = str(exc_value)
        if "health" in msg.lower() and "404" in msg:
            return None

    # Always capture payment errors
    if event.get("tags", {}).get("transaction.type") == "payment":
        return event

    return event

sentry_sdk.init(
    dsn=os.environ["SENTRY_DSN"],
    before_send=before_send,
    ignore_errors=[
        "ConnectionResetError",
        "BrokenPipeError",
    ],
)
```

### Step 4 — Enable Server-Side Inbound Data Filters

Inbound filters run on Sentry's servers before quota counting. Filtered events do not consume quota — this is free filtering.

Configure at **Project Settings > Inbound Filters**:

| Filter | What it blocks | Recommended |
|--------|---------------|-------------|
| Legacy browsers | IE 9/10, old Safari, old Android | Enable |
| Browser extensions | Errors from browser extension code | Enable |
| Localhost events | Events from localhost / 127.0.0.1 | Enable for production projects |
| Web crawlers | Bot-generated errors (Googlebot, etc.) | Enable |
| Filtered releases | Specific release versions | Use for deprecated releases |
| Error message patterns | Custom regex patterns | Add known false-positive patterns |

**Configure via API:**

```bash
# Enable legacy browser filter
curl -X PUT \
  -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"active": true}' \
  "https://sentry.io/api/0/projects/$SENTRY_ORG/$SENTRY_PROJECT/filters/legacy-browsers/"

# Enable browser extension filter
curl -X PUT \
  -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"active": true}' \
  "https://sentry.io/api/0/projects/$SENTRY_OR

Related in Backend & APIs