# Plans, limits, and retention

> Understand Anectico's planned usage meters and history windows during early access.

Canonical page: https://anectico.com/docs/reference/limits/


Anectico measures usage in units a developer can connect to application behavior. Seats are not a usage
meter.

Anectico is in early access. The table below describes the planned self-serve tiers shown on the pricing
page; an early-access agreement or dashboard may show different limits. **Settings → Account** is the
source of truth for an active organization.

## Planned self-serve limits

| Plan | Pooled events / month | Active metric series | Replay sessions / month | Retention | Hosted investigations | Daily AI spend cap | Daily decision-scoring cap |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| Free | 1 million | 1,000 | 1,000 | 7 days | — | — | $0.10 / day |
| Solo | 20 million | 10,000 | 10,000 | 30 days | — | — | $0.50 / day |
| Pro | 150 million | 50,000 | 50,000 | 90 days | — | — | $2 / day |
| Scale | 500 million | 100,000 | 250,000 | 180 days | 2,000 / month | $50 / day | $10 / day |

The daily AI spend cap is a brake on runaway model spend, not the allowance: the monthly count
is the allowance. When either is used up, an investigation is refused with `429` and a message
that names the limit and when it resets.

The daily decision-scoring cap bounds the small typed judgments Anectico makes on your behalf on
every plan. Today there are three made on your behalf — [ranking anomaly findings](/docs/investigate/anomalies),
ordering the evidence a [Customer Detective](/docs/investigate/customer-detective) investigation
reads when it has more than fits, and suggesting a category and an attention marker for the repeated
[log templates](/docs/investigate/search-telemetry) you look at — plus any
[decision-score evaluators](/docs/reference/evaluators) you configure. One judgment costs a fraction of a
cent, so your plan's event and series meters are the real allowance and this cap only stops a
runaway. When it is reached nothing is refused: findings simply stay unranked, investigations trim
evidence in their fixed order, log templates are listed without labels, and decision-score
evaluations wait to be retried rather than being scored, until the cap resets at
midnight UTC — everything else works exactly as before. The same holds whenever the scoring model is
unavailable.

Current prices and included product capabilities are listed on the [pricing page](/pricing).

An agent-run investigation can make **one sandboxed replay attempt**. The limit is per saved
investigation, including its follow-up turns, and does not reset when a client reconnects or the
investigation moves between processes. Asking again produces an explicit replay-limit refusal in
the answer. Agent-run investigations that do not request replay consume no replay attempt.

## What counts as an event

One log line, trace span, error event, or diagnostic event counts as one pooled event. The event types
share the same pool.

Metrics use active series because their cost depends on the number of distinct label combinations,
not only the number of data points. At the active-series limit, existing series continue to ingest;
only unseen metric names or label combinations are rejected. This rejection is non-retryable.
The meter is organization-wide and its background snapshot may lag by a few minutes. Replays count
recording sessions that started in the period.

### Which month usage lands in

Usage is counted in the period we **received** it, not the timestamp on the data. If your SDK
buffers events offline and sends them later — a mobile app reconnecting after a flight, say — those
events are billed in the month they reach us, even though they keep their original timestamps
everywhere you read them. Backdating a timestamp therefore does not move usage into an earlier
period, and it never removes usage from the current one.

## Projects

Each plan allows a fixed number of projects: **5** on Free, **10** on Solo, **50** on Pro and
**1000** on Scale. Creating one beyond the limit is refused; upgrade to raise it.

Downgrading never deletes a project. If a downgrade leaves you above the new limit, everything keeps
working and you simply cannot create another until you are back under it.

### Deleting data changes one meter, not the other

Deleting a metric frees active-series capacity. Active series measures what you are currently
tracking, so a metric you delete stops counting against the limit once the next background snapshot
runs; that is why removing an accidental high-cardinality metric is the remedy when you are near the
cap. The freed capacity is real capacity: new series are admitted against it from that same
snapshot, not only at the start of the next monthly period. A series reserved immediately before a
snapshot can be held through one additional snapshot interval so an in-flight data point from
another project is not mistaken for deleted data.

A deleted metric that your application keeps sending stays deleted, and stays free. Its data points
are still hidden from every query, dashboard, and alert, and they do not count toward your active
series or take capacity from a new one — so you do not have to redeploy before the capacity comes
back. Deletion suppression is project-scoped: the same series sent by a different project remains
active and is admitted normally. If you want the deleted project's metric again, send it under a
name you have not deleted.

