Back to buildstats

DOCS

API

Send metrics from GitLab CI, Jenkins, scripts or agents with one curl call and a bs_ API key, page through history with the read API, and link SVG charts and badges.

Updated October 7, 2026

01

API keys

Outside GitHub Actions, push with a project API key. A key starts with bs_, is shown once, pushes to one project and can't read anything. A project can have 20 active keys. In GitHub Actions, pass a key to the step's token input instead of using OIDC.

For CI outside GitHub, create a project at buildstats.io/claim under Any other CI. It lives at buildstats.io/p/name and comes with its first key. Admins add keys to any project in the Keys tab of its settings.

02

Push metrics

Send a POST to /api/v1/push with the key as a bearer token:

Terminal
curl https://buildstats.io/api/v1/push \  -H "Authorization: Bearer $BUILDSTATS_KEY" \  -H "Content-Type: application/json" \  --data @- <<EOF{  "metrics": [    { "name": "bundle_size", "value": 42.8, "unit": "kB", "better": "lower" },    { "name": "coverage", "value": 94.2, "unit": "%", "better": "higher" }  ],  "meta": { "ref": "main", "sha": "$(git rev-parse HEAD)" }}EOF
metrics
1 to 50 objects with name and value, plus optional unit and better (higher or lower). Names follow the metric format.
meta.ref
A branch like main, or a full ref like refs/pull/12/merge to record on a pull request. Defaults to the project's default branch.
meta.sha
The commit. Without it every push counts as its own commit, in push order. Pushing a metric again for the same commit, ref, meta.run_id and meta.run_attempt replaces its value.
meta.base_ref
Branch to compare against. Defaults to the project's default branch.
meta.message
Commit message, cut at 256 characters, and meta.committed_at, an ISO 8601 time. Both show on the project page.
meta.variant
Stores every metric of the push as name/variant.

Request bodies are capped at 64 KiB.

03

The response

A 200 carries project_url, locked, and one entry per metric in results:

stored
Whether the value was saved. When it wasn't, error says why.
previous
The last value on the same ref from an earlier commit.
base
The value on the base branch, with the change against it in delta and delta_pct.
verdict
better, worse or same, for metrics marked higher or lower.
chart_url
The metric's chart, and its badge in badge_url.

error is one of these:

metric_full
The series on this ref already holds 50,000 points.
metric_limit
The project already has 100 metrics.
project_limit
The owner already has 100 public or 25 private projects.
locked
The private project's trial or plan has ended, see locked projects.
duplicate_in_run
The same Action run already stored a different value for this metric.
ignored_ref
The run is not recorded, like a merge queue run.
04

Read the history

Public projects can be read without a key, private projects only by signed-in members. {slug} is owner/repo, or p/name for projects outside GitHub.

/api/v1/projects/{slug}
The project with every metric, its latest value, a sparkline and image URLs.
.../refs
Branches and pull requests with data.
.../metrics/{name}/points
Values of one metric on ref. A base name returns every variant.
.../commits
Commits on ref with the value of every metric.
.../pulls
Pull requests, and .../pulls/{number} compares one with its base.

Lists come newest first, in pages. n sets the page size, for points 50 by default and up to 1,000. Pass the response's next_before as before for the next page.

Terminal
curl "https://buildstats.io/api/v1/projects/acme/orbit/metrics/bundle_size/points?ref=main&n=100"
05

Charts and badges

/{slug}/{metric}.svg
History chart. ref picks the branch, the default branch unless set. n is the number of commits: 30, 50, 100, 200, 500, 1000, default 200. w is the width in pixels: 400, 600, 800, 1000, 1200, default 800. theme is light or dark.
/{slug}/{metric}/badge.svg
The latest value on the default branch.
/{slug}/{metric}/badge.json
The same badge as a Shields endpoint, for img.shields.io/endpoint?url=....

Public images are cached for 5 minutes. Images of private projects need the project's t token, see private images.

06

Errors and limits

Errors are JSON with an error code like invalid_request, a readable message, and a request_id to quote when you report a problem. Validation errors list up to 10 issues, and a 429 carries retry_after and a Retry-After header in seconds.

Metrics per push50
Request body64 KiB
Metrics per project100
Points per metric and ref50,000
Active API keys per project20
Pushes60 a minute per project, and 60 a minute per IP for key pushes
Read API120 requests a minute per IP
Images600 requests a minute per IP, counted per project for GitHub's image proxy
Still have a question?Talk to us