# Metrics REST API

> Use the supported metric discovery, aggregation, histogram, forecast, and deletion endpoints.

Canonical page: https://anectico.com/docs/reference/metrics-api/


Use the Metrics REST API for direct integrations that cannot use the Anectico CLI. Give read clients
`metrics:read`; deletion requires `metrics:delete`. OTLP ingestion is separate and requires
`ingest:write` at `POST https://api.anectico.com/v1/metrics`.

All resource calls below use this base URL and header:

```text
https://app.anectico.com/api/v1
X-Anectico-API-Key: an_...
```

Project selection is endpoint-specific. Name discovery, aggregation, and histogram queries take
`project_id` in the query string; forecasts take it in the JSON body; deletion derives its scope
from the credential. Label discovery also takes `project_id` in the query string and applies the
metric name and requested time window to both key and value discovery.

## List metric names

```bash
curl --fail-with-body \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  "https://app.anectico.com/api/v1/metrics/names?project_id=$ANECTICO_PROJECT"
```

Optional `start_time` and `end_time` query parameters are RFC 3339 timestamps. The response is
alphabetical and capped at 1,000 names:

```json
{
  "metrics": [
    {"name": "checkout.duration_ms", "type": "histogram"},
    {"name": "checkout.queue_depth", "type": "gauge"}
  ],
  "total_count": 2,
  "retrieved_at": "2026-07-21T12:00:00Z"
}
```

Types are `counter`, `gauge`, `histogram`, or `summary` when the ingested data supplies one.

## Discover labels

```bash
curl --fail-with-body \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  "https://app.anectico.com/api/v1/metrics/checkout.duration_ms/labels?project_id=$ANECTICO_PROJECT&start_time=2026-07-21T00:00:00Z&end_time=2026-07-21T12:00:00Z"
```

The response contains label keys found on the requested metric and available values for each key:

```json
{
  "labels": [
    {"name": "service_name", "values": ["checkout-api", "checkout-worker"]},
    {"name": "environment", "values": ["production"]},
    {"name": "region", "values": ["eu-west", "us-east"]}
  ]
}
```

URL-encode a metric name when constructing the path programmatically. Label discovery accepts the
same optional `start_time` and `end_time` query parameters and scopes both label keys and values
to the requested project, metric, and time window. A project-bound credential remains pinned to its
signed project.

A label named after a recorded-content convention is omitted with all of its values unless the
credential holds `agents:content:read` and the project's current read policy permits that content.
The same decision applies to custom-label filters and `group_by`: a denied classified dimension is
refused before its values are queried, rather than returning series whose labels were silently
emptied and could collapse together.

## Aggregate a metric

`POST /metrics/aggregate` returns time-ordered scalar series. Put `project_id` in the query string,
not the JSON body.

```bash
curl --fail-with-body \
  -X POST \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  -H "Content-Type: application/json" \
  "https://app.anectico.com/api/v1/metrics/aggregate?project_id=$ANECTICO_PROJECT" \
  --data '{
    "metric_name": "checkout.queue_depth",
    "aggregation": "max",
    "step": "5m",
    "time_range": {
      "start": "2026-07-21T10:00:00Z",
      "end": "2026-07-21T12:00:00Z"
    },
    "filters": [
      {"key": "region", "values": ["eu-west", "us-east"]}
    ],
    "group_by": ["service_name"],
    "environment": "production"
  }'
```

```json
{
  "series": [
    {
      "labels": {"service_name": "checkout-api"},
      "points": [
        {"timestamp": "2026-07-21T10:00:00Z", "value": 12},
        {"timestamp": "2026-07-21T10:05:00Z", "value": 18}
      ]
    }
  ]
}
```

Supported fields:

| Field | Required | Behavior |
| --- | --- | --- |
| `metric_name` | Yes | Exact metric name |
| `time_range.start`, `time_range.end` | No | RFC 3339 closed time window; retention still applies |
| `step` | No | Bucket size; defaults to `1h` |
| `aggregation` | No | Defaults to `avg` |
| `service_name` | No | Exact emitting-service filter |
| `environment` | No | Exact deployment-environment filter |
| `filters` | No | Up to two label dimensions |
| `group_by` | No | Up to two label dimensions |

Aggregations are `avg`, `sum`, `min`, `max`, `count`, `p50`, `p95`, `p99`, `stddev`, and
`variance`. Steps are `1s`, `5s`, `10s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`, `6h`,
`12h`, `1d`, and `7d`.

Each filter requires one to 50 non-empty, distinct values. Values within one filter use OR;
different filter dimensions use AND. Filter and group keys must begin with a letter or underscore
and may then contain letters, digits, `_`, `.`, or `-`, up to 128 characters.

Queries are bounded to 50 grouped series and 500 points per series. Narrow the time range, increase
`step`, or reduce grouping when the API rejects a larger query.

## Query a native histogram

`POST /metrics/histogram` returns the preserved explicit-bucket distribution for an OTLP histogram.
It does not accept aggregation or group-by.

