Documentation 40

Mock Prometheus API quickstart · 4 min read

The incident, as metric series. Compatible with the Prometheus HTTP API.

The data set's services as Prometheus series: latency, request and error counters, the error ratio and CPU, a PromQL subset that evaluates them, and one alerting rule per monitor. The spike is the one PagerDuty and Sentry serve the incident for.

Import it instead

Both files list exactly what this host serves — generated from our own coverage manifest, not from Prometheus's published specification — and carry no vendor schema: paths, methods and what each one answers, with the base URL and auth header already set. One folder holds a write, so the read-only refusal is something you can press.

Try it — a real request to prom.sandboxapis.dev

No key needed. Press Enter to send.
01Point your client at prom.
02Know where the data ends
03Find the spike and the alert
04Know what is not served

01 / Base URL + auth

A base-URL swap, nothing else.

Point your client at https://prom.sandboxapis.dev. Paths are /api/v1/… exactly as on a Prometheus server, and envelopes, error bodies, the sample encoding and the headers follow the 3.15.1 release. Auth is any Authorization: Bearer token, any Basic pair, or none at all, because Prometheus has none of its own.

request.sh
curl -G "https://prom.sandboxapis.dev/api/v1/query" --data-urlencode 'query=service:http_request_errors:ratio'
metrics.py
from prometheus_api_client import PrometheusConnect

prom = PrometheusConnect(url="https://prom.sandboxapis.dev")
print(prom.custom_query('http_request_duration_seconds{quantile="0.99"}'))
metrics.mjs
import { PrometheusDriver } from "prometheus-query";

const prom = new PrometheusDriver({ endpoint: "https://prom.sandboxapis.dev" });
// prometheus-query sends your clock as the evaluation time; pass the data's last instant instead.
const end = new Date(new Date().toISOString().slice(0, 10) + "T00:00:00Z");
console.log((await prom.instantQuery("service:http_request_errors:ratio", end)).result);

promtool query instant https://prom.sandboxapis.dev up works the same way, and so does a Grafana Prometheus data source with this URL. Using a key? Keys & rate limits The Python snippet is a verified quickstart

02 / Where the data ends

The data ends at 00:00 UTC today.

On the live host the data set's last instant is 00:00 UTC today, and that instant is "now": a query with no time is evaluated there, and so is anything relative to now. Nothing after it exists, and nothing is extrapolated past it. A client that sends its own clock (prometheus-query does, a dashboard's "last 1 hour" does) is asking about a period with no samples yet; ask for a window that ends at 00:00 UTC, or reaches back past it.

Outside the incident's window every series is hourly, so this server runs with a 2-hour lookback (/api/v1/status/flags reports query.lookback-delta: 2h): an instant query finds the latest hourly sample from any point in the hour. Give rate() and increase() a range of 2h or more there; inside the incident's window the error, latency and CPU series step every 5 minutes.

03 / The series

One canon series, one Prometheus series.

Every series carries job and service (the service's slug), team and tier. Units are converted on the way out: milliseconds become seconds and parts per million become a ratio.

series.txt
http_request_duration_seconds{quantile="0.5"}    median latency, seconds (summary)
http_request_duration_seconds{quantile="0.99"}   p99 latency, seconds (summary)
http_requests_total                              requests served (counter)
http_request_errors_total                        requests answered with an error (counter)
service:http_request_errors:ratio                error share of requests, 0 to 1 (gauge)
process_cpu_utilization_ratio                    CPU, as a ratio of one core (gauge)

04 / The spike and the alert

Find the incident from the alerts.

GET /api/v1/rules lists one alerting rule per canon monitor, grouped by team, with the monitor's threshold in the served unit and its sustained-breach time as duration. The rule manager's own series carry the history: ALERTS_FOR_STATE holds each alert's activeAt, and ALERTS{alertstate="firing"} is 1 exactly while an alert fired, from the moment the incident opened until it resolved. /api/v1/alerts lists only what is active at 00:00 UTC today.

incident.sh
# When each alert went pending (the value is activeAt, in epoch seconds)
curl -G "https://prom.sandboxapis.dev/api/v1/query" --data-urlencode 'query=ALERTS_FOR_STATE' --data-urlencode 'time=<an instant inside the incident>'

# Every alert's firing intervals over the last 90 days, one point per hour
curl -G "https://prom.sandboxapis.dev/api/v1/query_range" --data-urlencode 'query=ALERTS{alertstate="firing"}' \
  --data-urlencode "start=$(( $(date -u +%s) / 86400 * 86400 - 90 * 86400 ))" \
  --data-urlencode "end=$(( $(date -u +%s) / 86400 * 86400 ))" --data-urlencode 'step=3600'

# The spike itself, at the canon's 5-minute resolution around the fire time
curl -G "https://prom.sandboxapis.dev/api/v1/query_range" --data-urlencode 'query=service:http_request_errors:ratio{service="<service>"}' \
  --data-urlencode 'start=<fire time - 3h>' --data-urlencode 'end=<fire time + 3h>' --data-urlencode 'step=300'

The data set re-rolls daily on the live host, so the incident's instant and its service move from day to day; read them off ALERTS as above rather than copying a number from a previous run. Every query range is capped at 11,000 points per series, as Prometheus caps it.

05 / The PromQL subset

What evaluates, and what says so when it does not.

Vector 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 against a scalar, with bool; and arithmetic between two vectors whose labels match one to one. Results are the 3.15.1 engine's own, to the byte, on the same samples. /api/v1/format_query and /api/v1/parse_query answer for the same subset.

Anything else (offset, @, subqueries, topk, on() and group_left, histogram_quantile and the other functions) answers 400 bad_data naming the construct, with an X-SandboxAPIs-Coverage header. A syntax error gets Prometheus's own parse error. /api/v1/features lists exactly what evaluates.

06 / What is not served

Empty where the server has nothing, refused where Prometheus refuses.

This data set's series arrive by remote write, so there are no scrape targets, pools or target metadata, and those lists are empty, as a real server with no scrape job answers them. Paging is the incident hosts' job, so there is no alertmanager. There are no exemplars (exemplar storage is reported off), no persisted TSDB blocks and no notifications. The search API answers the same 500 a server without --enable-feature=search-api gives. Runtime info and self metrics are not served: they are measurements of a running process this replica does not have. Every write (the admin TSDB operations, remote write, OTLP) answers a 403 saying this universe is read-only.

Live host only: no pinned snapshot exists for this provider yet, so the data rolls with the live universe. Coverage manifest · Versioning & pinning