API reference · v0.1.0
Logs HTTP API
Loki-compatible LogQL query and log ingestion API. Generated from the OpenAPI 3.1 document shipped with the server.
Base URLs
Writers serve every
/write route and readers serve every /read route. The operator creates a Service for each, named after the Logs resource. Any writer accepts any write and forwards it to the owning shard, so plain round-robin load balancing is enough. Authentication
Requests use HTTP basic auth. Each NamespaceAuthentication grants one username
read or write on one namespace, with the password held in a Secret. Probes and self-metrics are unauthenticated.Namespaces
Data is partitioned into tenant namespaces, and every data route carries one as
/read/ns/{namespace} or /write/ns/{namespace}. A Loki data source pointed at http://logs-reader.telemetry:3100/read/ns/default works unchanged.Errors and backpressure
Errors use the Loki error envelope with a 4xx or 5xx status. When object storage falls behind, writers reject new batches with
429 and a Retry-After header; configure your agent to retry them.Base URLs
- writer
- http://logs-writer.telemetry:3100
- reader
- http://logs-reader.telemetry:3100
Endpoints16
- GET
/read/ns/…/loki/api/v1/query - POST
/read/ns/…/loki/api/v1/query - GET
/read/ns/…/loki/api/v1/query_range - POST
/read/ns/…/loki/api/v1/query_range - GET
/read/ns/…/loki/api/v1/label/{name}/values - GET
/read/ns/…/loki/api/v1/labels - GET
/read/ns/…/loki/api/v1/series - POST
/read/ns/…/loki/api/v1/series - GET
/write/ns/…/elasticsearch - POST
/write/ns/…/elasticsearch/_bulk - GET
/write/ns/…/elasticsearch/_cluster/health - POST
/write/ns/…/elasticsearch/{index}/_bulk - POST
/write/ns/…/loki/api/v1/push - POST
/write/ns/…/otlp/v1/logs - GET
/-/healthy - GET
/-/ready
Error · 400
{
"error": "parse error at line 1, col 18: syntax error: unexpected \"}\""
}Resource
query
LogQL evaluation. Every read route is namespace-scoped under/read/ns/{namespace}.Instant query
GET
Evaluates a LogQL expression at a single point in time. Log selectors return streams of entries; metric queries such as /read/ns/{namespace}/loki/api/v1/queryrate() or count_over_time() return a vector.Path parameters
- Configured tenant namespace
namespacestringrequired
Query parameters
- LogQL expression
querystringrequired - Evaluation timestamp as RFC 3339 or Unix nanoseconds. Defaults to now.
timestring - Maximum number of results to return.
limitintegerMinimum 0 - forward or backward
directionstring
Responses
200Loki query responseapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid queryapplication/json›ErrorEnvelope
errorstringrequired
401Authentication required404Unknown namespace
Request
curl -G http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/query \
-u reader:$PASSWORD \
--data-urlencode 'query={app="checkout"} |= "error" | logfmt | duration > 250ms' \
--data-urlencode 'limit=100'Response · 200
{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"app": "checkout",
"env": "prod"
},
"values": [
[
"1791464400000000000",
"level=error msg=\"payment declined\" duration=412ms"
]
]
}
],
"stats": {}
}
}Instant query (form)
POST
Evaluates a LogQL expression at a single point in time. Log selectors return streams of entries; metric queries such as /read/ns/{namespace}/loki/api/v1/queryrate() or count_over_time() return a vector.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Form fields
application/x-www-form-urlencoded- LogQL expression
querystringrequired - Evaluation timestamp as RFC 3339 or Unix nanoseconds. Defaults to now.
timestring - Maximum number of results to return.
limitintegerMinimum 0 - forward or backward
directionstring
Responses
200Loki query responseapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
Request
curl -X POST "http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/query" \
-u reader:$PASSWORD \
--data-urlencode 'query={app="checkout"} |= "error" | logfmt | duration > 250ms' \
--data-urlencode 'limit=100'Response · 200
{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"app": "checkout",
"env": "prod"
},
"values": [
[
"1791464400000000000",
"level=error msg=\"payment declined\" duration=412ms"
]
]
}
],
"stats": {}
}
}Range query
GET
Evaluates a LogQL expression over a time range. Log queries return up to /read/ns/{namespace}/loki/api/v1/query_rangelimit entries in direction order; metric queries return a matrix sampled every step.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Query parameters
- LogQL expression
querystringrequired - Start of the range as RFC 3339 or Unix nanoseconds. Defaults to one hour before
startstringend. - End of the range as RFC 3339 or Unix nanoseconds. Defaults to now.
endstring - Duration before
sincestringendto use asstart, such as1h. Ignored whenstartis set. - Step for metric queries as a duration or float seconds.
stepstring - Maximum number of results to return.
limitintegerMinimum 0 directionstringforwardorbackward(default). Order in which log entries are returned.
Responses
200Loki range-query responseapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid queryapplication/json›ErrorEnvelope
errorstringrequired
Request
curl -G http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/query_range \
-u reader:$PASSWORD \
--data-urlencode 'query={app="checkout"} |= "error" | logfmt | duration > 250ms' \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z' \
--data-urlencode 'step=60s' \
--data-urlencode 'limit=100'Response · 200
{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"app": "checkout",
"env": "prod"
},
"values": [
[
"1791464400000000000",
"level=error msg=\"payment declined\" duration=412ms"
]
]
}
],
"stats": {}
}
}Range query (form)
POST
Evaluates a LogQL expression over a time range. Log queries return up to /read/ns/{namespace}/loki/api/v1/query_rangelimit entries in direction order; metric queries return a matrix sampled every step.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Form fields
application/x-www-form-urlencoded- LogQL expression
querystringrequired - Start of the range as RFC 3339 or Unix nanoseconds. Defaults to one hour before
startstringend. - End of the range as RFC 3339 or Unix nanoseconds. Defaults to now.
endstring - Duration before
sincestringendto use asstart, such as1h. Ignored whenstartis set. - Step for metric queries as a duration or float seconds.
stepstring - Maximum number of results to return.
limitintegerMinimum 0 directionstringforwardorbackward(default). Order in which log entries are returned.
Responses
200Loki range-query responseapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
Request
curl -X POST "http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/query_range" \
-u reader:$PASSWORD \
--data-urlencode 'query={app="checkout"} |= "error" | logfmt | duration > 250ms' \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z' \
--data-urlencode 'step=60s' \
--data-urlencode 'limit=100'Response · 200
{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"app": "checkout",
"env": "prod"
},
"values": [
[
"1791464400000000000",
"level=error msg=\"payment declined\" duration=412ms"
]
]
}
],
"stats": {}
}
}Resource
metadata
Discovery of labels, values, and streams for query builders such as Grafana Explore.List label values
GET
Returns the sorted values of one stream label in the requested range./read/ns/{namespace}/loki/api/v1/label/{name}/valuesPath parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it. - Stream-label name
namestringrequired
Query parameters
- Start of the range as RFC 3339 or Unix nanoseconds. Defaults to one hour before
startstringend. - End of the range as RFC 3339 or Unix nanoseconds. Defaults to now.
endstring - Duration before
sincestringendto use asstart, such as1h. Ignored whenstartis set.
Responses
200Sorted values for the stream labelapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid time rangeapplication/json›ErrorEnvelope
errorstringrequired
401Authentication required
Request
curl -G http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/label/app/values \
-u reader:$PASSWORD \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z'Response · 200
{
"status": "success",
"data": [
"checkout",
"gateway",
"ledger"
]
}List label names
GET
Returns the sorted stream-label names seen in the requested range, read from the per-segment discovery catalog./read/ns/{namespace}/loki/api/v1/labelsPath parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Query parameters
- Range start as nanoseconds, seconds, or RFC3339
startstring - Range end as nanoseconds, seconds, or RFC3339
endstring - Default lookback when start is omitted
sincestring
Responses
200Sorted stream-label namesapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid time rangeapplication/json›ErrorEnvelope
errorstringrequired
401Authentication required
Request
curl -G http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/labels \
-u reader:$PASSWORD \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z'Response · 200
{
"status": "success",
"data": [
"app",
"env",
"namespace"
]
}Find series
GET
Returns the label sets of streams matching at least one /read/ns/{namespace}/loki/api/v1/seriesmatch[] selector. Per-entry structured metadata is intentionally excluded.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Query parameters
- One or more Loki stream selectors
match[]array of stringrequired - Start of the range as RFC 3339 or Unix nanoseconds. Defaults to one hour before
startstringend. - End of the range as RFC 3339 or Unix nanoseconds. Defaults to now.
endstring - Duration before
sincestringendto use asstart, such as1h. Ignored whenstartis set.
Responses
200Matching stream label setsapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid selector or time rangeapplication/json›ErrorEnvelope
errorstringrequired
401Authentication required
Request
curl -G http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/series \
-u reader:$PASSWORD \
--data-urlencode 'match[]={app="checkout"}' \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z'Response · 200
{
"status": "success",
"data": [
{
"app": "checkout",
"env": "prod"
}
]
}Find series (form)
POST
Returns the label sets of streams matching at least one /read/ns/{namespace}/loki/api/v1/seriesmatch[] selector. Per-entry structured metadata is intentionally excluded.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Form fields
application/x-www-form-urlencoded- One or more Loki stream selectors
match[]array of stringrequired - Start of the range as RFC 3339 or Unix nanoseconds. Defaults to one hour before
startstringend. - End of the range as RFC 3339 or Unix nanoseconds. Defaults to now.
endstring - Duration before
sincestringendto use asstart, such as1h. Ignored whenstartis set.
Responses
200Matching stream label setsapplication/json›LokiEnvelope
dataobjectrequiredstatusstringrequired
400Invalid selector or time rangeapplication/json›ErrorEnvelope
errorstringrequired
401Authentication required
Request
curl -X POST "http://logs-reader.telemetry:3100/read/ns/default/loki/api/v1/series" \
-u reader:$PASSWORD \
--data-urlencode 'match[]={app="checkout"}' \
--data-urlencode 'start=2026-10-08T12:00:00Z' \
--data-urlencode 'end=2026-10-08T13:00:00Z'Response · 200
{
"status": "success",
"data": [
{
"app": "checkout",
"env": "prod"
}
]
}Resource
ingest
Write routes, served by writers. Requests are forwarded to the owning shard over internal gRPC.Elasticsearch handshake
GET
Version handshake that Elasticsearch shippers perform before sending bulk requests./write/ns/{namespace}/elasticsearchPath parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Responses
200Elasticsearch version handshakeapplication/json
Request
curl http://logs-writer.telemetry:3100/write/ns/default/elasticsearch \
-u writer:$PASSWORDResponse · 200
{}Elasticsearch bulk
POST
Elasticsearch /write/ns/{namespace}/elasticsearch/_bulk_bulk NDJSON for Fluent Bit, Fluentd, Vector, Logstash, and Filebeat. index and create actions are ingested; update and delete fail per item because logs are append-only.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Query parameters
- Comma-separated message fields; overrides configuration
_msg_fieldstring - Timestamp field; overrides configuration
_time_fieldstring - Comma-separated document fields promoted to stream labels
_stream_fieldsstring
Request body
application/x-ndjsonindex and create actions are ingested; update and delete fail per item.Responses
200Per-item bulk resultsapplication/json›BulkResponse
- True when at least one item failed.
errorsbooleanrequired - One
itemsarray of objectrequired{action: {_index, _id, status, ...}}object per bulk action, in request order. tookinteger · int64requiredMinimum 0
400Malformed bulk bodyapplication/json›ErrorEnvelope
errorstringrequired
413Request exceeds configured limit
Request
curl -X POST "http://logs-writer.telemetry:3100/write/ns/default/elasticsearch/_bulk?_stream_fields=kubernetes.namespace%2Capp" \
-u writer:$PASSWORD \
-H 'Content-Type: application/x-ndjson' \
--data-binary $'{"index":{"_index":"logstash-2026.10.08"}}\n{"@timestamp":"2026-10-08T13:00:00Z","message":"payment declined","app":"checkout"}\n'Response · 200
{
"took": 3,
"errors": false,
"items": [
{
"index": {
"_index": "logstash",
"_id": "1",
"status": 201
}
}
]
}Elasticsearch health
GET
Health handshake for shippers that check cluster health. Always reports /write/ns/{namespace}/elasticsearch/_cluster/healthgreen.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Responses
200Always greenapplication/json
Request
curl http://logs-writer.telemetry:3100/write/ns/default/elasticsearch/_cluster/health \
-u writer:$PASSWORDResponse · 200
{}Elasticsearch bulk (index)
POST
Same as /write/ns/{namespace}/elasticsearch/{index}/_bulk_bulk, with a default index for actions that omit _index.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it. - Default index for actions without
indexstringrequired_index
Query parameters
- Document field to use as the log line for this request.
_msg_fieldstring - Document field holding the entry timestamp for this request.
_time_fieldstring - Comma-separated document fields to promote to stream labels for this request.
_stream_fieldsstring
Request body
application/x-ndjsonResponses
200Per-item bulk resultsapplication/json›BulkResponse
- True when at least one item failed.
errorsbooleanrequired - One
itemsarray of objectrequired{action: {_index, _id, status, ...}}object per bulk action, in request order. tookinteger · int64requiredMinimum 0
400Malformed bulk bodyapplication/json›ErrorEnvelope
errorstringrequired
413Request exceeds configured limit
Request
curl -X POST "http://logs-writer.telemetry:3100/write/ns/default/elasticsearch/logstash-2026.10.08/_bulk?_stream_fields=kubernetes.namespace%2Capp" \
-u writer:$PASSWORD \
-H 'Content-Type: application/x-ndjson' \
--data-binary $'{"create":{}}\n{"@timestamp":"2026-10-08T13:00:00Z","message":"payment declined","app":"checkout"}\n'Response · 200
{
"errors": false,
"items": [
{}
],
"took": 0
}Push logs
POST
Loki push API. Accepts /write/ns/{namespace}/loki/api/v1/pushapplication/json or Snappy-compressed application/x-protobuf. Point Promtail, Grafana Alloy, or any Loki client at this route.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Request body
application/jsonapplication/x-protobufstreamsarray of LokiStreamrequired›Show child attributesHide child attributes
- Stream labels.
streammap of stringrequired - Loki
valuesarray of array of objectrequired[timestamp_ns, line, optional_metadata]tuples.
Responses
204Logs accepted400Invalid push requestapplication/json›ErrorEnvelope
errorstringrequired
413Request exceeds configured limit
Request
curl -X POST "http://logs-writer.telemetry:3100/write/ns/default/loki/api/v1/push" \
-u writer:$PASSWORD \
-H 'Content-Type: application/json' \
--data-binary @- <<'EOF'
{
"streams": [
{
"stream": {
"app": "checkout",
"env": "prod"
},
"values": [
[
"1791464400000000000",
"level=error msg=\"payment declined\" duration=412ms",
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
]
]
}
]
}
EOFOTLP logs
POST
OTLP/HTTP /write/ns/{namespace}/otlp/v1/logsExportLogsServiceRequest, protobuf or JSON by Content-Type. Resource attributes become stream labels per the OTLP mapping; the rest is structured metadata.Path parameters
- Tenant namespace. A
namespacestringrequiredNamespaceAuthenticationfor this database must grant the caller access to it.
Request body
application/jsonapplication/x-protobufResponses
200OTLP logs accepted400Invalid OTLP requestapplication/json›ErrorEnvelope
errorstringrequired
413Request exceeds configured limit
Request
curl -X POST "http://logs-writer.telemetry:3100/write/ns/default/otlp/v1/logs" \
-u writer:$PASSWORD \
-H 'Content-Type: application/json' \
--data-binary @- <<'EOF'
{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "checkout"
}
}
]
},
"scopeLogs": [
{
"logRecords": [
{
"timeUnixNano": "1791464400000000000",
"severityText": "ERROR",
"body": {
"stringValue": "payment declined"
}
}
]
}
]
}
]
}
EOFResource
operations
Probes for Kubernetes and load balancers.Liveness
GET
Returns /-/healthy200 while the process is running. Use it as the Kubernetes liveness probe; it is unprefixed and not namespace-scoped.Responses
200Process is healthy
Request
curl http://logs-reader.telemetry:3100/-/healthyReadiness
GET
Returns /-/ready200 once every storage shard assigned to this pod is open, and 503 while shards are still being acquired or handed off. Use it as the readiness probe.Responses
200Server is ready503Assigned shards are not ready
Request
curl http://logs-reader.telemetry:3100/-/ready