```bash
curl --fail-with-body \
  -X POST \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  -H "Content-Type: application/json" \
  "https://app.anectico.com/api/v1/metrics/histogram?project_id=$ANECTICO_PROJECT" \
  --data '{
    "metric_name": "checkout.duration_ms",
    "step": "5m",
    "time_range": {
      "start": "2026-07-21T10:00:00Z",
      "end": "2026-07-21T12:00:00Z"
    },
    "environment": "production"
  }'
```

```json
{
  "metric_name": "checkout.duration_ms",
  "step": "5m",
  "cells": [
    {
      "timestamp": "2026-07-21T10:00:00Z",
      "upper_bound": 100,
      "upper_bound_infinite": false,
      "count": "42"
    },
    {
      "timestamp": "2026-07-21T10:00:00Z",
      "upper_bound": 0,
      "upper_bound_infinite": true,
      "count": "3"
    }
  ]
}
```

The terminal `+Inf` bucket uses `upper_bound_infinite: true`; ignore its numeric `upper_bound`.
Each count belongs to the range after the preceding bound through the cell's upper bound; it is not a
cumulative count. Protobuf `uint64` counts may be encoded as JSON strings. A non-histogram metric,
or old histogram data without preserved buckets, returns no distribution cells.

## Forecast a metric

`POST /metrics/forecast` calculates an expected band from an average-value history. Unlike the
aggregate and histogram endpoints, `project_id` is a JSON field.

```bash
curl --fail-with-body \
  -X POST \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  -H "Content-Type: application/json" \
  "https://app.anectico.com/api/v1/metrics/forecast" \
  --data @- <<JSON
{
  "metric_name": "checkout.queue_depth",
  "interval": "5m",
  "horizon": 24,
  "sensitivity": 0,
  "environment": "production",
  "project_id": "$ANECTICO_PROJECT",
  "time_range": {
    "start": "2026-07-14T12:00:00Z",
    "end": "2026-07-21T12:00:00Z"
  }
}
JSON
```

`horizon` is the number of future buckets, defaults to 24, and cannot exceed 1,000. `sensitivity`
is `0` for automatic band width or a value from 1 through 10. Without a time range, the service uses
the last seven days of retained history. The response always explains whether the model could fit
the history; `forecast` is populated only when `status` is `FORECAST_FIT_STATUS_READY`.

```json
{
  "metric_name": "checkout.queue_depth",
  "interval": "5m",
  "horizon": 24,
  "history_points": 2016,
  "required_history_points": 576,
  "missing_buckets": 0,
  "status": "FORECAST_FIT_STATUS_READY",
  "status_reason": "Forecast fitted from regular history with a stable daily seasonal pattern.",
  "confidence": 0.91,
  "band_semantics": "additive_holt_winters_brutlag_seasonal_deviation",
  "forecast": [
    {
      "timestamp": "2026-07-21T12:05:00Z",
      "expected": 11.8,
      "lower": 6.1,
      "upper": 17.5
    }
  ]
}
```

The possible statuses are `FORECAST_FIT_STATUS_READY`,
`FORECAST_FIT_STATUS_INSUFFICIENT_HISTORY`, `FORECAST_FIT_STATUS_IRREGULAR_HISTORY`, and
`FORECAST_FIT_STATUS_NONSEASONAL`. `status_reason` is the stable human-readable explanation;
`required_history_points` is the two-daily-season minimum for the selected interval.
`missing_buckets` counts expected grid timestamps skipped by forward gaps. Duplicate and
out-of-order observations also make history irregular but do not themselves increase that count.

`confidence` is a deterministic seasonal-fit score from 0 through 1, not probability or interval
coverage. `band_semantics` identifies the additive Holt-Winters/Brutlag seasonal-deviation model
used for a ready response. An unavailable status returns an empty `forecast` together with these
explicit fields, so an empty array is never ambiguous. A forecast remains a statistical expectation,
not a root-cause explanation or capacity guarantee. When every historical value is non-negative,
the expected value and both bounds are floored at zero; an additive trend cannot produce an
impossible negative count, rate, or latency in the returned cone.

## Delete a metric

```bash
curl --fail-with-body \
  -X DELETE \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  "https://app.anectico.com/api/v1/metrics/anectico.verification.value?project_id=$ANECTICO_PROJECT"
```

Deletion removes every datapoint with that metric name **in one project** and cannot be undone
through Anectico. `project_id` is required: an organization-level credential must name the project to
delete from, and there is no way to delete a metric across every project in one call. A
project-scoped key may omit `project_id` and stays confined to its own project; naming a different
one is rejected. Prefer the CLI, which sends your active project and requires explicit
confirmation:

```bash
anectico metrics delete anectico.verification.value --yes
```

Deletion is durable, not a filter. Once a metric is deleted, its datapoints stop appearing in
metric queries, series, exemplars, dashboards and exports — and a late or retried copy sent by an
SDK after the delete stays hidden too, rather than reappearing. **Alert rules honour the same
deletion**, including anomaly rules: a deleted metric cannot raise an alert, and cannot contribute
to the value an alert reports.

Deletion is metric-specific. Removing one metric name leaves your traces, logs, errors and other
metrics untouched; delete those through their own controls.

- [Record application metrics](/docs/instrument/metrics)
- [Explore and compare metrics](/docs/investigate/metrics)
- [REST API conventions](/docs/reference/rest-api)
- [Permission scopes](/docs/reference/permissions)
