> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aiaxoniq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Exporters

> How telemetry actually leaves your process or Collector: OTLP transports, batching, retries, queueing, and what happens when aiAxonIQ is unreachable.

An **exporter** is the component that sends telemetry out — from an SDK to a
Collector, or from a Collector to aiAxonIQ. Its configuration decides how much
you lose when something goes wrong, which makes it worth more attention than it
usually gets.

## The endpoint

| Deployment                               | Base endpoint                    |
| :--------------------------------------- | :------------------------------- |
| **aiAxonIQ Cloud**                       | `https://app.aiaxoniq.com/otlp`  |
| **Self-hosted** behind the bundled nginx | `https://app.<your-domain>/otlp` |

Both forms carry the `/otlp` prefix because nginx serves OTLP under it and
strips it before forwarding, so the receiver still sees `/v1/logs`. Dropping the
prefix is the most common setup mistake: the request reaches the dashboard
instead of the receiver and comes back as an HTML 404 rather than an ingest
error.

<Info>
  **Get Started** in the dashboard shows the exact base endpoint for your
  deployment next to a license key you create there, with copy buttons, and
  then watches for your first records. Where these pages write
  `$OIQ_ENDPOINT`, that page has the real value.
</Info>

## OTLP over HTTP or gRPC

aiAxonIQ accepts both. They carry identical data.

|                                            | OTLP/HTTP                               | OTLP/gRPC                |
| :----------------------------------------- | :-------------------------------------- | :----------------------- |
| Default port convention                    | 4318                                    | 4317                     |
| Path                                       | `/v1/logs`, `/v1/metrics`, `/v1/traces` | Service methods, no path |
| Credential                                 | `X-License-Key` header                  | `x-license-key` metadata |
| Passes ordinary proxies and load balancers | **Usually**                             | Needs HTTP/2 end to end  |
| Debuggable with `curl`                     | **Yes**                                 | No                       |

<Note>
  **Choose HTTP unless you have a reason not to.** It survives corporate
  proxies, TLS-terminating load balancers and egress inspection that gRPC does
  not, and you can reproduce any failure with one `curl`.

  gRPC is worth it for high-volume Collector-to-backend links, where the
  multiplexing and smaller framing measurably help.
</Note>

Full setup for both, including a first request you can verify, is on
[Send data with OpenTelemetry](/send-data/otel/collector).

## SDK exporter configuration

The portable environment variables, read by every OpenTelemetry SDK:

```bash theme={null}
OTEL_EXPORTER_OTLP_ENDPOINT="$OIQ_ENDPOINT"
OTEL_EXPORTER_OTLP_HEADERS="X-License-Key=$OIQ_LICENSE_KEY"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

<Warning>
  **The endpoint is the base URL, not a signal path.** The SDK appends
  `/v1/traces` itself. Setting `OTEL_EXPORTER_OTLP_ENDPOINT` to something
  already ending in `/v1/traces` produces a request to `/v1/traces/v1/traces`
  and a `404` that reads as if the endpoint were wrong.

  The per-signal variables — `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` and friends
  — are the exception: those *are* full paths, and nothing is appended.
</Warning>

| Protocol value  | Meaning                                                                               |
| :-------------- | :------------------------------------------------------------------------------------ |
| `http/protobuf` | OTLP over HTTP with a protobuf body. The common default.                              |
| `http/json`     | OTLP over HTTP with a JSON body. Larger, but human-readable — useful while debugging. |
| `grpc`          | OTLP over gRPC.                                                                       |

## Batching

Nothing should export one span at a time. Every SDK and the Collector batch by
default, and the defaults are usually right.

```yaml theme={null}
processors:
  batch:
    timeout: 5s
    send_batch_size: 8192
    send_batch_max_size: 10000
```

<Note>
  **Batching is what keeps you inside the ingest rate limit.** The receiver
  limits *requests*, not records, so a thousand small requests per second is a
  problem where one large request carrying a thousand records is not.

  If you are seeing `429`, batch harder before anything else. See
  [Ingest endpoints](/send-data/endpoints).
</Note>

`send_batch_max_size` also protects you from the opposite failure: a batch that
grows past the receiver's body limit and is rejected with `413` in full. Cap it
rather than discovering the ceiling.

## When aiAxonIQ is unreachable

This is the part worth configuring deliberately, because the default answer to
"what happens to my telemetry during a network blip" is *it is discarded*.

```yaml theme={null}
exporters:
  otlp:
    endpoint: ${OIQ_ENDPOINT}
    headers:
      X-License-Key: ${OIQ_LICENSE_KEY}
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s
    sending_queue:
      enabled: true
      num_consumers: 10
      queue_size: 5000
```

| Setting            | What it buys                                                                         |
| :----------------- | :----------------------------------------------------------------------------------- |
| `retry_on_failure` | Survives a transient failure or a `503`.                                             |
| `max_elapsed_time` | The point at which data is dropped rather than retried forever. Set it deliberately. |
| `sending_queue`    | An in-memory buffer so a brief outage does not lose data.                            |
| `queue_size`       | How much you buffer. Bigger means more memory and more to lose on a restart.         |

<Warning>
  **The sending queue is in memory by default.** A Collector restart loses
  whatever is in it. For a genuinely durable buffer, enable the Collector's
  persistent queue with a file storage extension — otherwise treat the queue as
  smoothing, not as a guarantee.
</Warning>

<Warning>
  **Do not retry a `4xx`.** A rejected payload will be rejected again, and
  retrying a `401` in a loop simply multiplies the failure. The Collector's
  default retry behaviour already distinguishes these — a hand-rolled exporter
  in your own code frequently does not.
</Warning>

## Multiple destinations

Telemetry can be exported to more than one place — aiAxonIQ and an existing
system during a migration, for instance:

```yaml theme={null}
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp, otlp/legacy]
```

<Info>
  **This is what makes an evaluation low-risk.** Because everything speaks
  standard OTLP, you can send to both your current backend and aiAxonIQ from
  the same Collector, compare them on real traffic, and change nothing in your
  applications either way.
</Info>

## Debugging what is leaving

```yaml theme={null}
exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      exporters: [otlp, debug]
```

The `debug` exporter prints what is being exported to the Collector's own log —
which settles "is my application sending this at all" without guessing.

<Warning>
  **Remove it before production.** At detailed verbosity it prints every span,
  which is a large volume of log output and can include attribute values you
  would rather not have in a log file.
</Warning>

## Verify

Any exporter change is confirmed the same way: send something recognisable and
find it.

* **`202 Accepted`** means the receiver took it. Anything else is answered by
  [Ingest endpoints](/send-data/endpoints).
* **Then look in the product**, because accepted is not queryable — see
  [Verify your data arrived](/get-started/verify-data).

## Next

<CardGroup cols={3}>
  <Card title="Collector configuration" icon="server" href="/send-data/otel/collector-config">
    The full pipeline: receivers, processors, exporters.
  </Card>

  <Card title="Sampling" icon="percent" href="/send-data/otel/sampling">
    Reduce what you export in the first place.
  </Card>

  <Card title="Ingest endpoints" icon="list" href="/send-data/endpoints">
    Status codes, rate limits and size caps.
  </Card>
</CardGroup>
