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.
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:
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:
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:
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:
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.
# Set rate limit to 1000 events per hour on a specific client key
# First, list client keys to find the key ID
curl -s -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
"https://sentry.io/api/0/projects/$SENTRY_ORG/$SENTRY_PROJECT/keys/" \
| python3 -m json.tool
# Then set the rate limit
curl -X PUT \
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rateLimit": {"window": 3600, "count": 1000}}' \
"https://sentry.io/api/0/projects/$SENTRY_ORG/$SENTRY_PROJECT/keys/$KEY_ID/"
Strategy for multi-environment setups:
Production DSN: 5,000 events/hour (critical errors matter)
Staging DSN: 500 events/hour (only need representative sample)
Development DSN: 100 events/hour (prevent local debugging floods)
Step 6 — Enable Spike Protection
Spike protection is auto-enabled on Team and Business plans. It detects sudden event volume increases and temporarily rate-limits the project to prevent quota exhaustion from error storms.
Configure at Organization Settings > Spike Protection.
When spike protection triggers:
Sentry detects volume exceeding 10x normal baseline
Events are temporarily dropped (429 returned to SDK)
An email notification is sent to organization owners
Protection auto-disables after the spike subsides
For programmatic spike alerts, set up a Sentry alert rule:
Condition: Number of events in project exceeds threshold
Action: Send notification to Slack/PagerDuty/email