Skip to content

Transports and encoding

The Ingestion and Query APIs are defined in protobuf and served two ways from the same host. This page applies to both.

Ingestion Query
Target app.metrana.ai:443 (TLS) app.metrana.ai:443 (TLS)
Service metrana_protobuf.ingestion.v1.svc.IngestService metrana_protobuf.query.v1.svc.QueryService
Definition ingestion/v1/svc/api.proto query/v1/svc/api.proto

Self-hosted deployments use their own host (the url inside your API token). The .proto files are under libs/models/proto in the Metrana source; import paths are relative to that directory (for example common/v1/series.proto). Python users can pip install metrana-protobuf for generated messages and stubs.

  • Authentication: the authorization metadata key, Bearer <access token>. See Authentication.
  • Message size: the servers accept requests up to 4 MiB. Query responses can be much larger than 4 MiB — raise your client’s maximum receive size (the SDK uses 128 MiB), or request smaller pages.
  • Compression: the Ingestion API accepts zstd-compressed requests.
  • Health: both servers implement the standard grpc.health.v1.Health service, without authentication.
  • gRPC-Web: the Query API also accepts gRPC-Web, for browser clients.
  • Reflection is not enabled: give tools such as grpcurl the .proto files.

Each unary RPC is also served as POST with a JSON body:

API Base path Example
Ingestion https://app.metrana.ai/api/ingest/v1 POST /api/ingest/v1/runs:update
Query https://app.metrana.ai/api/query/v1 POST /api/query/v1/runs:list

The body is the RPC’s request message in JSON, and a success response (200) is the response message in JSON. Each RPC’s route is listed on its reference entry. Streaming RPCs (InitializeRunLogging, UpdateRunStream) have no REST route. Send Content-Type: application/json and the Authorization: Bearer … header.

REST bodies use the standard protobuf JSON mapping:

Protobuf JSON Example
Field names lowerCamelCase in responses; requests accept lowerCamelCase or the original snake_case runName (or run_name)
int64, uint64 string (requests also accept numbers) "firstStep": "1000"
int32, uint32 number "ordinal": 0
double, float number; see non-finite values "value": 0.25
bool true / false
bytes standard base64 string "runId": "AAAAAAAAAAAAAAAAAAAAAQ=="
enum value name as a string (requests also accept the number) "ordering": "ORDERING_MODE_ABSOLUTE_ORDERED"
google.protobuf.Timestamp RFC 3339 string "creationTime": "2026-09-30T09:00:00Z"
map<K, V> object "labels": {"gpu": "0"}
repeated array "values": [0.41, 0.40]
message object "run": {…}
oneof the chosen member’s field, alone {"path": "lr", "floatValue": 0.001}

Responses omit fields that hold their default value: 0, false, "", empty arrays and maps, and unset messages. A missing field means “default”, not “absent from the schema”. Fields declared optional are the exception: they appear whenever set, even to a default value, so their absence means “not set”.

Unknown fields in a request are rejected.

Requests may send NaN, Infinity and -Infinity as the strings "NaN", "Infinity" and "-Infinity". Responses write a non-finite double as null. Where telling them apart matters — NaN points in float series, for example — use gRPC, or the fields the Query API provides for it (such as bucketsWithNans on bucketed series).

  • REST needs nothing beyond an HTTP client, works in any language, and is easy to debug with curl.
  • gRPC gives generated, typed clients, smaller messages, compression and HTTP/2 multiplexing — and is the only way to use the streaming ingestion path.

Both run the same validation and return the same data.