Drift API
Read and triage API drift findings, their baselines and evidencing requests, check drift scan health, and generate AI investigation prompts.
Drift detection learns what each endpoint normally accepts and returns (its baseline) and raises a finding when that changes, for example a field that disappears from a response, a new required parameter or a new dependency. Detection only observes; it never changes traffic. See API drift for how it works in the app.
Plan: drift scans run for buckets with drift detection turned on (see Update bucket) on plans that include API drift detection. Reading and triaging existing findings keeps working if the plan changes. Permissions: any member, or a user or administrator API token.
Drift is identified per endpoint and method: /v1/orders/{order_id} with GET and with PUT are separate.
The drift finding object
-
sid (string, required): The finding’s identifier (
drift_followed by 27 letters and digits). -
accountSid (string, required): The account.
-
bucketSid (string, required): The bucket.
-
environmentSid (string | null): The environment.
-
endpoint (string, required): The endpoint, for example
/v1/orders/{order_id}. -
method (string, required): The HTTP method.
-
changeType (string, required):
breaking_request,breaking_response,additive,behavioral,dependency,new_endpoint,disappeared_endpointorresponse_body_missing. -
severity (string, required):
high,mediumorlow. Breaking changes arehigh. -
status (string, required):
open,expected,acknowledgedorresolved. -
classification (string, required): Whether the change is explained (for example by a deploy) or not (
unknown). -
title (string | null): A one-line summary.
-
diff (object): Before and after detail. The shape depends on
changeType. -
baselineSid (string | null): The baseline the change was detected against, and
baselineVersionits version. -
correlatedEventSid (string | null): A deploy event that may explain the change.
-
sampleCount (number, required): Requests sampled for the finding.
-
onsetAt (string | null): When the change first appeared in traffic.
-
detectedAt (string | null): When the scan detected it.
-
createdAt (string | null): When the finding was created.
List drift findings
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftFindings
Returns a bucket’s drift findings.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
Query parameters
-
environmentSid (query, string): Only this environment.
-
status (query, string):
open,expected,acknowledgedorresolved. -
changeType (query, string): One of the change types above.
-
endpoint (query, string): Only this endpoint, as List endpoints returns it. Up to 500 characters.
-
method (query, string): Only this HTTP method.
-
limit (query, integer, default 250): Findings to return, 1 to 1000.
Response
-
hasMore (boolean, required): Whether more findings match than were returned.
-
records (object[], required): The findings, as drift finding objects.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftFindings \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'status=open' \
--data-urlencode 'endpoint=/v1/orders/{order_id}'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ status: 'open', endpoint: '/v1/orders/{order_id}' });
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftFindings?${params}`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const { records } = await response.json();
{
"hasMore": false,
"records": [
{
"sid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2",
"endpoint": "/v1/orders/{order_id}",
"method": "GET",
"baselineSid": "aeb_2pQ4rS6tU8vW1xY3zA5bC7dE9fG",
"baselineVersion": 3,
"changeType": "breaking_response",
"severity": "high",
"status": "open",
"classification": "unknown",
"title": "fulfillment.trackingNumber is no longer returned",
"diff": {},
"correlatedEventSid": "evt_2tG5yH7uJ9iK1oL3pZ6xC8vB4nM",
"sampleCount": 214,
"detectedAt": "2026-10-19T11:15:00.000Z",
"onsetAt": "2026-10-19T10:02:41.000Z",
"createdAt": "2026-10-19T11:15:00.000Z"
}
]
}
Get drift finding
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftFindings/{driftSid}
Returns one finding with a summary of the baseline it was detected against.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
-
driftSid (path, string, required): The finding’s identifier (
drift_…).
Response
A drift finding object, plus baseline: a summary of the baseline, or null.
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftFindings/drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const driftSid = 'drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftFindings/${driftSid}`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const finding = await response.json();
{
"sid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2",
"endpoint": "/v1/orders/{order_id}",
"method": "GET",
"baselineSid": "aeb_2pQ4rS6tU8vW1xY3zA5bC7dE9fG",
"baselineVersion": 3,
"changeType": "breaking_response",
"severity": "high",
"status": "open",
"classification": "unknown",
"title": "fulfillment.trackingNumber is no longer returned",
"diff": {},
"correlatedEventSid": "evt_2tG5yH7uJ9iK1oL3pZ6xC8vB4nM",
"sampleCount": 214,
"detectedAt": "2026-10-19T11:15:00.000Z",
"onsetAt": "2026-10-19T10:02:41.000Z",
"createdAt": "2026-10-19T11:15:00.000Z",
"baseline": {}
}
The example shortens diff and baseline.
Update drift finding
PATCH https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftFindings/{driftSid}
Changes a finding’s triage status. Mark a change you meant to make as expected, one you are looking at as acknowledged, and one that is fixed as resolved.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
-
driftSid (path, string, required): The finding’s identifier.
Body parameters
- status (body, string, required):
open,expected,acknowledgedorresolved.
Response
The updated drift finding object.
curl -X PATCH https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftFindings/drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "acknowledged" }'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const driftSid = 'drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftFindings/${driftSid}`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'acknowledged' }),
},
);
const finding = await response.json();
{
"sid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2",
"endpoint": "/v1/orders/{order_id}",
"method": "GET",
"baselineSid": "aeb_2pQ4rS6tU8vW1xY3zA5bC7dE9fG",
"baselineVersion": 3,
"changeType": "breaking_response",
"severity": "high",
"status": "acknowledged",
"classification": "unknown",
"title": "fulfillment.trackingNumber is no longer returned",
"diff": {},
"correlatedEventSid": "evt_2tG5yH7uJ9iK1oL3pZ6xC8vB4nM",
"sampleCount": 214,
"detectedAt": "2026-10-19T11:15:00.000Z",
"onsetAt": "2026-10-19T10:02:41.000Z",
"createdAt": "2026-10-19T11:15:00.000Z"
}
List drift finding requests
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftFindings/{driftSid}/requests
Lists the captured requests that show the change. Open one with Get request.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
-
driftSid (path, string, required): The finding’s identifier.
Response
-
hasMore (boolean, required): Whether more requests exist.
-
records (object[], required): One entry per request:
sid,driftSid,requestSid,observedAtandrole:exemplar(a typical example),onset(one of the first after the change) orclean(one from before it, for comparison).
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftFindings/drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD/requests \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const driftSid = 'drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftFindings/${driftSid}/requests`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const { records } = await response.json();
{
"hasMore": false,
"records": [
{
"sid": "dar_2gH5jK7lM9nP1qR3sT6uV8wX4yZ",
"driftSid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"requestSid": "req_2jK5lM7nP9qR1sT3uV6wX8yZ4aB",
"role": "onset",
"observedAt": "2026-10-19T10:02:41.000Z"
},
{
"sid": "dar_2hJ6kL8mN1pQ3rS5tU7vW9xY2zA",
"driftSid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"requestSid": "req_2kL6mN8pQ1rS3tU5vW7xY9zA2bC",
"role": "clean",
"observedAt": "2026-10-19T09:58:10.000Z"
}
]
}
Get drift diagnosis
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftFindings/{driftSid}/diagnosis
Builds a prompt you can paste into an AI coding agent to investigate the change, with the baseline, the requests that show it and the deploys that landed shortly before. The prompt points the agent at the MCP server to fetch the captured requests, rather than including them.
Plan: needs AI diagnoses. Each call counts toward the plan’s monthly AI diagnoses allowance; when it is used up, the API answers 429 with code 2222.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
-
driftSid (path, string, required): The finding’s identifier.
Query parameters
-
environmentSid (query, string): The environment to look for deploys in. Defaults to the finding’s own.
-
lookbackMinutes (query, number, default 180): How far before the change to look for deploys, 1 to 10080 minutes.
Response
-
prompt (string, required): The prompt, ready to paste.
-
requests (object[], required): The requests that show the change, as in List drift finding requests.
-
deploys (object[], required): Deploys found before the change, in the same shape as in Get fix prompt.
-
baseline (object | null): A summary of the baseline.
-
driftSid (string, required): The finding, with its
bucketSid,endpoint,method,changeType,severity,classification,onsetAtanddetectedAt.
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftFindings/drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD/diagnosis \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const driftSid = 'drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftFindings/${driftSid}/diagnosis`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const { prompt } = await response.json();
{
"driftSid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"bucketSid": "k3m9p2x",
"endpoint": "/v1/orders/{order_id}",
"method": "GET",
"changeType": "breaking_response",
"severity": "high",
"classification": "unknown",
"onsetAt": "2026-10-19T10:02:41.000Z",
"detectedAt": "2026-10-19T11:15:00.000Z",
"baseline": {},
"requests": [
{
"sid": "dar_2gH5jK7lM9nP1qR3sT6uV8wX4yZ",
"driftSid": "drift_2kM4nP6qR8sT1uV3wX5yZ7aB9cD",
"requestSid": "req_2jK5lM7nP9qR1sT3uV6wX8yZ4aB",
"role": "onset",
"observedAt": "2026-10-19T10:02:41.000Z"
}
],
"deploys": [],
"prompt": "The response of GET /v1/orders/{order_id} changed at 2026-10-19T10:02:41Z ..."
}
List drift baselines
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftBaselines
Returns the learned baselines: what each endpoint and method normally looks like.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
Query parameters
-
environmentSid (query, string): Only this environment.
-
status (query, string):
learning(still collecting samples),active(findings are raised against it) orsuperseded(replaced by a newer version). -
endpoint (query, string): Only this endpoint.
-
method (query, string): Only this method.
-
limit (query, integer, default 250): Baselines to return, 1 to 1000.
Response
-
hasMore (boolean, required): Whether more baselines match.
-
records (object[], required): One entry per baseline:
sid,accountSid,bucketSid,environmentSid,endpoint,method,version,status,schema(the request and response shape),behavior,dependencies,sampleCount,firstSeenAtandlastSeenAt.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftBaselines \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'status=active'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftBaselines?status=active`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const { records } = await response.json();
{
"hasMore": false,
"records": [
{
"sid": "aeb_2pQ4rS6tU8vW1xY3zA5bC7dE9fG",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2",
"endpoint": "/v1/orders/{order_id}",
"method": "GET",
"version": 3,
"status": "active",
"schema": {},
"behavior": {},
"dependencies": {},
"sampleCount": 1840,
"firstSeenAt": "2026-09-30T08:00:00.000Z",
"lastSeenAt": "2026-10-19T09:58:10.000Z"
}
]
}
Get drift health
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftHealth
Explains whether drift detection is running for a bucket and why the finding list looks the way it does: turned off, not yet scanned, failing, no traffic, still learning, watching or drifting.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
Query parameters
- environmentSid (query, string): Only this environment.
Response
-
state (string, required):
disabled,never_scanned,failing,no_traffic,learning,watchingordrifting. -
summary (string, required): A one-line explanation of
state, with more indetail. -
lastRunAt (string | null): When the last scan ran, and
nextRunAtwhen the next one is due.scanIntervalMsis the time between scans. -
lastRun (object | null): The last scan run, and
lastSuccessfulRunthe last one that succeeded. -
baselines (object, required): Baseline counts:
total,activeandlearning. -
openAlertCount (number, required): Open findings.
-
failedScopes (number, required): Scan scopes (bucket and environment) whose last scan failed.
-
totals (object, required): Totals across recent runs.
-
recentRuns (object[], required): Recent scan runs, newest first.
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftHealth \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftHealth`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { state, summary } = await response.json();
{
"state": "drifting",
"summary": "1 open finding on 15 watched endpoints.",
"detail": null,
"lastRunAt": "2026-10-20T11:15:00.000Z",
"nextRunAt": "2026-10-20T12:15:00.000Z",
"scanIntervalMs": 3600000,
"lastRun": null,
"lastSuccessfulRun": null,
"totals": {},
"baselines": { "total": 15, "active": 14, "learning": 1 },
"openAlertCount": 1,
"failedScopes": 0,
"recentRuns": []
}
The example leaves out the run details; see List drift scan runs for their fields.
List drift scan runs
GET https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}/driftScanRuns
Returns recent drift scans, newest first, with what each one looked at.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
bucketSid (path, string, required): The bucket identifier.
Query parameters
-
environmentSid (query, string): Only this environment.
-
status (query, string):
ok,no_trafficorfailed. -
limit (query, integer, default 50): Runs to return, 1 to 200.
Response
-
hasMore (boolean, required): Whether more runs exist.
-
records (object[], required): One entry per run.
run
-
sid (string): Run identifier.
-
environmentSid (string | null): The environment scanned.
-
status (string):
ok,no_trafficorfailed;errorMessagesays why it failed. -
windowFrom (string | null): Start of the traffic examined, and
windowToits end. -
endpointsDiscovered (number): Endpoints found in the window.
-
endpointsProcessed (number): Endpoints sampled.
-
samplesCollected (number): Requests sampled.
-
endpointsDiffed (number): Endpoints compared against an established baseline.
-
baselinesWritten (number): Baselines created or updated.
baselinesActiveandbaselinesLearningcount them by status. -
findingsWritten (number): Findings raised.
-
startedAt (string | null): When the run started,
finishedAtwhen it ended, anddurationMshow long it took.
curl "https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/buckets/k3m9p2x/driftScanRuns?limit=5" \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(
`https://api.apitraffic.io/v1/accounts/${accountSid}/buckets/k3m9p2x/driftScanRuns?limit=5`,
{ headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` } },
);
const { records } = await response.json();
const failed = records.filter((run) => run.status === 'failed');
{
"hasMore": true,
"records": [
{
"sid": "dscan_2mN5pQ7rS9tU1vW3xY6zA8bC4dE",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2",
"status": "ok",
"errorMessage": null,
"windowFrom": "2026-10-20T10:15:00.000Z",
"windowTo": "2026-10-20T11:15:00.000Z",
"endpointsDiscovered": 15,
"endpointsProcessed": 15,
"samplesCollected": 412,
"endpointsDiffed": 14,
"baselinesWritten": 2,
"baselinesActive": 14,
"baselinesLearning": 1,
"findingsWritten": 0,
"startedAt": "2026-10-20T11:15:00.000Z",
"finishedAt": "2026-10-20T11:15:09.000Z",
"durationMs": 9120
}
]
}
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
1000 |
A parameter is invalid, for example a driftSid in the wrong format or an unknown status. |
403 |
2221 |
The plan does not include AI diagnoses (diagnosis only). |
403 |
2509 |
Changing a finding on the read-only sample bucket. |
404 |
4000 |
No finding with that sid in this bucket. |
429 |
2222 |
The monthly AI diagnoses allowance is used up. |
Related
-
Drift detection: How drift detection works and how to read a finding.
-
Endpoints API: The endpoints drift watches.