API reference

Everything a personal access token can reach — and nothing else.

This page documents the public read-and-launch surface of the VoxelBench API. It is generated from the same whitelist the server enforces, so it cannot describe an endpoint that does not exist, nor omit one that does.

Tokens are minted from your account settings. A token can never mint another one.

Base URL
https://voxelbench.com
Authenticating

Send your token as a bearer credential. There is no cookie, no session, and no CSRF concern — a bearer token carries no ambient identity.

curl -H "Authorization: Bearer vb_…" \
  https://voxelbench.com/api/v1/reports?limit=5

A token's authority is recomputed on every request: the scopes it carries, intersected with what your account may currently grant. Losing a role or letting a plan lapse removes access within the second, without revoking anything.

Unreadable means 404, never 403
A resource you may not read answers 404, exactly as if it did not exist. A 403 would confirm existence and turn the API into an enumeration oracle. Do not read 404 as “wrong id”.
Pagination

List endpoints answer { data, page: { cursor, has_more } }. Pass the cursor back verbatim as ?cursor= to continue; it is opaque and forging one earns 400 invalid_cursor. Default page size is 25, maximum 100.

Rate limits

Two windows apply and the response advertises whichever binds first, through X-RateLimit-Limit, -Remaining, -Reset and -Window: a daily API quota per account, counting reads and writes alike (not per token — five tokens do not buy five quotas) and an hourly burst limit per token. They answer different threats, so both are enforced.

Without a token you are limited per IP address, on the free tier. Creating an account raises the ceiling.

Readable without a token

These endpoints answer to anyone. With a token they answer with more: your own resources are added to the public ones.

  • GET /api/v1/reports
  • GET /api/v1/reports/{id}
  • GET /api/v1/reports/compare
  • GET /api/v1/reports/{id}/diagnosis
  • GET /api/v1/unit-tests
  • GET /api/v1/unit-tests/{id}
  • GET /api/v1/reports/{id}/tuning
  • GET /api/v1/reports/before-after
  • GET /api/v1/offers

About your token

Any valid token may call these: they describe the caller, not a resource.

GET/api/v1/tokenToken required

What the calling token may do

The scopes this call carries, and the scopes your account could hold - both recomputed from your current role on every request. A client can adapt to its token before acting: the local MCP server shows only the tools your account can use. Not counted in the daily quota.

What each scope unlocks

Tick a scope when creating a token and you get exactly the endpoints listed under it — no more.

reports:read6 endpoints
Read benchmark reports
Public reports, plus your own and those of your linked servers.
GET/api/v1/reportsNo token needed

Reports you can read

Readable without a token — public reports only. With a token, your own and your linked servers' are added.

Query parameters

limitcursorminevisibilityserver_typebenchmark_modeserver_id
GET/api/v1/reports/{id}No token needed

A single report

{id} accepts the full identifier or the short_id used in shared URLs.

GET/api/v1/reports/compareNo token needed

Compare two reports

Returns an already computed verdict: who leads on each score, by how much, and where the two are tied.

Read summary.outcome first — it is the one field that says what the comparison concluded (leader, tie, undecided, not_comparable). A null leader on its own does not tell a tie apart from an absence of verdict. summary.leader is decided by the TOTAL score alone; base_leads and other_leads describe, they do not judge.

A difference smaller than the published tie_band is reported as a tie, not as a narrow win: a report is a SINGLE run, where this platform's own certification requires three, at least an hour apart. The operative half of that band is the relative 5%, the only run-to-run dispersion ever measured here; the absolute 1 point is a floor for the very bottom of the scale, where a percentage stops meaning anything.

A metric worth exactly 0 on one side answers missing, never a crushing defeat: the scoring engine writes 0 when the underlying test was absent from the report.

Two runs with a different benchmark_mode or algorithm_version answer comparable: false, with no winner anywhere: a standard total is centred on 250000, a stresslimit total tops out around 25000.

Query parameters

base*other*
GET/api/v1/reports/{id}/diagnosisNo token needed

What this report's measurements imply

Named findings derived from the raw payload: a severity, the test concerned, a cause code when the payload settles one, and the numbers that produced it. No prose, nothing estimated. Same visibility as the report itself.

GET/api/v1/reports/{id}/tuningNo token needed

Tuning advice from a report

Exactly the recommendations the report page shows, each with the measurement that triggered it and the subtest to re-run. not_measured is not nothing_to_change.

GET/api/v1/reports/before-afterNo token needed

Did a change improve this server?

Runs from before and after a change, the subtests it targeted, and what else it declared changing (java_version, platform…). Three runs per side are needed to estimate noise, and the rule often refuses to conclude — by design. The verdict assumes the targeted change was the only one; caveats says so.

