API reference

POST /api/v1/push

The only endpoint CI needs. It records one run and answers with each value against the previous run.

Authentication

Send a project write token as Authorization: Bearer <token>. Tokens belong to one project, several can be active at once so you can rotate, and any of them can be revoked. A token is shown once, when it is created.

Request

POST https://app.driftmetrics.watch/api/v1/push
FieldTypeRequiredConstraintsDescription
tagsobjectyes1–12 keys, slug-safeThe run's coordinates: they identify it and are what you filter, group and compare on.
timestampstringnoRFC 3339, with an offsetSeries time of the run. Defaults to now; set it to backfill.
metaobjectnoat most 30 keys, any JSON valueFree-form provenance — commit, CI URL. Never identifies a run.
metricsarrayyes1–200, names unique per pushEvery number this run measured, pushed together.

metrics[]

FieldTypeRequiredConstraintsDescription
namestringyesslug-safe · ≤120 charsMetric name; the project defines a metric on first sight.
valuenumberyesfiniteThe measured number.
unitstringnofree text · ≤20 charsDisplay unit, pinned on first sight — MB, KB, ms, %.
directionstringnolower_is_better | higher_is_betterWhich way is good; pinned on first sight.

Example

curl -X POST https://app.driftmetrics.watch/api/v1/push \
  -H "Authorization: Bearer $DRIFT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "tags": {"version":"v2.5.0","platform":"linux-x64","build":"release"},
  "metrics": [
    {"name":"binary_size","value":2.14,"unit":"MB","direction":"lower_is_better"},
    {"name":"peak_ram","value":419,"unit":"KB","direction":"lower_is_better"},
    {"name":"latency_ms","value":41.6,"unit":"ms","direction":"lower_is_better"}
  ],
  "meta": {"commit":"9f2c1ab","ci":"https://github.com/acme/app/actions/runs/812"}
}'

Response

200 with the run's tags, the axis value it was compared against, and one delta per metric. comparedTo is null for the first run of a series.

{
  "tags": {"version":"v2.5.0","platform":"linux-x64","build":"release"},
  "comparedTo": "v2.4.0",
  "deltas": [
    {"metric":"binary_size","value":2.14,"previous":1.93,"deltaPct":10.88},
    {"metric":"peak_ram","value":419,"previous":412,"deltaPct":1.7},
    {"metric":"latency_ms","value":41.6,"previous":42.1,"deltaPct":-1.19}
  ]
}

Semantics

  • A push carries the whole run. Pushing the same tags again replaces that run's measurements, and a metric left out is dropped from it.
  • A metric's unit and direction are pinned the first time the project sees it; later pushes keep the first values. Direction defaults to lower_is_better.
  • A tag key's value type — text or number — is pinned on first sight too. Mixing types later is rejected.
  • timestamp is optional and defaults to now. Send it to backfill history at the right point.

Errors

{
  "error": "Invalid request body",
  "details": [
    {"path":"metrics.0.value","message":"expected number"}
  ]
}
StatuserrorWhen
400Invalid request bodyA field failed validation; details names the path.
400Invalid JSON bodyThe body is not JSON.
401Invalid or missing project tokenNo bearer token, or one that is unknown or revoked.
413Request body too largeOver 100 KB.
500Internal server errorUnexpected failure; nothing was written.

details is present only on a validation failure.