Execute code
Execute JavaScript in a sandbox with access to Datadog through the official TypeScript-SDK-shaped API and explicitly enabled code-exec extensions.
Quick start:
```javascript
import { client, v1 } from "@datadog/datadog-api-client";
const monitors = new v1.MonitorsApi(client.createConfiguration());
return await monitors.listMonitors({ pageSize: 1 });
```
The `dd` global only exposes `dd.time.*`. Product APIs are not mounted under `dd`; import official APIs from `@datadog/datadog-api-client` and construct the appropriate `v1` or `v2` API class as shown above. Reviewed APIs that are not in the official SDK are exposed separately through the sandbox-only `@datadog/code-exec` package when enabled for the current session.
Datadog APIs are available through both the official TypeScript SDK and enabled code-exec extensions. Code-exec's value is that you can compose API calls in one script, join data across products, and return a compact result — not that you need a separate MCP helper for each Datadog query.
**SDK discovery requirement:** Do not rely on model memory for Datadog SDK method names, request fields, enum values, or response shapes. Before calling `execute_code`, call the separate `search_datadog_sdk` MCP tool unless the exact operation and complete request shape are already present in a known-good example below or were returned by an earlier SDK search in this conversation. If `execute_code` returns `invalid_request`, an unknown method or member error, or a request-shape error, call `search_datadog_sdk` before retrying. Do not repair these errors by guessing fields. Its metadata-only catalog only returns official SDK methods and code-exec extensions available in the current `execute_code` session. In one JavaScript program, select the operation and return its `usageNotes` plus a bounded closure of its `typeRefs` from `catalog.types`. For official SDK operations, use the declared version, API, method, and TypeScript types. For extensions, use the declared module, export, method, and TypeScript types. Do not guess fields from an HTTP path or summary.
For non-trivial investigations where you need a multi-query script, API aggregation, cross-source joins, or compact ranked output, load the `datadog/code-exec` skill before writing the script. Skip it for trivial one-liners or when the exact request shape is already clear. Code-exec guidance lives in `datadog/code-exec` and its bundled references; don't guess product-specific skill names like `datadog/traces` or `datadog/metrics` from the task topic.
## Execution discipline
- `dd.time.*` returns RFC3339 strings. Logs and spans accept those strings directly. Metrics scalar and timeseries requests require Unix milliseconds; use `Date.parse(dd.time.hoursAgo(...))` and `Date.parse(dd.time.now())`, and do not hand-calculate Unix timestamps.
- After an `invalid_request`, re-check the operation signature, type declarations, and usage notes before retrying. Verify timestamp units, enum values, and exact camelCase property names.
- Compose related queries in one script and return a compact result. Use `Promise.all` when every result is required. When independent calls can still provide useful evidence if a sibling fails, use `Promise.allSettled` and return the successful evidence alongside a compact failures array.
When a script returns after mixed API-call outcomes, `execute_code` reports `outcome: "partial"` and `partial: true`; the returned `value` remains usable, and `diagnostics.api_calls` describes the failed calls. Treat partial evidence as incomplete and retry only the missing source when it is necessary for the answer. If every API call fails, the execution is still reported as a failure even if the script caught the rejections.
## Investigation rules
- Use aggregate APIs for totals, top-Ns, rankings, ratios, and percentiles. Prefer aggregate operations where supported. Do not group returned samples and present them as global rankings.
- For top-N aggregations, put the sort on the `groupBy` entry. A `limit` without `sort` does not guarantee the first bucket is the top bucket.
- For log facet rankings, use `v2.LogsApi.aggregateLogs`. Group by facets such as `@error.kind`, `@http.status_code`, `@http.route`, or `@duration`.
- For span rankings, use `v2.SpansApi.aggregateSpans`. Use `v2.SpansApi`, not `v2.TracesApi`, for span search and aggregation.
- For metric totals, grouped metric rows, or APM metrics, use `v2.MetricsApi.queryScalarData`; use `queryTimeseriesData` when the answer needs points over time. Scalar responses are columnar under `result.data.attributes.columns`, not `attributes.series` or `attributes.values`.
- When the user names a data source — metrics, logs, or spans/traces — use an operation for that source unless the task explicitly asks to compare sources.
- If the user asks for operation name, group spans by `operation_name`. If the user asks for endpoint, resource, route, or path, group spans by `resource_name`.
- For APM error spans, use `status:error` unless you've verified another facet. Do not assume `error:true` works.
- For HTTP status code filters, use attributes: `@http.status_code:[400 TO 599]` or `@http.status_code:[500 TO 599]`, not `status:4xx` / `status:5xx`.
- Span reserved fields don't use `@`: `service`, `status`, `resource_name`, `operation_name`, `trace_id`, `type`. Span duration is `@duration` in nanoseconds; p95 span aggregation uses `pc95` over `@duration`.
## Known-good SDK examples
### Find source repositories for a service
When you need source code for a service found in telemetry, query its Service Catalog definition. Repository metadata may be represented as `repos`, repo-typed `links`, or the definition's GitHub source URL depending on the schema version.
```javascript
import { client, v2 } from "@datadog/datadog-api-client";
const serviceName = "example-service";
const definitions = new v2.ServiceDefinitionApi(client.createConfiguration());
const result = await definitions.getServiceDefinition({ serviceName });
const attributes = result.data?.attributes;
const schema = attributes?.schema;
const repositories = [
...(schema?.repos ?? []),
...(schema?.links ?? []).filter((link) => link.type === "repo"),
...(schema?.externalResources ?? []).filter((link) => link.type === "repo"),
].map((repository) => ({
name: repository.name,
url: repository.url,
}));
return {
serviceName: schema?.ddService ?? schema?.info?.ddService ?? serviceName,
repositories,
definitionSource: attributes?.meta?.githubHtmlUrl ?? null,
};
```
### Investigate a Synthetics failure against a passing baseline
Use the v2 result APIs to compare the initial failure with the most recent passing run from the same location and browser device. The result summary's event ID is required for reliable full-result retrieval.
```javascript
import { client, v2 } from "@datadog/datadog-api-client";
const publicId = "abc-def-ghi";
const fromTs = Date.parse("2026-01-01T10:00:00Z");
const toTs = Date.parse("2026-01-01T10:15:00Z");
const synthetics = new v2.SyntheticsApi(client.createConfiguration());
// Prefer the initial failure over its fast retries.
const failedRuns = await synthetics.listSyntheticsTestLatestResults({
publicId,
fromTs,
toTs,
status: "failed",
});
const failing = (failedRuns.data ?? [])
.filter((result) => !result.attributes?.executionInfo?.isFastRetry)
.sort(
(a, b) =>
(a.attributes?.startedAt ?? 0) - (b.attributes?.startedAt ?? 0)
)[0];
if (!failing?.id) {
return { error: "No failing Synthetics run found" };
}
const failingAttributes = failing.attributes;
// Keep location and browser device constant when selecting the baseline.
const passedRuns = await synthetics.listSyntheticsTestLatestResults({
publicId,
fromTs: failingAttributes.startedAt - 24 * 60 * 60 * 1000,
toTs: failingAttributes.startedAt - 1,
status: "passed",
probeDc: failingAttributes.location?.id
? [failingAttributes.location.id]
: undefined,
deviceId: failingAttributes.device?.id
? [failingAttributes.device.id]
: undefined,
});
const passing = (passedRuns.data ?? []).sort(
(a, b) =>
(b.attributes?.startedAt ?? 0) - (a.attributes?.startedAt ?? 0)
)[0];
async function getFullResult(summary) {
if (!summary?.id) return null;
const response = await synthetics.getSyntheticsTestResult({
publicId,
resultId: summary.id,
eventId: summary.attributes?.additionalProperties?.event_id,
timestamp: Math.floor(summary.attributes.startedAt / 1000),
});
return response.data?.attributes?.result;
}
const [failed, passed] = await Promise.all([
getFullResult(failing),
getFullResult(passing),
]);
const failedSteps = failed?.steps ?? [];
const failedIndexes = failedSteps.flatMap((step, index) =>
step.status === "failed" || step.failure ? [index] : []
);
const compactStep = (step) =>
step && {
type: step.type,
failure: step.failure,
assertionResult: step.assertionResult,
assertions: step.assertions,
request: step.request && {
method: step.request.method,
url: step.request.url,
},
response: step.response && {
statusCode: step.response.statusCode,
body: step.response.body?.slice(0, 1000),
},
trace: step.trace,
};
return {
testType: failingAttributes.testType,
location: failingAttributes.location,
device: failingAttributes.device,
failing: {
id: failing.id,
startedAt: failingAttributes.startedAt,
version: failingAttributes.additionalProperties?.test_version,
failure: failed?.failure,
assertions: failed?.assertions,
response: failed?.response && {
statusCode: failed.response.statusCode,
body: failed.response.body?.slice(0, 1000),
},
trace: failed?.trace,
},
passing: passing && {
id: passing.id,
startedAt: passing.attributes?.startedAt,
version: passing.attributes?.additionalProperties?.test_version,
},
versionChanged:
failingAttributes.additionalProperties?.test_version !==
passing?.attributes?.additionalProperties?.test_version,
failedSteps: failedIndexes.map((index) => ({
index,
failing: compactStep(failedSteps[index]),
passing: compactStep(passed?.steps?.[index]),
})),
};
```
If there isn't a passing run in the previous 24 hours, widen the baseline search to 7 days and then 60 days. If the result or failed step contains a trace ID, inspect it with the `traces.getTrace` code-exec extension in the same script. If the result version changed but the executed step evidence is inconclusive, use `getSyntheticsTestVersion` to compare retained configurations.
### Top log error kind
```javascript
import { client, v2 } from "@datadog/datadog-api-client";
const logs = new v2.LogsApi(client.createConfiguration());
const result = await logs.aggregateLogs({
body: {
filter: {
query: "service:example-service status:error @error.kind:*",
from: dd.time.hoursAgo(1),
to: dd.time.now(),
},
compute: [{ aggregation: "count" }],
groupBy: [{
facet: "@error.kind",
limit: 5,
sort: { aggregation: "count", order: "desc", type: "measure" },
}],
},
});
return (result.data?.buckets ?? []).map((bucket) => ({
errorKind: bucket.by?.["@error.kind"],
count: bucket.computes?.c0,
}));
```
For HTTP status breakdowns, keep the SDK shape and change the filter/facet:
```javascript
const result = await logs.aggregateLogs({
body: {
filter: {
query: "service:example-service @http.status_code:[400 TO 599]",
from: dd.time.hoursAgo(1),
to: dd.time.now(),
},
compute: [{ aggregation: "count" }],
groupBy: [{
facet: "@http.status_code",
limit: 10,
sort: { aggregation: "count", order: "desc", type: "measure" },
}],
},
});
return (result.data?.buckets ?? []).map((bucket) => ({
status: bucket.by?.["@http.status_code"],
count: bucket.computes?.c0,
}));
```
### Top span operation or resource
```javascript
import { client, v2 } from "@datadog/datadog-api-client";
const spans = new v2.SpansApi(client.createConfiguration());
const result = await spans.aggregateSpans({
body: {
data: {
type: "aggregate_request",
attributes: {
filter: {
query: "service:example-service status:error",
from: dd.time.hoursAgo(1),
to: dd.time.now(),
},
compute: [{ aggregation: "count", metric: "*" }],
groupBy: [{
facet: "operation_name", // use resource_name for endpoint/resource/path questions
limit: 5,
sort: { aggregation: "count", metric: "*", order: "desc", type: "measure" },
}],
},
},
},
});
return (result.data ?? []).map((bucket) => ({
operation: bucket.attributes?.by?.operation_name,
count: bucket.attributes?.compute?.c0,
}));
```
For p95 span latency, use `compute: [{ aggregation: "pc95", metric: "@duration" }]` and read `bucket.attributes.compute.c0`.
## JavaScript and imports
Code-exec runs JavaScript, not TypeScript: type annotations like `const params: v2.LogsApiAggregateLogsRequest = ...` won't parse. Request objects, class names, method names, and imports follow the declarations returned by the API catalog, but the code itself should be plain JavaScript.
The official Datadog SDK and enabled extension virtual packages are resolved inside the sandbox. Use the exact extension import returned by the API catalog rather than assuming a namespace:
```javascript
import { client, v1, v2 } from "@datadog/datadog-api-client";
```
There is no general npm/module access. Non-Datadog imports, filesystem access, and arbitrary network calls aren't available. API calls use the current user's Datadog permissions; credentials are not exposed to scripts.
Return a value to send it back. Use `console.log/info/warn/error` for intermediate output. Keep returned values small: aggregate rows, counts, and selected fields rather than raw API envelopes or broad samples. For paginated search and list APIs, follow response cursors when completeness matters; otherwise return a bounded page and label it as partial.
Top-level `await` is supported. Optional `timeout` is in seconds (1–180, default 60); raise it for slower aggregate queries.
## Sandbox utilities
The only public `dd.*` utilities are time helpers:
```javascript
const from = dd.time.hoursAgo(1);
const to = dd.time.now();
```
If you need examples or field discovery after choosing an aggregate path, use a tiny sample and label it as a sample. Do not turn sampled rows into authoritative rankings.