Quickstart
This walkthrough reads back the run from the Ingestion API quickstart: it lists the project’s runs, finds the ones with a low final loss, and fetches the loss curve.
1. Authenticate
Section titled “1. Authenticate”export METRANA_API_KEY="…" # portal user menu → Get API tokenexport METRANA_URL=$(printf '%s' "$METRANA_API_KEY" | tr '_-' '/+' | base64 -d | jq -r .url)
export ACCESS_TOKEN=$(curl -s -X POST "$METRANA_URL/api/management/v1/tokens/exchange" \ -H "Content-Type: application/json" \ -d "{\"api_token\": \"$METRANA_API_KEY\"}" | jq -r .access_token)
q() { # q <route> <json body> curl -s -X POST "$METRANA_URL/api/query/v1/$1" \ -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d "$2"}2. List runs
Section titled “2. List runs”q runs:list '{ "workspace": "my-workspace", "project": "api-quickstart", "attributePaths": ["config/optimizer", "sys/creation_time"], "seriesSummaries": ["train/loss():last", "train/loss():min"], "orderBy": "attr(sys/creation_time) DESC", "limit": "20"}'{ "runs": [ { "name": "run-001", "experiment": "baseline", "attributes": [ {"path": "config/optimizer", "stringValue": "adamw"}, {"path": "sys/creation_time", "timestampValue": "2026-09-30T09:00:00Z"} ], "seriesSummaries": [ {"seriesSummary": "train/loss():last", "value": 1.21}, {"seriesSummary": "train/loss():min", "value": 1.21} ] } ], "totalCount": "1"}seriesSummaries computes a statistic of a series for every run — here the last and minimum loss. The strings are
series summary selectors: a metric name, options in
parentheses (none here), and functions after the colon.
3. Filter and sort by a metric
Section titled “3. Filter and sort by a metric”Filters and orderings use the same selectors:
q runs:list '{ "workspace": "my-workspace", "project": "api-quickstart", "filter": "attr(config/optimizer) == \"adamw\" AND train/loss():last < 1.5", "orderBy": "train/loss():min ASC"}'See Resource filters for every operator.
4. Fetch the loss curve
Section titled “4. Fetch the loss curve”q float-series:list '{ "workspace": "my-workspace", "project": "api-quickstart", "runNames": ["run-001"], "seriesQueries": ["train/loss()"], "includeTimestamps": true}'{ "runPoints": [{ "run": "run-001", "series": [{ "definition": {"regular": {"metricName": "train/loss", "scale": "ML_STEP"}}, "points": { "steps": ["0", "1", "2", "3", "4"], "values": [2.31, 1.87, 1.52, 1.34, 1.21], "timestampMillis": ["1759233600000", "1759233600000", "1759233600000", "1759233600000", "1759233600000"] } }] }]}Points come back columnar: steps[i], values[i] and timestampMillis[i] describe one point. For long series
you would page with resumeToken, or ask for chart-ready buckets instead — see
Reading series.
The same call over gRPC
Section titled “The same call over gRPC”grpcurl -import-path libs/models/proto -proto query/v1/svc/api.proto \ -H "authorization: Bearer $ACCESS_TOKEN" \ -d '{"workspace": "my-workspace", "project": "api-quickstart", "seriesSummaries": ["train/loss():last"]}' \ "${METRANA_URL#https://}:443" metrana_protobuf.query.v1.svc.QueryService/ListRunsimport grpcfrom metrana_protobuf.query.v1.svc import api_pb2, api_pb2_grpc
channel = grpc.secure_channel( "app.metrana.ai:443", grpc.ssl_channel_credentials(), options=[("grpc.max_receive_message_length", 128 * 1024 * 1024)], # series pages can be large)stub = api_pb2_grpc.QueryServiceStub(channel)
response = stub.ListRuns( api_pb2.ListRunsRequest( workspace="my-workspace", project="api-quickstart", series_summaries=["train/loss():last"], order_by="train/loss():last ASC", ), metadata=(("authorization", f"Bearer {access_token}"),),)for run in response.runs: print(run.name, [(s.series_summary, s.value) for s in run.series_summaries])Next steps
Section titled “Next steps”- Query language — selectors, filters and orderings in full.
- Discovering data — find out which metrics and attributes a project has.
- Reading series — points, buckets, histograms and scatter plots.