An application is configured with OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf and sends telemetry to http://collector:4317. The Collector is listening. DNS works. The port is open. Nothing arrives.
The problem is one small mismatch: the application is sending OTLP over HTTP to the default OTLP/gRPC port. Both sides speak OTLP, but they are knocking on different doors.
That is the first useful thing to understand about OTLP. It is not a single URL or one wire format.
OTLP, short for OpenTelemetry Protocol, is the standard protocol OpenTelemetry uses to encode and transport telemetry between sources, Collectors and observability backends. It carries logs, metrics and traces over gRPC or HTTP while preserving the OpenTelemetry data model.
What is OTLP?
OTLP is a vendor-neutral telemetry delivery protocol defined by the OpenTelemetry project. An OpenTelemetry SDK, Collector or another compatible client can use it to send telemetry to any server that implements the same protocol.
The current OTLP specification defines three parts of that exchange:
- encoding: how logs, metrics and traces are represented using Protocol Buffers
- transport: how those messages travel over gRPC or HTTP
- delivery: how a receiver reports success, rejection, throttling and retryable failure
OTLP is stable for logs, metrics and traces. The profiles signal remains under development, so do not treat every OTLP signal as having the same maturity.
The protocol gives telemetry a common path across languages and vendors. It does not decide which spans to sample, redact secrets, retain data or provide a query language. Those jobs belong to SDKs, Collectors and backends.
Where OTLP sits in an OpenTelemetry pipeline
OTLP connects components. It is not the component itself.
OpenTelemetry SDK --OTLP--> Collector --OTLP--> observability backendThe first hop is optional. An SDK can send OTLP directly to a compatible backend:
OpenTelemetry SDK --OTLP--> observability backend
An OTLP exporter is the sending side. It serializes OpenTelemetry data, attaches transport settings such as headers or compression and sends export requests to an endpoint.
An OTLP receiver is the listening side. It accepts those requests, decodes them and hands the telemetry to the next component. In the OpenTelemetry Collector, the otlp receiver can listen for OTLP/gRPC, OTLP/HTTP or both.
One Collector can therefore receive OTLP from an application and export OTLP to another Collector or backend. The protocol stays the same while the sender and receiver roles change at each hop.
If the Collector itself is new to you, start with the OpenTelemetry Collector guide. This article stays at the protocol boundary.
How OTLP works
OTLP uses a request-response model. A client sends an export request containing one signal and the server returns a response for that request.
Each request carries a hierarchy rather than repeating the same context on every record. A trace request, for example, groups spans by resource and instrumentation scope. Logs and metrics follow a similar structure.
resource -> instrumentation scope -> spans, metric points or log recordsThe resource identifies the entity producing telemetry, using attributes such as service.name, service.version and deployment environment. The instrumentation scope identifies the library that produced it. The records contain the signal-specific data.
This structure is one reason OpenTelemetry semantic conventions matter. OTLP can carry an attribute faithfully, but it cannot make two teams use the same name for the same idea.
OTLP uses Protocol Buffers
OTLP defines its messages with Protocol Buffers. Both transports use the same underlying message schema:
- OTLP/gRPC sends binary Protobuf messages through gRPC service calls.
- OTLP/HTTP can send binary Protobuf or JSON-encoded Protobuf in HTTP POST bodies.
JSON encoding is useful when an environment requires it or when a payload must be inspected with familiar HTTP tools. Binary Protobuf is the usual production choice because it is more compact.
A response is an acknowledgement, not permanent storage
A successful response means the receiving node accepted the request. It does not prove that the data survived every later hop or reached durable storage.
This distinction matters in a multi-hop pipeline:
application -> agent Collector -> gateway Collector -> backendEach acknowledgement covers one client-server pair. Queues, retries and persistence between later nodes still need their own configuration and monitoring.
OTLP/HTTP vs OTLP/gRPC
OTLP has two standard transports. Neither is a less legitimate version of the protocol.
| Detail | OTLP/HTTP | OTLP/gRPC |
|---|---|---|
| Default port | 4318 | 4317 |
| Payload | Binary Protobuf or JSON-encoded Protobuf | Binary Protobuf |
| Request style | HTTP POST | Unary gRPC export call |
| Signal path | /v1/traces, /v1/metrics, /v1/logs | Signal-specific gRPC service |
| Network fit | Familiar HTTP proxies, load balancers and debugging tools | Environments with working HTTP/2 and gRPC support |
| Typical protocol value | http/protobuf or http/json | grpc |

