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
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| tags | object | yes | 1–12 keys, slug-safe | The run's coordinates: they identify it and are what you filter, group and compare on. |
| timestamp | string | no | RFC 3339, with an offset | Series time of the run. Defaults to now; set it to backfill. |
| meta | object | no | at most 30 keys, any JSON value | Free-form provenance — commit, CI URL. Never identifies a run. |
| metrics | array | yes | 1–200, names unique per push | Every number this run measured, pushed together. |
metrics[]
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| name | string | yes | slug-safe · ≤120 chars | Metric name; the project defines a metric on first sight. |
| value | number | yes | finite | The measured number. |
| unit | string | no | free text · ≤20 chars | Display unit, pinned on first sight — MB, KB, ms, %. |
| direction | string | no | lower_is_better | higher_is_better | Which 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
unitanddirectionare pinned the first time the project sees it; later pushes keep the first values. Direction defaults tolower_is_better. - A tag key's value type — text or number — is pinned on first sight too. Mixing types later is rejected.
timestampis 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"}
]
}| Status | error | When |
|---|---|---|
| 400 | Invalid request body | A field failed validation; details names the path. |
| 400 | Invalid JSON body | The body is not JSON. |
| 401 | Invalid or missing project token | No bearer token, or one that is unknown or revoked. |
| 413 | Request body too large | Over 100 KB. |
| 500 | Internal server error | Unexpected failure; nothing was written. |
details is present only on a validation failure.