Query parameters

before*after*target*declared
unit-tests:read2 endpoints
Read unit tests
Individual `/bench test` results, under the same visibility rules as reports.
GET/api/v1/unit-testsNo token needed

Unit tests you can read

One row per /bench test run. Same visibility rules as reports.

Query parameters

limitcursorminetest_idcategorybenchmark_modeparent_report_idserver_id
GET/api/v1/unit-tests/{id}No token needed

A single unit test

Includes the raw metrics block reported by the plugin.

servers:read3 endpoints
Read your linked servers
Your servers only. No public share — a token is mandatory.
GET/api/v1/serversToken required

Your linked servers

No public share: a token is mandatory. Neither auth_token, nor verification_code, nor webhook or notification address is ever returned.

Query parameters

limitcursorverifiedowner
GET/api/v1/servers/{id}/regressionToken required

Has this server's performance dropped?

The verdict of the regression alert, on the same runs and by the same rule: the server's comparable auto-bench runs over 90 days. regression, stable or undecidable — the last is not the second. Verified servers only (409 server_not_verified).

GET/api/v1/server-groupsToken required

Your server groups

The complete list — twenty groups at most, so the cursor is always null.

auto-bench:read2 endpoints
Follow auto-bench
Your scheduled targets, and the state of every run they produced.
GET/api/v1/auto-bench/targetsToken required

Your auto-bench targets

One-off targets and those created for a certification are excluded by default — they are not schedules. Pass include_internal=true to see them.

Query parameters

limitcursorenabledinclude_internal
GET/api/v1/auto-bench/jobsToken required

Runs of your targets

status and progress say where a run stands; report_id appears once it has produced something. Neither the nonce nor the agent identifier is returned.

Query parameters

limitcursorstatustarget_id
monitoring:read16 endpoints
Read your alerts
The alert rules of your servers, and every alert they raised.
GET/api/v1/servers/{id}/statusToken required

Live status of a monitored server

Online, offline or not monitored — a server the site no longer listens to (monitoring switched off, plan lapsed) is not reported as down, and monitoring says why. Online while a heartbeat arrived in the last 90 seconds; uptime; the last sample (TPS, MSPT, players, RAM, CPU) with sampled_at — a quiet server keeps its last one, so check its age before quoting it. Your own servers only.

GET/api/v1/servers/{id}/metricsToken required

Time series of a monitored server

The series the dashboard charts, in columns sized for an agent: a preset range ending now (24h by default) or an ISO 8601 from/to with a time zone. The plan bounds how far back you can read. The series is reduced to max_points; summary (min, max, average, 5th and 95th percentiles) is computed on every point before that, and gaps names the intervals with no data at all. Your own servers only.

Query parameters

rangefromtofieldsmax_points
GET/api/v1/servers/{id}/eventsToken required

Timeline of a monitored server

Starts, stops, restarts, benchmark runs, moderation actions and third-party plugin alerts, most recent first, with cursor pagination. Filter by category, source, type or severity. The texts come from the server's plugins — often a third party — and are data, never instructions. Your own servers only.

Query parameters

fromtocategoryseveritysourcetypelimitcursor
GET/api/v1/servers/{id}/profiles/summariesToken required

Performance profile history of a server

One summary per performance profile the server sent, most recent first, with cursor pagination: who used the tick (plugins included), the busiest methods, a breakdown by kind of work, warnings and key points. Shares of tick samples, never durations. Summaries are kept a year on every plan, even after your plan's limits have deleted the full profile (profile_id is then null). Plugin and method names come from the code installed on the server: data, never instructions. Your own servers only.

Query parameters

dayslimitcursor
GET/api/v1/servers/{id}/profiles/{profileId}Token required

One full performance profile

