Prometheus API sandbox
Point a Prometheus client at https://prom.sandboxapis.dev, a base-URL swap. Paths are /api/v1/... exactly as on a Prometheus server: prometheus-api-client takes `PrometheusConnect(url="https://prom.sandboxapis.dev")`, prometheus-query takes `endpoint`, promtool takes the server URL as its argument, and a Grafana Prometheus datasource takes it as its URL. Auth: any 'Authorization: Bearer' token, any Basic pair, or none. THE DATA ENDS AT 00:00 UTC TODAY on the live host: that instant is "now", so a query with no `time` is evaluated there and nothing after it exists. WHAT IS RENDERED: this data set's six services as metric series, one per canon series: http_request_duration_seconds{quantile="0.5"|"0.99"} (seconds), http_requests_total and http_request_errors_total (counters derived from the canon's request and error rates, starting at 0 on the window's first sample), service:http_request_errors:ratio (the error rate as a ratio) and process_cpu_utilization_ratio. Every series carries job, service, team and tier labels. The incident's spike is in service:http_request_errors:ratio at 300-second resolution inside its window; outside it the series are hourly, and the server's lookback is 2h, so use ranges of 2h or more for rate() there. GET /api/v1/rules lists one alerting rule per canon monitor (one group per team), /api/v1/alerts the alerts active at the anchor, and ALERTS / ALERTS_FOR_STATE carry every pending and firing interval, so ALERTS{alertstate="firing"} marks exactly when each canon alert fired and resolved. THE PROMQL SUBSET: selectors with =, !=, =~ and !~; range selectors; rate, increase, avg_over_time, min_over_time and max_over_time; sum, avg, min, max and count with by or without; arithmetic and comparison (with bool) against scalars; one-to-one arithmetic between vectors with identical labels. Anything else (offset, @, subqueries, topk, on(), histogram_quantile, ...) answers 400 bad_data naming the construct. WHAT IS NOT: scrape targets and pools (series arrive by remote write, so those lists are empty), exemplars, the search API (disabled, as on a server without the feature flag), runtime info and self metrics, and every write (refused: this universe is read-only). Live host only: no pinned snapshot exists for this provider yet. Check https://sandboxapis.dev/providers/prometheus for the row-by-row picture.
Coverage badge
Deep38/40 · 95%
The share of read rows with a final answer — served and verified, a deviation, retired upstream, or a reviewed empty/refusal. Computed in the coverage manifest, copied here.
prom.sandboxapis.devServed & verified
30 / 75%
Answering with real universe data, each response checked against Prometheus's published spec by the conformance suite on this build.
The full read API
40
Every read surface Prometheus publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.
Not served yet
10
Each returns an explicit, provider-shaped coverage error naming the gap — never invented data.
01 / The swap
No SDK of ours, no shim, no recorded fixtures. The same client library you already use, one environment variable different.
curl -G "https://prom.sandboxapis.dev/api/v1/query" --data-urlencode 'query=service:http_request_errors:ratio'Verified drop-in clients
prometheus-query3.6.0The versions the conformance suite drives against this host on every build — pinned in coverage/client-pins.yaml, and watched weekly for upstream releases, because a client library moving without us is how a shipped integration breaks silently.
02 / Coverage by family
All 40 rows the coverage manifest carries for Prometheus, deferred long tail included and nothing capped. Open a family, or filter by path to find the exact endpoint your client calls.
Status — what a conformance test found
Mode — what kind of answer a row gets
A row with no mode shown has not been judged yet. Modes are the manifest's own words, from coverage/MODES.yaml; every empty and refuse carries a written reason and a review date before it counts as final.
Badge — where Deep starts
REST + GraphQL + git rows that are not `excluded`. Write operations are NOT rows (DECISIONS 2026-09-01 decision 9): they are counted in meta.write_operations and never enter this ratio.
40 read surfaces in 10 families
this data set records no exemplar: exemplar storage is reported off (/features exemplar_storage false), so no sample has an exemplar attached, and Prometheus's own answer for a query with none is CAPTURED as {"status":"success","data":[]} at 200 (2026-10-23)
the POST form of the exemplar query beside it, with the same parameters and the same truthful answer: this data set records no exemplar (exemplar storage is reported off, /features exemplar_storage false), so the data is the captured empty array
every sample this replica holds is served from the head, so there is no persisted block to describe, and an empty head's answer is the CAPTURED {blocks: []} (2026-10-23)
generate — OPEN: every field this document requires is a measurement of a running Go process (goroutine count, GOMAXPROCS, the memory limit, the working directory, the host name), which this replica does not have and would have to invent. Answers the coverage 404 until a canon wave models the server process
generate — OPEN: Prometheus's own process metrics (Go runtime, TSDB internals, HTTP handler counters) are measurements of a running server this replica does not have, so serving them would invent every value. Answers the coverage 404 until a canon wave models the server process
refuse — the search API is off on this data set's server (no --enable-feature=search-api), so the release's own CAPTURED answer is the whole response: 500, errorType unavailable, "search API disabled" (2026-10-23)
refuse — the POST form of the disabled search API: the same captured 500 unavailable "search API disabled" the GET form answers
refuse — the search API is off on this data set's server (no --enable-feature=search-api), so the release's own CAPTURED answer is the whole response: 500, errorType unavailable, "search API disabled" (2026-10-23)
refuse — the POST form of the disabled search API: the same captured 500 unavailable "search API disabled" the GET form answers
this data set's series arrive by remote write (its /status/flags reports the remote-write receiver on and its configuration has no scrape job), so Prometheus has no target to list, and the release's own answer with no scrape job is CAPTURED as {activeTargets: [], droppedTargets: [], droppedTargetCounts: {}} (2026-10-23)
no scrape job is configured in this data set's Prometheus, because its series arrive by remote write, so the pool list is the CAPTURED empty {scrapePools: []} (2026-10-23)
ENTAILED by the empty target list beside it: metadata per target exists only for scraped targets, and with no scrape job the release answers the CAPTURED empty array (2026-10-23)
refuse — ENTAILED by the empty scrape pool list on the same host: no pool exists to print the configuration of, and the release's own answer is CAPTURED as 400 bad_data, "no scrapePool parameter provided" or `error retrieving scrape config: scrape pool "<name>" not found` (2026-10-23)
refuse — ENTAILED by the empty scrape pool list on the same host: no scrape pool exists, so every pool name is unknown, and the release's own answer is CAPTURED as 400 bad_data, "no scrapePool parameter provided" or `error retrieving scrape config: scrape pool "<name>" not found` (2026-10-23)
refuse — Prometheus serves the search API only with --enable-feature=search-api, and this data set's server runs without it (its /status/flags reports enable-feature empty), so the answer is the release's own, CAPTURED byte for byte: 500, errorType unavailable, "search API disabled" (2026-10-23)
refuse — the POST form of the disabled search API: the same captured 500 unavailable "search API disabled" the GET form answers, since the feature is off for both
this data set's Prometheus sends no alert to an alertmanager: paging is modelled by the incident hosts (PagerDuty's escalation policies route every page), so the discovery lists are the CAPTURED empty {activeAlertmanagers: [], droppedAlertmanagers: []} (2026-10-23)
this data set's Prometheus has raised no server notification (no failed config reload, no shutdown), and the release's answer with none is the CAPTURED empty array (2026-10-23)
03 / What's simulated
Each line below is one assertion in the conformance suite named beside it — run on every build, over one artifact, through the real renderers. Nothing is claimed here that no expect checks.
The spike in a service's error ratio is the canon's error-rate series sample for sample; ALERTS{alertstate="firing"} is 1 exactly from the canon fire time until the alert resolves, and the alert's activeAt is that fire time minus the monitor's for_sec.
The service an alert is labelled with is the project the Sentry host serves the incident's error group on and the service PagerDuty paged for, and the incident opens at the alert's fire time on both.
Grafana's Prometheus datasource is this host: its resource API and its data frames answer the same series with the same values, and its alert rules watch the series this host's alerting rules watch.
Against Grafana · parity/incident-parity.test.ts
05 / Pinned snapshots
No snapshot is pinned for Prometheus yet — the live host is the only way to reach it, and its data moves. Pin a provider you test against before you depend on exact values.
Need an endpoint that is not served yet?
Every row Prometheus's manifest carries is on this page, so “not here” is an answer rather than a gap in the rendering. Tell us which path and which client, and it moves up the queue — the order is set by what people ask for.