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.

Copied
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 the metric_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):

Copied
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):

Copied
HTTP/1.1 429 Too Many Requests
Retry-After: 2700
X-Sentry-Rate-Limits: 60::organization, 2700::organization

Example response (for metric buckets):

Copied
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:

  1. On every response, look for X-Sentry-Rate-Limits. If present, parse it and immediately go to Stage 2.
  2. On 429 responses, check for Retry-After. Treat it like categories=[] and go to Stage 2. It is allowed but not required to do this on other status codes.
  3. 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.
  4. 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 the Retry-After header to determine rate limit expiration.
  • SDKs must implement and obey limits for supported categories, for example error and transaction. 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 and reason_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 in 200 OK responses should be treated like on 429 responses. Sentry may choose to respond with 200 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_type csp, hpkp, expectct, expectstaple
    • attachment: Attachment bytes stored (unused for rate limiting)
    • session: Session update events
    • profile: Profiling events
    • replay: Session Replays
    • metric_bucket: Sentry Metrics sent via the statsd or metrics items. The namespace 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

  1. 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 handle internal 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 to internal event types as well.
You can edit this page on GitHub.