Documentation 40

Mock Datadog API quickstart · 4 min read

Monitors, metric queries and incidents, read-only. Compatible with the Datadog API, v1 and v2.

A monitor for every alerting rule the data set's services carry, metric queries over their trace metrics, monitor events, a dashboard per service, downtimes, incidents, on-call schedules, users, teams, keys and error logs. The spike these queries return is the one PagerDuty paged for and Sentry recorded, and the incident ends at a fix pull request the git hosts serve.

Import it instead

Both files list exactly what this host serves — generated from our own coverage manifest, not from Datadog'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 api.dd.sandboxapis.dev

No key needed. Press Enter to send.
01Point your client at api.dd.
02Query the incident window
03Know what is empty and why

01 / Host + auth

The host is api.dd., and that is the point.

Every Datadog client builds its host as api.<site>: both published API documents declare the server as https://{subdomain}.{site} with the subdomain api. So the site here is dd.sandboxapis.dev and the host is api.dd.sandboxapis.dev. Paths are /api/v1/... and /api/v2/... exactly as on api.datadoghq.com, and every error body is Datadog's own {"errors": [...]} envelope. Auth is any DD-API-KEY and DD-APPLICATION-KEY, or none at all.

request.sh
curl -H 'DD-API-KEY: any-key' -H 'DD-APPLICATION-KEY: any-key' \
  "https://api.dd.sandboxapis.dev/api/v1/monitor"

The clients differ in how they take the host. The TypeScript client checks nothing, so DD_SITE=dd.sandboxapis.dev is enough. The Python and Go clients reject a site that is not one of Datadog's own, so give them the whole host instead: Configuration(host=...) in Python, server index 1 with a name variable in Go.

monitors.py
from datadog_api_client import ApiClient, Configuration
from datadog_api_client.v1.api.monitors_api import MonitorsApi

config = Configuration(host="https://api.dd.sandboxapis.dev")
config.api_key["apiKeyAuth"] = "any-key"
config.api_key["appKeyAuth"] = "any-key"
with ApiClient(config) as client:
    for m in MonitorsApi(client).list_monitors():
        print(m.id, m.name, m.overall_state)
monitors.mjs
// DD_SITE=dd.sandboxapis.dev DD_API_KEY=any-key DD_APP_KEY=any-key node monitors.mjs
import { client, v1 } from "@datadog/datadog-api-client";

const config = client.createConfiguration();
for (const m of await new v1.MonitorsApi(config).listMonitors()) console.log(m.id, m.name, m.overallState);
monitors.go
ctx := context.WithValue(datadog.NewDefaultContext(context.Background()),
    datadog.ContextServerIndex, 1)
ctx = context.WithValue(ctx, datadog.ContextServerVariables,
    map[string]string{"name": "api.dd.sandboxapis.dev", "protocol": "https"})

The incident API is marked unstable, so the clients refuse to call it until you opt in: configuration.unstable_operations["list_incidents"] = True in Python, the same key in TypeScript's unstableOperations. Datadog's own hosted MCP server cannot be pointed at this host; MCP servers built on the clients above can, with the same setting.

Using a key? Keys & rate limits → A SandboxAPIs key rides the DD-API-KEY slot and lifts the anonymous limit; the answer carries Datadog's X-RateLimit-* headers for your budget. The Python snippet is a verified quickstart →

02 / The incident window

Query the metrics, find the spike.

The data set ends at 00:00 UTC today. A relative window (now-1h, now-1d) resolves against that instant, never against the wall clock, so the last 24 hours always overlap the data. Four metrics are held, tagged service and team: trace.http.request (a latency distribution in seconds; p50 and p99), trace.http.request.hits and trace.http.request.errors (counts), and system.cpu.user (percent).

query.sh
# One request-count series per service, the last day of the data set
curl -G "https://api.dd.sandboxapis.dev/api/v1/query" \
  --data-urlencode "from=$(( $(date -u -d 'today 00:00' +%s) - 86400 ))" \
  --data-urlencode "to=$(date -u -d 'today 00:00' +%s)" \
  --data-urlencode 'query=sum:trace.http.request.hits{*} by {service}.as_count()'

# The monitors, then the incident and who commanded it
curl "https://api.dd.sandboxapis.dev/api/v1/monitor"
curl "https://api.dd.sandboxapis.dev/api/v2/incidents"

The query language covered is the common subset: an aggregator (avg, sum, min, max, and p50/p99 on the distribution), a scope of * or tag:value terms (with !), by {tags}, .rollup(), .as_count(), .as_rate(), and one binary operator between two terms. Anything else (functions such as top() or timeshift(), percentiles the data set does not hold) answers a 400 naming the construct, with an X-SandboxAPIs-Coverage header. It never answers a silently different query.

03 / Traces, spans and hosts

Spans that lead to the real error.

Traces are sampled, the way an APM backend keeps them: one trace per service every two hours, one every five minutes on the incident's service while the incident runs, and one for every recorded error. A failing span carries the same error the Sentry issue shows, so a trace found from the incident leads to the code that broke. The services run on thirteen Linux hosts, each named after its service with a two-digit number (<service>-01, <service>-02): three for each critical service, two for each standard one and one for the internal one. Hosts carry service, team and tier tags and nothing about a cloud provider. Read them at /api/v1/hosts, search spans at /api/v2/spans/events, and fetch one trace at /api/v2/trace/{trace_id}.

04 / What is empty, refused or not served

The whole API is counted.

Every read operation in both published documents is a row on the coverage page, the unstable and deprecated ones included. A row this data set has no use for answers what Datadog would: the empty list when the data set never configured that product (synthetic tests, RUM, security monitoring, cloud cost and the rest), and Datadog's own documented 404 for an id under an empty list. The caller is treated as an organization admin, so nothing is refused for want of a role. Keys are listed with their names and last four characters, never a key value.

Some rows answer a 404 with an X-SandboxAPIs-Coverage header instead: ingested span bytes, cost figures (they need Datadog's price list), Datadog's own catalogues (default detection rules, synthetic locations, integration lists), and vocabularies the published documents only give examples of. Every write answers a 403 saying this universe is read-only.

Live host only for now: no pinned snapshot exists for this provider, so the data rolls with the live data set every day. Coverage manifest → · Versioning & pinning →