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.

This page as Markdown

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_sid or environment_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, graphql or custom.

  • 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, or none (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 limit is applied: total (time), p95, p99, avg, runs, errors or runs_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 runs across the groups returned.

  • sourceState (string, required): ready, or absent when 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): runs divided by requests. 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) and sampleStatement.

  • sampleCallSite (string): file:line:col in your code that issued the operation; empty unless call-site capture is on. sampleCallSiteFn is the enclosing function and sampleCallStack the caller chain, one file:line:col|function per line.

  • outCalls (number): Outbound HTTP calls in the group. outClientErrorCount (4xx), outServerErrorCount (5xx) and outNoResponseCount break down the failures.

bash
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'
js
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);
json
{
  "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, 5xx or none.

  • 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, outServerErrorCount and outNoResponseCount. The period width follows the length of the range.
bash
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'
js
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();
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_us and lastSeen.

bash
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'
js
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();
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, statement and callStack are null.

  • fingerprints (object[], required): One entry per known fingerprint: fingerprint, fpVersion, type, system (for example postgresql), db, op (for example SELECT), target (the table, collection, host or queue), name, host, statement, callSite, callSiteFn, callStack, firstSeenVersion, firstSeenAt and lastSeenAt.

bash
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/spans/fingerprints \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  --data-urlencode 'fingerprints=fpr_5b8e2d7c1a9f4e3b,fpr_2a7e9c4b1d8f6e3a'
js
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();
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.