Deleting logs, spans, errors, or product events does **not** refund pooled events. That meter counts
what was ingested during the period, and the ingestion already happened, so a deletion cannot return
it. Delete for privacy, correction, or hygiene — not to recover pooled-event capacity. Replay
sessions behave the same way.

## Retention

Retention is a rolling history window. Data outside the plan's window is no longer available to
search, timelines, API reads, MCP tools, or Customer Detective.

The window is organization-wide. Every project inherits the organization's current plan or
administrative override; projects cannot advertise or configure a separate retention period.

If the window is shortened, older history expires under the new limit. Increasing it later keeps
the history that is still available for longer, but it does not bring back history that already
expired under the earlier window.

## Field limits

These apply to every plan and are not usage meters. They bound one payload rather than a month's
volume, so a request that trips one is rejected immediately and is not retryable without a change.

| Field | Limit |
| --- | --- |
| Metric name | 255 characters |
| Span name | 255 characters |
| Attribute key | 512 bytes |
| Attribute value | 32,768 bytes |
| Attribute value nesting depth | 8 levels |
| `distinct_id` | 200 bytes |
| Distinct IDs in one recording lookup | 100 |
| Matchers in one alert silence | 32 |
| Alert-silence matcher label name | 512 bytes |
| Alert-silence matcher value | 4,096 bytes |
| Stack frames symbolicated for one request or occurrence | 256 |
| Event name (product events) | 200 characters |
| Attributes per span, span link, log record, data point, or exemplar | 256 |
| Total attribute bytes per span, log record, or data point | 262,144 bytes |
| Spans, log records, or data points per export request | 10,000 |

The attribute value limit is measured on the value's encoded size, whatever its type: a string, a
byte string, an array, or a nested key-value list. An array of 1,000 strings is measured as the sum
of those strings, not as one value.

Each distinct ID in a recording lookup is also subject to the 200-byte `distinct_id` limit above.
Alert-silence regular expressions use the matcher-value limit and are validated when the silence is
created or edited; a malformed expression is rejected instead of creating a silence that can never
match.

The **total attribute bytes** limit is the sum for one item, because one item is stored as one
record. For a span that is its own attributes plus every span link's and every span event's; for a
log record it is the body plus the attributes; for a metric data point it is the point's attributes
plus its exemplars'. Individually legal attributes can add up past this, and an item that does is
rejected. It is set well above ordinary instrumentation — a span carrying eight maximum-size
exception stack traces still fits — so reaching it usually means an application is attaching a whole
request or response body to a span. Attach a reference instead, or truncate before the attribute is
set.

Metric and span names follow the OpenTelemetry maximum instrument-name length of 255 characters.
Names are otherwise accepted as your instrumentation emits them, including forms OpenTelemetry does
not itself recommend — a leading digit, a colon (as Prometheus recording rules use), spaces, and
non-ASCII letters all work.

What a name may not contain is characters a reader cannot see: control characters, bidirectional
overrides, zero-width and invisible characters, and variation selectors. These have no legitimate use
in a name, and a name carrying them would display as something other than what it is wherever it is
read — in a dashboard, in a search result, or by an agent reading it through the API.

A rejected export names the field and the rule. It does not echo the value back, so if a name is
rejected, compare it against the limits above rather than expecting it in the error text.

**One invalid item rejects the whole export request.** If a single metric in a batch carries an
invalid name, the batch is refused rather than partially accepted, so nothing is silently dropped.
Fix the name and resend.

Gauge, sum, explicit-histogram and summary metrics are accepted. Exponential histograms are rejected
as unsupported metric data; configure the exporter to send explicit histograms before resending.
Summary counts and quantile values are stored as measurements and do not replace exporter labels
or create a new series whenever the observed values change.

Span start/end times, span-event times, metric observation times and exemplar times must be present
and fit the storage timestamp range. A metric's optional start time cannot follow its observation
time. Numeric observations must be present and finite; summary quantiles must be between zero and
one. These checks happen before delivery is acknowledged. OTLP `NoRecordedValue` markers are
accepted and count toward request limits; their timestamps and attributes are validated, while
their unused measurement fields are ignored. They produce no measurement row or persistent
staleness tombstone, so earlier measurements remain available in historical queries. Logs use
their source timestamp when present and otherwise their observed timestamp; at least one valid
time is required.

