Appearance
Limits & Errors
Beta
Telemetry is in beta. It's included with paid plans at no extra cost while in beta, and limits are subject to change.
Plans
Telemetry is on paid plans. The Free plan doesn't include it: you can't create Streams, and data sent to a Free account's Streams gets 403 plan_required.
| Plan | Streams | Events per month | Retention | Requests per minute | Data per minute |
|---|---|---|---|---|---|
| Hobby | 2 | 1,000,000 | 7 days | 3,000 | 64 MB |
| Pro | 5 | 10,000,000 | 14 days | 12,000 | 256 MB |
| Team | 20 | 50,000,000 | 30 days | 30,000 | 1 GB |
| Founders | 5 | 10,000,000 | 14 days | 12,000 | 256 MB |
| Enterprise | Custom | Custom | Up to 90 days | Custom | Custom |
Events, requests and data are counted across every Stream in every namespace on the account, not per Stream.
Monthly events
Every stored record counts as one event. Each Stream's Overview shows the account's total for the month.
- At 80% and 100% we email the account owner, and the console shows a banner.
- At 110% ingest pauses until the 1st of next month (UTC): requests get
429 quota_exceeded. Upgrading lifts the pause within a minute, and your stored data is never affected.
Rate limits
Budgets are per rolling minute and apply across all our servers, so bursts are fine as long as the minute's total stays under the budget.
- Account budget: the plan's requests and data per minute (table above). Data is the request body as sent, so compressing stretches it further.
- Key limits (optional): cap a single ingest key below the account budget, for example a key shipped in browser code. Set them under Keys & Setup → Limits.
- Fair share: if ingest falls behind and our buffer passes half full, the Streams holding the most of it are slowed first (
429 buffer_fair_share), so one flood can't hold up everyone else.
A 429 always carries Retry-After, and tells you which limit you hit in the X-Latency-Limit header and the body's limit field:
limit | Meaning |
|---|---|
account_requests_per_minute | Over the plan's requests per minute |
account_bytes_per_minute | Over the plan's data per minute |
key_requests_per_minute | Over this key's own request limit |
key_bytes_per_minute | Over this key's own data limit |
buffer_fair_share | Ingest is catching up and this Stream holds more than its share |
monthly_events | The account is at 110% of its monthly events (quota_exceeded) |
Throttled requests show up in the Stream's Overview, under usage and Ingest Health.
Requests
| Limit | Value |
|---|---|
| Request body | 4 MB (compressed size, if you compress) |
| Decompressed body | 32 MB |
| Records per request | 100,000 |
Batching is the way to go: one request with 1,000 records costs you far less than 1,000 requests.
Records
| Limit | Value | Over the limit |
|---|---|---|
| Log message or event body | 1,048,576 characters | The excess is dropped |
| Attributes per record | 256 | Extra attributes are dropped |
| Attribute name | 256 characters | The excess is dropped |
| Attribute value | 16 KB | The excess is dropped |
| Nesting depth | 8 levels | Deeper objects are stored as JSON text |
Nothing is dropped silently. The record is still stored, and carries a latency.dropped attribute saying what was dropped (for example body:2000000>1048576 attrs:+56), so you can find them all with the search latency.dropped:*. Each Stream's Overview counts records with dropped data and shows why, with an example.
Timestamps
- More than 24 hours in the future: replaced with the time we received it.
- Older than the stream's retention: rejected, since it would be deleted straight away.
Responses
| Status | Meaning | What to do |
|---|---|---|
202 (HTTP API) or 200 (OpenTelemetry) | Stored | Nothing |
400 stream_type_mismatch | OpenTelemetry signal sent to the wrong kind of stream | Use a key from a stream of the right type |
401 | Key missing, wrong, disabled or revoked, or its Stream is deleted | Check the key in Manage Streams |
403 plan_required | The account's plan doesn't include telemetry | Upgrade the plan |
403 ingest_blocked | Latency has blocked this key, Stream or namespace; the body's reason says why | Contact feedback@latency.app |
403 (older URL) | Only with /v1/ingest/{stream}: the key belongs to a different Stream | Send to /v1/ingest |
413 | Body over 4 MB | Send smaller batches, or compress |
415 | Unsupported Content-Type or Content-Encoding | See the HTTP API or OpenTelemetry setup |
429 rate_limited | Over a rate limit | Retry after the Retry-After header |
429 quota_exceeded | The account is at 110% of its monthly events | Upgrade, or wait for next month |
429 ingest_backlog_full | Our buffer is full while ingest catches up | Retry after the Retry-After header |
503 | Ingest briefly unavailable | Retry after the Retry-After header |
429 and 503 are safe to retry. OpenTelemetry SDKs and the Collector do it automatically. For the HTTP API, add an Idempotency-Key so a retry never stores twice. A 403 won't succeed on retry, so SDKs drop the data instead.
Records we can't read
A 202 means the request was stored, before we parse it. If a record turns out to be unreadable (say, a metric with no name, or a timestamp older than the Stream's retention), that record is skipped and the rest of the request is kept. A body that can't be read at all (invalid JSON, say) is set aside whole.
Either way you can see it: the Stream's Overview lists every reason from the last 7 days with a count and an example, and counts rejected records in its usage.