The OpenTelemetry exporter specification recommends starting with OTLP/HTTP using binary Protobuf unless compatibility requires gRPC. Test gRPC when its connection model offers a measurable benefit in your environment.
Do not choose gRPC because a comparison page calls it the “fast option” and stop there. Actual throughput depends on exporter implementation, request concurrency, payload size, latency, compression and the receiver. Test both through the network path you will operate.
HTTP is often easier to inspect with existing proxies and tooling. gRPC avoids signal paths and is widely used between backend services and Collectors. Browser environments usually favor HTTP, subject to the receiver's CORS and authentication setup.
The choice must match on both ends. Sending HTTP to a gRPC-only listener does not become gRPC because the port number looks close enough.
OTLP ports, paths and endpoints
The defaults are simple:
- OTLP/gRPC:
4317 - OTLP/HTTP:
4318 - traces over HTTP:
/v1/traces - metrics over HTTP:
/v1/metrics - logs over HTTP:
/v1/logs
The paths are part of OTLP/HTTP, not OTLP/gRPC.
For an SDK exporter, the broad environment variables are:
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318When the general OTEL_EXPORTER_OTLP_ENDPOINT is used for OTLP/HTTP, compliant exporters construct the signal URLs from that base. Traces go to /v1/traces, metrics to /v1/metrics and logs to /v1/logs.
Signal-specific endpoint variables behave differently. Their URLs are used as provided, so include the complete path:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel-collector:4318/v1/traces
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://otel-collector:4318/v1/metrics
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://otel-collector:4318/v1/logsThat base-versus-complete-URL distinction produces a surprising number of 404 responses. The official OTLP exporter configuration defines the precedence and URL construction rules. Language SDK support still varies, so check the exporter documentation for the SDK version you deploy.
Ports 4317 and 4318 are defaults, not requirements. A hosted backend may expose OTLP over port 443 and a Collector can listen on another port. Configure the endpoint the receiver actually exposes.
Configure an OTLP receiver and exporter
The following Collector configuration accepts OTLP over both transports and sends the received traces to the debug exporter:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
verbosity: basic
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]The receiver block only defines the listeners. The pipeline activates the receiver for traces. If you need a complete three-signal example with processors and validation, use the OpenTelemetry Collector configuration guide.
To forward data to an OTLP/HTTP destination, replace or supplement debug with an HTTP exporter:
exporters:
otlp_http/backend:
endpoint: https://telemetry.example.com
headers:
Authorization: Bearer ${env:OTLP_TOKEN}
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlp_http/backend]The receiver and exporter can use different transports. A Collector may receive gRPC from applications and export HTTP to the backend. It decodes the incoming request into its internal data model before the exporter encodes the outgoing request.
Should an SDK send OTLP directly or through a Collector?
Direct export is valid. A Collector is not a protocol tax that every small deployment must pay.
| Send directly when | Put a Collector in the path when |
|---|---|
| one service sends to one OTLP backend | many services need one managed endpoint |
| the backend handles the required authentication | application code should not hold backend credentials |
| no shared processing is required | telemetry needs batching, filtering, redaction, enrichment or routing |
| a short local setup matters more than central policy | queues and retry behavior need to be operated centrally |
Use a Collector when it has a specific job. “Our architecture diagram had an empty box” is not one.
For production pipelines, a Collector also keeps backend changes away from application configuration. The applications send OTLP to an internal endpoint; the Collector owns destination credentials and routing.