**Span events on an agent span are separately indexed up to 4,096 per span.** Agent span events —
the evaluation, guardrail and context records carried on a model-call span — each become an
individually addressable agent event you can filter, count and alert on. Past 4,096 on one span,
the remaining events are still stored on the span and still readable there, but they are not
separately indexed. Nothing is rejected and nothing is dropped from the span. The OpenTelemetry SDK
default caps span events at 128, so reaching this needs a deliberately raised
`OTEL_SPAN_EVENT_COUNT_LIMIT`; if you are, split the work across spans rather than raising it
further, because one span carrying thousands of events is also slower to read.

## Instrumentation attributes, service name, and exception types

Every instrumentation attribute map gets the same display-safety handling: resource, span,
span-event, span-link, log-record, gauge, sum, histogram and summary data-point attributes, plus
exemplar filtered attributes.

For item attributes, the count and byte limits in the table above are validation limits. Exceeding
one rejects the export as described above. Attributes that pass those limits then receive a second,
per-attribute character check. That check never rejects the export: a key or value containing
undisplayable characters is replaced by a stable fingerprint.

Resource and span-event attributes are not subject to the per-attribute count and value limits in
the table above. A span event's attribute bytes do count toward its span's total-attribute-bytes
limit, because they are stored on that span. Their stored representation is bounded instead:

| Field | Stored limit |
| --- | --- |
| Resource `service.name`, `deployment.environment` (and `environment`, `env`, `service.environment`) | 255 characters |
| Any other resource or span-event attribute value | 32,768 characters |
| Resource or span-event attribute key | 512 characters |
| Resource attributes per resource | 256 |
| Span event name, exception type | 255 characters |

An undisplayable value is replaced by `sha256:<digest>`. A clean value that exceeds a stored
character limit keeps a short readable prefix followed by `…sha256:<digest>`. The fingerprint is
stable, so all telemetry from that source still groups together, and it is computed over exactly the
bytes you sent, so you can identify which value it was:

```
printf '%s' "<the value you sent>" | shasum -a 256
```

Every attribute key is an identifier and may not contain newlines, carriage returns or tabs.
Attribute values are prose and may contain all three — a multi-line exception stack trace, SQL
statement, HTTP user agent or request body is stored intact. The resource identity values in the
first row above are the exception: they are single-line identifiers because they appear as labels
throughout the product.

If a resource carries more than 256 attributes, the excess is dropped in a stable order and the
number dropped is recorded on the resource as `anectico.resource.attributes_dropped`. `service.name`
and the environment attributes are never dropped.

The character-safety handling in this section is applied per attribute and never causes an export to
be rejected. The item count and byte validation limits still apply before it.

## Product analytics measurement limits

Trend, funnel and retention measurements run asynchronously and are bounded separately from the
usage meters above:

| Limit | Value |
| --- | --- |
| Measurements pending or running per project | 4 |
| Maximum wait for a synchronous answer (`wait_millis`) | 25 seconds |
| Maximum time one measurement may run | 120 seconds |
| How long a completed result stays readable | 1 hour |
| Contributors per selection | 1,000,000, read 1–100 per page |

A measurement that exceeds its running time, memory or scan budget fails outright with a named
reason rather than returning a smaller answer. See
[measure product events](/docs/investigate/product-analytics#measurement-limits) for the full
definition, retry semantics, and how to poll past the `wait_millis` ceiling.

## When a planned limit is reached

Anectico warns before a limit is exhausted. Under the planned policy, verbose logs degrade before spans,
while errors continue to ingest and alerts continue to evaluate. New replay sessions can be paused.
Clients should honor `429` responses and `Retry-After`; they should not retry in a tight loop.

The OTLP gRPC endpoint carries no HTTP status, so a throttled export is answered with
`RESOURCE_EXHAUSTED` accompanied by a retry delay. Standard OpenTelemetry exporters read that delay
and retry the batch automatically; no client change is needed. The delay matches the `Retry-After`
value the HTTP endpoint returns for the same condition.

Use **Settings → Account** to view current plan and usage.

- [Review usage and AI settings](/docs/manage/usage-and-ai-settings)
- [Keep metric series low-cardinality](/docs/instrument/metrics#keep-names-units-and-labels-stable)
- [REST API errors and retries](/docs/reference/rest-api)
- [Measure product events](/docs/investigate/product-analytics)
