# Sending OTLP

> The OTLP/HTTP endpoints, ingest keys, formats, compression, size limits, responses, and how metering counts bytes.

Pulse speaks [OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp), so any OpenTelemetry Collector or SDK can send to it with no plugin.

| Endpoint | Takes |
| --- | --- |
| `POST https://pulse.nightroll.app/v1/logs` | `ExportLogsServiceRequest` |
| `POST https://pulse.nightroll.app/v1/metrics` | `ExportMetricsServiceRequest` |
| `POST https://pulse.nightroll.app/v1/traces` | Nothing yet: answers `404` with `{"error":"Pulse doesn't accept traces yet"}` |

Set the exporter's endpoint to `https://pulse.nightroll.app` and it adds the `/v1/...` paths itself.

## Ingest keys

Send the key as `Authorization: Bearer pulse_ik_...` (or as `X-Pulse-Key: pulse_ik_...`). Org owners and admins create and revoke keys under **Ingest keys** in the app, or with the MCP tools `create_ingest_key` and `revoke_ingest_key`. A key is shown once.

Ingest keys only send data. They can't read anything, and the org's API keys (`nr_pulse_...`) can't send data.

## Formats

- **Protobuf** (`Content-Type: application/x-protobuf`), the default for SDKs and the Collector.
- **JSON** (`Content-Type: application/json`), the OTLP JSON mapping: camelCase field names, trace and span ids as hex, 64-bit integers as strings or numbers.
- **Compression:** send `Content-Encoding: gzip`; the Collector does with `compression: gzip`.

A request may be at most **16 MB after decompression**. Bigger requests get `413`; lower the batch size.

## Responses

| Status | Means | Retried by exporters |
| --- | --- | --- |
| `200` | Accepted. An empty `Export*ServiceResponse`, with `partial_success` when some rows were rejected | — |
| `400` | Malformed body | No |
| `401`, `403` | Missing, bad or revoked ingest key | No |
| `413` | Over 16 MB decompressed | No |
| `429`, `503` | Pulse can't keep up right now; `Retry-After` says when to try again | Yes |

## What you pay for

Ingest is metered on **decompressed OTLP bytes**, and **each request counts as at least 128 KB**, because every request costs Pulse the same however small it is. Batch: the Collector's `batch` processor (`send_batch_size: 8192, timeout: 10s`) or `OTEL_BLRP_SCHEDULE_DELAY=10000` in SDKs keeps requests large. Prices are on the [pricing page](/pricing).

## What Pulse keeps

Every log record and metric data point becomes a row. Resource attributes, scope name and point attributes are all kept; `service.name` is lifted out as the row's service. See [Logs](/docs/logs/) and [Metrics](/docs/metrics/) for the fields.
