---
title: "Ingest OTLP metrics (Bearer-authenticated)"
method: POST
path: "/v1/metrics"
tags: ["Traces"]
---

# Ingest OTLP metrics (Bearer-authenticated)

`POST /v1/metrics`

## Request

- Base URL: `https://ping.lastping.dev`
- URL: `https://ping.lastping.dev/v1/metrics`
- Auth: HTTP bearer

Accepts one OTLP `ExportMetricsServiceRequest` (protobuf or its JSON
mapping) and enqueues the agent usage it carries: token and cost
counters from Claude Code (`claude_code.token.usage`,
`claude_code.cost.usage`), Gemini CLI, Cursor and the OpenTelemetry
GenAI conventions (`gen_ai.client.token.usage`). Only DELTA
temporality is summed; a cumulative point, a metric family LastPing
does not keep and a point with no usable value are counted in
`dropped` and discarded, and the rest of the request is kept. A
request adds at most 16 model series. A data point's own timestamp
counts when it is within seven days before, or an hour after, the
moment ingest received it; otherwise it counts at that moment.

**Auth**: exactly as `POST /v1/traces`: `Authorization: Bearer lp_…`
with an unrevoked, unexpired key scoped `ingest`, `write` or `admin`,
enforced unconditionally. `lastping.monitor_id` (a resource
attribute) names the monitor, required unless the key is bound to
one; a monitor outside the key's project is 404, the same as a
monitor that does not exist.

**Content-Type**: `application/x-protobuf` or `application/json`
(415 otherwise; gRPC is not accepted, so set
`OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`). `Content-Encoding:
gzip` is accepted under the same size cap.

**Caps**: body ≤ 1 MiB decompressed (413 over); ≤ 500 data points per
request (400 over). Rate limit: 3,600 requests/hour/project, its own
bucket, separate from traces, logs and pings. Data points count
against the project's daily span budget. Every label is redacted
like a span attribute and stored up to 200 bytes.

**Durability**: 202 only after the enqueue succeeds; a queue failure
is 503, never a silent ack. Metric sums are at-least-once: a request
retried after a 503 may count twice.

## Request body

- object: The OTLP JSON mapping of `ExportMetricsServiceRequest`.

## Response `202`

Accepted and enqueued.

- object
  - `accepted` integer, required: Data points or log records enqueued from this request.
  - `dropped` integer, required: Data points or log records of this request that LastPing does not keep (an unknown metric family or event, a cumulative point, a point with no usable value) and discarded. When a request kept nothing, the monitor's ingest diagnostics say why.

## Other responses

- `400`: Malformed OTLP payload, more than 500 data points, or a missing `lastping.monitor_id`, or one that disagrees with the monitor the key is bound to.
- `401`: Missing, unknown, revoked or expired API key.
- `403`: The API key's scope is `read`.
- `404`: `lastping.monitor_id` does not name a check in the key's project. No existence oracle.
- `413`: Decompressed body exceeds 1 MiB.
- `415`: Content-Type is neither `application/x-protobuf` nor `application/json`.
- `429`: The metrics rate cap for this project (3,600 requests/hour, its own bucket) is exceeded, with `Retry-After: 60`. Or the project's daily span budget, which data points count against, is used up: `code: "SPAN_BUDGET_EXCEEDED"`, `Retry-After` to 00:00 UTC. Or LastPing is already reading as many OTLP exports as it takes at once: `code: "OTLP_BUSY"`, `Retry-After: 5`.
- `503`: Ingestion is unavailable on this deployment, the queue could not accept the message, or the budget could not be read. Retry.

---

[API](https://skmtc.dev/lastping/apis/lastping-management-api.md) · [All operations](https://skmtc.dev/lastping/apis/lastping-management-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lastping/apis/lastping-management-api/revisions/bbc6eda91c46?raw)