What happens when an OTLP request fails?
OTLP distinguishes full success, partial success and failure.
Full success
The server accepted the request. OTLP/HTTP returns 200 OK with an export response. OTLP/gRPC returns the corresponding successful service response.
Partial success
The server accepted some records and rejected others. Its response includes the rejected item count and may include an error message.
The client must not retry the whole request after a partial-success response. Doing so would resend the accepted records and could create duplicates. Instead, record the rejection and fix the data or receiver limit that caused it.
Retryable failure
Temporary failures may be retried. For OTLP/HTTP, the specification lists 429, 502, 503 and 504 as retryable. A client should honor Retry-After when supplied and otherwise use exponential backoff with jitter.
For OTLP/gRPC, UNAVAILABLE is the usual retryable signal. A receiver can include retry timing information.
Non-retryable failure
Bad data and incompatible requests should be dropped rather than retried forever. OTLP/HTTP uses 400 Bad Request for permanently invalid data. Repeating the same request only converts a data problem into a queue problem.
OTLP also has an unavoidable duplicate edge case. If a server accepts a request but the response is lost, the client cannot know whether retrying will duplicate data. Treat OTLP as robust hop-to-hop delivery, not exactly-once delivery.
Common OTLP errors and how to read them
Most first-day OTLP failures I have debugged were boundary mismatches.
| Symptom | Likely cause | Check |
|---|---|---|
gRPC UNIMPLEMENTED or HTTP/2 protocol error | Transport sent to the wrong listener | Match grpc with the gRPC endpoint and http/protobuf with the HTTP endpoint |
HTTP 404 | Missing or duplicated signal path | Check whether the variable expects a base URL or complete /v1/... URL |
HTTP 415 | Unsupported Content-Type or encoding | Use application/x-protobuf for binary Protobuf or a receiver-supported JSON encoding |
HTTP 401 or 403 | Missing or invalid authentication | Check exporter headers and secret expansion |
| TLS handshake failure | Scheme, certificate or server name mismatch | Verify http versus https, CA trust and endpoint hostname |
HTTP 413 or gRPC resource exhaustion | Request exceeds a receiver limit | Reduce batch size or raise a tested receiver limit |
| Telemetry reaches the Collector but not the backend | Exporter, queue or downstream failure | Inspect Collector exporter logs and internal metrics |
When debugging, prove one boundary at a time:
- Confirm the receiver is listening on the expected interface and port.
- Confirm exporter protocol and endpoint agree.
- Send one signal to the Collector's
debugexporter. - Add authentication and the remote backend only after that local path works.
- Watch rejected, failed and dropped telemetry counters rather than relying on an empty dashboard.
The Collector metrics guide covers the internal signals that show whether a receiver accepted data and whether an exporter sent it.
Send OTLP data to Parseable
Parseable accepts OTLP logs, metrics and traces over HTTP at /v1/logs, /v1/metrics and /v1/traces. A Collector can route each signal with the dataset headers Parseable expects.
This trace exporter uses the Parseable base URL; the OTLP HTTP exporter appends /v1/traces:
exporters:
otlp_http/parseable_traces:
endpoint: ${env:PARSEABLE_URL}
encoding: proto
headers:
X-API-Key: ${env:PARSEABLE_API_KEY}
X-P-Stream: otel-traces
X-P-Log-Source: otel-traces
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlp_http/parseable_traces]Use separate exporter instances when signals need different datasets or headers. The current Parseable OpenTelemetry documentation covers direct OTLP ingestion and signal-specific examples.
What OTLP does not do
OTLP is easy to give too much credit because it sits on an important boundary.
It does not:
- instrument application code
- define all attribute names and values
- choose a sampling policy
- redact sensitive data
- guarantee durable end-to-end storage
- provide search, dashboards or alerts
- make a backend OpenTelemetry-native by itself
An endpoint accepting OTLP proves protocol compatibility. It does not prove that the backend preserves semantic conventions, correlates signals or lets you export the data without translating it into a private schema.
The practical rule
OTLP standardizes the handoff. To make that handoff work, match four things on both sides: transport, endpoint, signal and authentication.
Start with one signal and a local receiver. Prove it with the debug exporter. Then add the backend, processing and production failure behavior. That order removes several moving parts from the first error message—and usually finds the wrong door quickly.

