Rate Limiting
Rate limits are communicated to SDKs via status codes and response headers. For regular rate limit responses, we emit a 429
status code and specify a Retry-After
header.
In addition to Retry-After
, Sentry (Relay) also includes a special response header X-Sentry-Rate-Limits
containing a detailed list of all rate limits that apply per data category.
X-Sentry-Rate-Limits: <quota_limit>, <quota_limit>, ...
Each quota_limit has the form retry_after:categories:scope:reason_code:namespaces:...
with the following parameters:
retry_after
: Number of seconds (as an integer or a floating point number) until this rate limit expires.categories
: Semicolon-separated list of data categories. If empty, this limit applies to all categories. While these categories might look similar to the envelope item types, they are not identical, and have slight differences.scope
: The scope that this limit applies to. Can be ignored by SDKs.reason_code
: A unique identifier for the quota hinting at the rate limiting reason. Can be ignored by SDKs.namespaces
: Semicolon-separated list of metric namespace identifiers. This will only be present if the rate limit applies to themetric_bucket
data category. If the namespace is not present, the backoff applies to all metrics.- More parameters can be added in the future, and can be ignored by SDKs (promise of backward compatibility).
The header may contain spaces which must be ignored. Relay will only emit spaces after ,
to separate quota_limits.
Example response (formatted for readability):
HTTP/1.1 429 Too Many Requests
Retry-After: 2700
X-Sentry-Rate-Limits:
60:transaction:key,
2700:default;error;security:organization
Example response (with empty categories list):
HTTP/1.1 429 Too Many Requests
Retry-After: 2700
X-Sentry-Rate-Limits: 60::organization, 2700::organization
Example response (for metric buckets):
HTTP/1.1 429 Too Many Requests
Retry-After: 2700
X-Sentry-Rate-Limits: 2700:metric_bucket:organization:quota_exceeded:custom
The X-Sentry-Rate-Limits
header may appear in any response, regardless of status code, even 200
responses. This is to proactively inform SDKs that certain payload types are disabled before SDKs even try to send them.
Rate Limit Enforcement
SDKs are expected to honor 429 responses and rate limits communicated from Sentry by stopping data transmission until the rate limit has expired. Events, transactions, sessions, and/or any other supported payload type, depending on the communicated limits, are to be discarded during the period that the rate limit is in place.
Stage 1: Parse Response Headers
To detect rate limits in a response from Sentry, apply the following steps:
- On every response, look for
X-Sentry-Rate-Limits
. If present, parse it and immediately go to Stage 2. - On
429
responses, check forRetry-After
. Treat it likecategories=[]
and go to Stage 2. It is allowed but not required to do this on other status codes. - On
429
responses without the above headers, assume a 60s rate limit for all categories and go to Stage 2. See the Java implementation for reference. - Otherwise, there are no rate limits communicated by Sentry.
Stage 2: Determine Rate Limits
Guidelines for how SDKs should determine the current rate limits:
- Rate limits must be tracked per each DSN, what corresponds to storing a rate limits map in an instance of the SDK transport/client.
- SDKs that only support one payload type can ignore
X-Sentry-Rate-Limits
and use theRetry-After
header to determine rate limit expiration. - SDKs must implement and obey limits for supported categories, for example
error
andtransaction
. For each category, they should maintain a separate limit, and another separate limit for the implicit "all" category. - SDKs must ignore dimensions they do not support or are irrelevant, for example
scope
andreason_code
. - Categories and scopes that are unknown to the client should be ignored. The limit still applies to the known categories. Clients should expect that more will be added in the future.
- More broadly, unknown dimensions must be ignored, that is, all additional colons and text to the right of the last parsed/interpreted dimension.
- Limits where all categories are unknown must be ignored since those limits apply to data the SDK cannot send. Do not apply unknown categories to a default category. Note that this is distinct from limits with no category, which implicitly apply to all categories (think of this as a special category).
- Always keep the maximum rate limit if multiple rate limits reference the same category. If a new rate limit is shorter than an already stored rate limit, then keep the longer one.
X-Sentry-Rate-Limits
returned in200 OK
responses should be treated like on 429 responses. Sentry may choose to respond with200 OK
regardless of a rate limit or may choose to inform a client proactively about a rate limit that is unrelated to the current request. This happens specifically for reject-all quotas to prevent clients from sending requests.
Definitions
As stated earlier, SDKs can ignore the scope
dimension. These definitions are here as a suplement to explain what the X-Sentry-Rate-Limits
header is made of.
- Category: Classifies the type of data that is being counted. Arbitrary categories can be added as long as they can be inferred from the event or data being ingested.
While these data categories might look similar to the envelope item types, they are not identical, and have slight differences.
default
: Events with an event_type not listed explicitly below.error
: Error events.transaction
: Transaction type events.security
: Events with event_typecsp
,hpkp
,expectct
,expectstaple
attachment
: Attachment bytes stored (unused for rate limiting)session
: Session update eventsprofile
: Profiling eventsreplay
: Session Replaysmetric_bucket
: Sentry Metrics sent via thestatsd
ormetrics
items. Thenamespace
component of the quota_limit defines which namespace is affected.internal
: a sentry/system internal eventinternal
- Scope: The unit / model in Sentry that quotas are enforced for.
organization
project
key
- Special note on the internal data category: this data category exists but is promised not to be emitted as we do not enforce
rate limits on internal items. This might appear odd but the purpose of this is to allow envelope implementations in SDKs to
assign a data category to internal items such as
client_report
. SDKs can handleinternal
like any other data category but they can rely on the fact that rate limits are not communicated for these explicitly. That said, the special all category does apply tointernal
event types as well.↩