Spans API
Aggregate the database, cache, outbound HTTP and queue operations inside requests, chart one over time, drill back to its requests and look up fingerprints.
A span is one unit of work inside a request: a database query, a cache call, an outbound HTTP request, a queue publish. Every run of the same operation shares a fingerprint (fpr_…), so you can ask how often a query runs, how slow it is at the 95th percentile, and which endpoints run it most. A high runsPerRequest usually means an N+1 query. For request-level time, use the APM API.
Plan: needs a plan that includes APM; otherwise every operation answers 403 with code 2221. Permissions: any member, or a user or administrator API token.
Times are ISO 8601 in UTC and must end in Z. Span durations are in microseconds (_us).
Operation report
GET https://api.apitraffic.io/v1/accounts/{accountSid}/spans/reports
Groups spans by a field you choose and returns run counts, errors and latency percentiles for each group. Group by fingerprint for one row per operation, or by endpoint, host, type and others to see where the work happens.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
-
groupBy (query, string, required): The field to group by:
fingerprint,type,endpoint,name,system,host,db,call_site,out_endpoint,out_target,out_status_class,bucket_sidorenvironment_sid. -
startTime (query, string, required): Start of the range.
-
endTime (query, string, required): End of the range.
-
environmentSid (query, string): Only this environment.
-
bucketSid (query, string): Only this bucket.
-
endpoint (query, string): Only spans inside this endpoint of your application, for example
/v1/orders/{order_id}. -
type (query, string): Only this span type:
db.query,redis,cache,http.out,queue,rpc,graphqlorcustom. -
fingerprint (query, string): Only this operation.
-
host (query, string): Only operations against this peer host, for example
payments.acme-pay.example. -
outEndpoint (query, string): Only outbound HTTP calls to this endpoint on the other service.
-
outStatusClass (query, string): Only outbound calls with this outcome:
2xx,3xx,4xx,5xx, ornone(no response, for example a timeout). -
outStatusCode (query, integer): Only outbound calls that returned exactly this status, 0 to 599.
-
orderBy (query, string, default total): How groups are ranked before
limitis applied:total(time),p95,p99,avg,runs,errorsorruns_per_request. Ranking happens on the server, so slow but rare groups stay in the page when you sort by a percentile. -
limit (query, integer, default 50): Groups to return, 1 to 1000.
Response
-
groupBy (string, required): The grouping used.
-
total (number, required): The sum of
runsacross the groups returned. -
sourceState (string, required):
ready, orabsentwhen the account has no span data yet, so an empty result is expected rather than a quiet period. -
points (object[], required): One entry per group.
point
-
key (string): The group’s value, for example the fingerprint.
-
runs (number): Spans in the group.
-
requests (number): Distinct requests they ran in.
-
runsPerRequest (number):
runsdivided byrequests. High values point to an N+1. -
errorCount (number): Spans that failed.
-
avg_us (number): Average duration.
-
p50_us (number): Median duration.
-
p95_us (number): 95th percentile duration.
-
p99_us (number): 99th percentile duration.
-
total_us (number): Total time.
-
lastSeen (string | null): The latest run.
-
sampleType (string): A span type in the group. The other
sample…fields describe a representative span the same way:sampleName,sampleSystem,sampleHost,sampleDb,sampleEndpoint(your endpoint),sampleOutEndpoint,sampleOutMethod,sampleOutStatusCode(0 when no call got a response) andsampleStatement. -
sampleCallSite (string):
file:line:colin your code that issued the operation; empty unless call-site capture is on.sampleCallSiteFnis the enclosing function andsampleCallStackthe caller chain, onefile:line:col|functionper line. -
outCalls (number): Outbound HTTP calls in the group.
outClientErrorCount(4xx),outServerErrorCount(5xx) andoutNoResponseCountbreak down the failures.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/spans/reports \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'groupBy=fingerprint' \
--data-urlencode 'type=db.query' \
--data-urlencode 'endpoint=/v1/orders/{order_id}' \
--data-urlencode 'orderBy=runs_per_request' \
--data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
--data-urlencode 'endTime=2026-10-20T00:00:00.000Z'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
groupBy: 'fingerprint',
type: 'db.query',
endpoint: '/v1/orders/{order_id}',
orderBy: 'runs_per_request',
startTime: '2026-10-19T00:00:00.000Z',
endTime: '2026-10-20T00:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/spans/reports?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
const nPlusOne = points.filter((point) => point.runsPerRequest > 5);
{
"groupBy": "fingerprint",
"total": 21960,
"sourceState": "ready",
"points": [
{
"key": "fpr_5b8e2d7c1a9f4e3b",
"runs": 21960,
"requests": 1830,
"runsPerRequest": 12,
"errorCount": 0,
"avg_us": 1840,
"p50_us": 1500,
"p95_us": 4200,
"p99_us": 7900,
"total_us": 40406400,
"lastSeen": "2026-10-19T23:59:12.000Z",
"sampleType": "db.query",
"sampleName": "SELECT order_items",
"sampleSystem": "postgresql",
"sampleHost": "db.acme.internal",
"sampleDb": "acme_store",
"sampleEndpoint": "/v1/orders/{order_id}",
"sampleOutEndpoint": "",
"sampleOutMethod": "",
"sampleOutStatusCode": 0,
"outCalls": 0,
"outClientErrorCount": 0,
"outServerErrorCount": 0,
"outNoResponseCount": 0,
"sampleStatement": "SELECT * FROM order_items WHERE order_id = $1 AND line = $2",
"sampleCallSite": "src/repositories/orderRepository.js:48:22",
"sampleCallSiteFn": "loadLineItems",
"sampleCallStack": "src/repositories/orderRepository.js:48:22|loadLineItems\nsrc/routes/orders.js:31:17|getOrder"
}
]
}
Operation trend
GET https://api.apitraffic.io/v1/accounts/{accountSid}/spans/timeseries
Charts runs, errors and latency of spans over time. Pass a fingerprint to follow one operation, for example before and after a deploy.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
-
startTime (query, string, required): Start of the range.
-
endTime (query, string, required): End of the range.
-
fingerprint (query, string): The operation to chart. Strongly recommended: without it, the series covers every span in the range.
-
environmentSid (query, string): Only this environment.
-
bucketSid (query, string): Only this bucket.
-
endpoint (query, string): Only spans inside this endpoint.
-
type (query, string): Only this span type, as in Operation report.
-
host (query, string): Only operations against this peer host.
-
outEndpoint (query, string): Only outbound calls to this endpoint.
-
outStatusClass (query, string):
2xx,3xx,4xx,5xxornone. -
outStatusCode (query, integer): Exact outbound status, 0 to 599.
Response
- points (object[], required):
One entry per period, in time order:
bucket(period start),runs,requests,errorCount,avg_us,p95_us,p99_us,total_us,outCalls,outClientErrorCount,outServerErrorCountandoutNoResponseCount. The period width follows the length of the range.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/spans/timeseries \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'fingerprint=fpr_2a7e9c4b1d8f6e3a' \
--data-urlencode 'startTime=2026-10-19T13:00:00.000Z' \
--data-urlencode 'endTime=2026-10-19T16:00:00.000Z'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
fingerprint: 'fpr_2a7e9c4b1d8f6e3a',
startTime: '2026-10-19T13:00:00.000Z',
endTime: '2026-10-19T16:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/spans/timeseries?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
{
"points": [
{
"bucket": "2026-10-19T14:00:00.000Z",
"runs": 41,
"requests": 41,
"errorCount": 19,
"avg_us": 2310000,
"p95_us": 3990000,
"p99_us": 4010000,
"total_us": 94710000,
"outCalls": 41,
"outClientErrorCount": 0,
"outServerErrorCount": 15,
"outNoResponseCount": 4
}
]
}
Drill back to requests
GET https://api.apitraffic.io/v1/accounts/{accountSid}/spans
Lists the requests that ran an operation, so you can open real examples of a slow query or a failing call.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
-
fingerprint (query, string, required): The operation.
-
startTime (query, string, required): Start of the range.
-
endTime (query, string, required): End of the range.
-
environmentSid (query, string): Only this environment.
-
bucketSid (query, string): Only this bucket.
-
endpoint (query, string): Only requests to this endpoint.
-
limit (query, integer, default 50): Requests to return, 1 to 1000.
Response
-
fingerprint (string, required): The operation.
-
points (object[], required): One entry per request:
request_sid(use it with Get request),endpoint,spanCount(runs of the operation in that request),total_usandlastSeen.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/spans \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'fingerprint=fpr_5b8e2d7c1a9f4e3b' \
--data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
--data-urlencode 'endTime=2026-10-20T00:00:00.000Z' \
--data-urlencode 'limit=10'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
fingerprint: 'fpr_5b8e2d7c1a9f4e3b',
startTime: '2026-10-19T00:00:00.000Z',
endTime: '2026-10-20T00:00:00.000Z',
limit: '10',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/spans?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
{
"fingerprint": "fpr_5b8e2d7c1a9f4e3b",
"points": [
{
"request_sid": "req_2hJ6kL8mN1bV3cX5zA7sD9fG4qW",
"endpoint": "/v1/orders/{order_id}",
"spanCount": 14,
"total_us": 31200,
"lastSeen": "2026-10-19T23:59:12.000Z"
}
]
}
Look up fingerprints
GET https://api.apitraffic.io/v1/accounts/{accountSid}/spans/fingerprints
Describes up to 100 fingerprints: the operation type, system, target and code location, and the normalized statement when the account stores statement text. Use it to label fingerprints you got from another operation.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
- fingerprints (query, string, required):
Comma-separated fingerprints (
fpr_followed by 16 to 64 hex digits), at most 100.
Response
-
statementText (boolean, required): Whether the account stores statement text. When
false,statementandcallStackarenull. -
fingerprints (object[], required): One entry per known fingerprint:
fingerprint,fpVersion,type,system(for examplepostgresql),db,op(for exampleSELECT),target(the table, collection, host or queue),name,host,statement,callSite,callSiteFn,callStack,firstSeenVersion,firstSeenAtandlastSeenAt.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/spans/fingerprints \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'fingerprints=fpr_5b8e2d7c1a9f4e3b,fpr_2a7e9c4b1d8f6e3a'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ fingerprints: ['fpr_5b8e2d7c1a9f4e3b', 'fpr_2a7e9c4b1d8f6e3a'].join(',') });
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/spans/fingerprints?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { fingerprints } = await response.json();
{
"statementText": true,
"fingerprints": [
{
"fingerprint": "fpr_5b8e2d7c1a9f4e3b",
"fpVersion": 1,
"type": "db.query",
"system": "postgresql",
"db": "acme_store",
"op": "SELECT",
"target": "order_items",
"name": "SELECT order_items",
"host": "db.acme.internal",
"statement": "SELECT * FROM order_items WHERE order_id = $1 AND line = $2",
"callSite": "src/repositories/orderRepository.js:48:22",
"callSiteFn": "loadLineItems",
"callStack": "src/repositories/orderRepository.js:48:22|loadLineItems\nsrc/routes/orders.js:31:17|getOrder",
"firstSeenVersion": "v2026.10.2",
"firstSeenAt": "2026-10-02T10:04:51.000Z",
"lastSeenAt": "2026-10-19T23:59:12.000Z"
}
]
}
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
1000 |
A parameter is missing or invalid, for example an unknown groupBy or more than 100 fingerprints. |
403 |
2221 |
The plan does not include APM. |
502 |
5105 |
The query failed. Retry. |
503 |
5102, 5103, 5104 |
Analytics are temporarily unavailable. Retry after Retry-After seconds. |
504 |
5101 |
The query took too long. Narrow the range, or pass a fingerprint. |
Related
-
Performance analytics: The same reports in the app.
-
APM capture settings: Control statement text, Redis keys and code locations.