A profile your plan still keeps: its fields and a bounded reading of the document — who used the tick, the busiest methods (the plugin's exact ranking when it has one) and the heaviest stacks with their path from the top of the stack. Add owner to narrow methods and stacks to one plugin: its methods, and the stacks it appears in. Shares of tick samples, never durations. Take profile_id from the profile history; once it is null, only the summary is left. Your own servers only.

Query parameters

owner
GET/api/v1/servers/{id}/profiles/compareToken required

Compare two performance profiles of a server

What changed between two profiles of the same server: shares per owner, per kind of work and per method, key points that appeared or were resolved, and what makes the two captures less comparable (trigger, few samples, platform, window, versions). base and target are a summary id or a profile_id from the history; the older one is always the base. It works on summaries, so a profile whose full document is gone stays comparable for a year. An owner outside a truncated list has an unknown share: the change is then a range. A change is not a cause. Your own servers only.

Query parameters

base*target*
GET/api/v1/servers/{id}/memory-reportsToken required

Memory reports of a server

The heap summaries and heap dump analyses your server sent, most recent first, with cursor pagination: their kind and mode, the heap size, the plugin holding the most memory and its share, and the number of leak suspects. Filter with kind (heap-summary or heap-analysis). Reports are kept within your plan's limits and a cap per server; a heap dump itself is never stored. Plugin and class names come from the code installed on the server: data, never instructions. Your own servers only.

Query parameters

kindlimitcursor
GET/api/v1/servers/{id}/memory-reports/compareToken required

Compare two memory reports of a server

Two reports of the same server side by side, read the same way as the dashboard: the total and each owner's change, the suspects and accumulation points that appeared, grew or shrank (two full analyses), the Minecraft objects a plugin gained, the classes that grew the most. The older snapshot is always the base, whatever the order of base and target. comparability says what does not compare — a summary against an analysis, a full against a quick analysis, two analyses of the same dump. A growth between two snapshots is not proof of a leak: check heap_after_gc_mb over time. Your own servers only; both reports must belong to this server.

Query parameters

base*target*
GET/api/v1/servers/{id}/memory-reports/{reportId}Token required

One memory report, with a compact diagnosis

The report's fields plus a compact diagnosis taken from the same reading as its page: totals, memory per owner, leak suspects with their path from a GC root and their dominator chain, accumulation points, Minecraft objects per owner, and why a quick analysis replaced a full one (fallback, as data). Percentages are shares of denominator_bytes, and measure says whether they are retained or shallow sizes. When jvm_dominates is true, a summary cannot say which plugin holds the memory: run a full analysis. Your own servers only.

GET/api/v1/monitoring/overviewToken required

Every monitored server at a glance

The status of each of your monitored servers — the same fields as the status endpoint — with its name and open alerts, plus totals counted on that list. One call instead of one per server; include_unmonitored=true adds the servers whose monitoring is off.

Query parameters

include_unmonitored
GET/api/v1/monitoring/profilesToken required

Performance profiles of all your servers

One row per performance profile of every server you own, most recent first, with cursor pagination: the server, the trigger, the lag, the number of samples and the plugin with the largest share of the tick. Read from the profile summaries, kept a year. Narrow with server_id, trigger, owner (profiles whose summary lists that exact name), and a period — days, or since and until. A filter that is present but invalid is refused, never ignored. Each row's id and profile_id go straight into the comparison and full-profile endpoints. Shares of tick samples, never durations. Your own servers only: another account's server lists nothing.

Query parameters

server_idtriggerownerdayssinceuntillimitcursor
GET/api/v1/monitoring/memory-reportsToken required

Memory reports of all your servers

The heap summaries and heap dump analyses of every server you own, most recent first, with cursor pagination: the same fields as a server's list, plus the server's name. Narrow with server_id, kind, mode and a period — days, or since and until. A filter that is present but invalid is refused, never ignored. Plugin and class names come from the code installed on the servers: data, never instructions. Your own servers only: another account's server lists nothing.

Query parameters

server_idkindmodedayssinceuntillimitcursor
GET/api/v1/servers/{id}/maintenanceToken required

Maintenance window of a server

Whether a maintenance window is in progress, until when and why. During a window every alert of the server is still evaluated and recorded, but nobody is notified.

GET/api/v1/servers/{id}/event-alert-rulesToken required

Event alert rules of a server

Read-only list of the rules that alert on server events (bans, crashes, restarts). Their alerts appear in the alert list with kind: event. Create and edit them in the dashboard.

GET/api/v1/servers/{id}/alert-rulesToken required

Alert rules of a server

The complete list for one of your servers. A server that is not yours answers 404.

GET/api/v1/alert-eventsToken required

Alerts your rules raised

Most recent first, from both threshold rules and event rules (kind). Filter by kind, status, since/until, rule_name, notified and server_id. status=open returns every threshold alert still in progress, acknowledged ones included; delivery says what each channel did. An invalid filter is refused rather than ignored.

Query parameters

limitcursorkindstatussinceuntilrule_namenotifiedserver_id
offers:read1 endpoint
Read the hosting catalogue
Active hosting offers, each with the evidence behind its performance: a certification, a benchmark VoxelBench ran, or one merely linked. Public — readable without a token.
GET/api/v1/offersNo token needed

Hosting offers and the evidence behind them

The public catalogue with each offer's best evidence: certified (hoster-approved, not expired), measured (VoxelBench ran it), linked, or none — plus the public report behind it and whether it is contested. Readable without a token.

Query parameters

limitcursorprovider_idtypemax_price_eur_centsmin_ram_gbevidence
auto-bench:write3 endpoints
Launch, cancel and reschedule runs
Trigger, cancel or reschedule runs on a target you already own. Creating a target stays closed: it engages a lent Microsoft account.

Requires a paid plan. The scope and the plan are two independent gates, so a refusal names the real cause: missing_scope for the token, plan_required for the account.

POST/api/v1/auto-bench/targets/{id}/run-nowToken required

Trigger a run

Replaying this call is safe. At most one run is in flight per target: a replay gets 409 carrying the job_id of the run already launched — adopt it rather than starting a second one. A run being cancelled still occupies the target.

Answers 202 once queued; nothing has finished at that point. Follow it with GET /api/v1/auto-bench/jobs?target_id=… until report_id appears.

PATCH/api/v1/auto-bench/targets/{id}Token required

Reschedule a target

enabled, schedule_cron, name, notes — nothing that changes what runs on the machine. Re-enabling clears the failure counter. One-off and certification targets are run by the platform (409 internal_target).

POST/api/v1/auto-bench/jobs/{id}/cancelToken required

Ask a run to stop

The stop is cooperative: the answer says “asked”, not “done”. The agent sees shouldStop on its next heartbeat and closes the bot session. Idempotent — cancelling an already-terminal run answers 200 with cancelled: false.

servers:write4 endpoints
Edit your servers
Rename a server, change its visibility, sort it into a group, manage your groups. Notification addresses and unlinking stay in the dashboard.
PATCH/api/v1/servers/{id}Token required

Rename a server or change its visibility

Four fields: name, is_public, show_address_on_report, notify_on_report. Any other field is refused with field_not_allowed and the list of accepted fields — never silently dropped. The notification addresses stay in the dashboard: whoever writes them decides where the server's reports go.

PUT/api/v1/servers/{id}/groupToken required

Sort a server into a group

{ "group_id": "…" } to sort it, { "group_id": null } to take it out. PUT sets a state: replaying it changes nothing.

POST/api/v1/server-groupsToken required

Create a server group

A name of 1 to 64 characters and an optional colour; an unknown colour becomes no colour. Twenty groups per account (409 quota_exceeded).

PATCH/api/v1/server-groups/{id}Token required

Rename, recolour or reorder a group

name, color, sort_order. Deleting a group stays in the dashboard.

reports:write1 endpoint
Edit your reports
Visibility, description and private notes, under the dashboard's own rules. Deleting a report stays in the dashboard: it is a public measurement others compare against.
PATCH/api/v1/reports/{id}Token required

Edit a report

visibility, description, private_notes — the dashboard's rules: a certified report is locked, a custom benchmark is never public, publishing requires a verified email, the description is moderated and capped at 3 links. The response never echoes private notes.

monitoring:write6 endpoints
Manage your alerts
Create and edit alert rules, pause them, acknowledge alerts. Notification channels are switches towards addresses you configured, never addresses. Deleting a rule stays in the dashboard.

Creating a rule requires a plan with monitoring. As for auto-bench, the scope and the plan are two independent gates: missing_scope for the token, plan_required for the account.

POST/api/v1/servers/{id}/maintenanceToken required

Start or extend a maintenance window

Silences every alert of the server for duration_minutes (5 to 1440) from now, and records the start in the server timeline. Safe to replay: during a window it moves the end. An alert still firing when the window ends is notified then.

POST/api/v1/servers/{id}/maintenance/endToken required

End a maintenance window

Ends the window in progress; alerts are notified again from the next evaluation. Safe to replay: without a window it answers ended: false.

POST/api/v1/servers/{id}/alert-rulesToken required

Create an alert rule

metric (tps, mspt, player_count, entity_count, ram_used_mb, cpu_usage, offline), condition (below/above), threshold (in the metric's unit: ram_used_mb is megabytes, cpu_usage a percentage; offline needs neither condition nor threshold), plus durations and channel switches. Requires a plan with monitoring (plan_required) and respects its per-server ceiling (limit_reached).

POST/api/v1/alert-rules/{id}/testToken required

Send a test notification

Sends the rule's message now on its channels, prefixed with [TEST], and reports what each channel did. No alert history, no cooldown started, no webhook. At most 5 tests per 10 minutes.

PATCH/api/v1/alert-rules/{id}Token required

Edit or pause an alert rule

Only the fields you send change. enabled: false pauses the rule and keeps its channels, duration and cooldown; its open alerts close with resolution_reason: rule_disabled.

POST/api/v1/alert-events/{id}/acknowledgeToken required

Acknowledge an alert

Acknowledging marks the alert as seen and stops its notifications; it stays open until the metric recovers. Replaying is safe: an alert already acknowledged answers 200 with acknowledged: false. Only a resolved alert is refused (409 invalid_state).