Skip to main content

Latency and loss

GET /api/targets/{target_id}/data/latency

Returns a target's measurements as one time series per agent. Each data point aggregates the measurements one agent made during a fixed interval: average latency, success rate, and the lowest and highest latency.

Parameters​

ParameterDescription
agent_labelOptional. Return only this agent's series. By default, every agent that measured the target in the range is included.
startOptional. Range start, Unix milliseconds. Default: 24 hours before end.
endOptional. Range end, Unix milliseconds. Default: now.

The interval of each data point depends on the length of the range:

RangeInterval
Up to 7 days1 minute
More than 7 days, up to 30 days5 minutes
More than 30 days1 hour

Request​

TARGET_ID=01M4MZ5T3A76E4RBFKGBD5MP8V
END=$(date +%s000)
START=$(( END - 3600000 )) # one hour

curl --header "Authorization: Bearer $OBSERVIANT_API_KEY" \
"$OBSERVIANT_URL/api/targets/$TARGET_ID/data/latency?start=$START&end=$END"

Response​

{
"latency_series": [
{
"target_id": "01M4MZ5T3A76E4RBFKGBD5MP8V",
"agent_label": "dc1-probe",
"display_name": "dc1-probe",
"latency_success_data": [
[1791705180000, 0.986, 100, 0.901, 1.119],
[1791705240000, 1.69, 100, 0.904, 4.315],
[1791705300000, 37.42, 92.5, 35.8, 61.07],
[1791705360000, -1, 0, -1, -1]
]
},
{
"target_id": "01M4MZ5T3A76E4RBFKGBD5MP8V",
"agent_label": "sys-amsterdam",
"display_name": "Amsterdam",
"latency_success_data": [
[1791705180000, 4.12, 100, 3.98, 4.71],
[1791705240000, 4.07, 100, 3.95, 4.33]
]
}
]
}

latency_series contains one object per agent, sorted by agent_label:

FieldDescription
target_idThe target.
agent_labelStable agent identifier. See Agent labels and display names.
display_nameAgent name for presentation.
latency_success_dataData points, oldest first.

Each data point is an array of five numbers:

PositionValueUnit
0Start of the intervalUnix milliseconds
1Average latency of the successful measurements in the intervalmilliseconds
2Average success ratepercent, 0–100
3Lowest latency observedmilliseconds
4Highest latency observedmilliseconds

Read the values as follows:

  • -1 means no successful measurement. When every measurement in an interval failed, latency, minimum and maximum are -1 and the success rate is 0. For ICMP targets this is 100% packet loss.
  • A missing interval means no measurements. The agent was offline, the target was not assigned to it, or the target did not exist yet. Treat a gap differently from a failure.
  • Packet loss is 100 - success rate for ICMP targets. For the other protocols, each measurement either succeeds or fails, so the success rate is the share of successful checks in the interval.
  • Minimum and maximum show the spread. For ICMP they are the lowest and highest round-trip times of individual echo replies. For the other protocols they are the lowest and highest measurement in the interval.

An unknown target returns 404 Not Found. An invalid start or end, or a start after end, returns 400 Bad Request. A target with no measurements in the range returns an empty latency_series.

Example: summarize the last hour​

This script prints the average latency, the worst one-minute interval and the number of minutes with loss for each agent:

latency_summary.py
"""Summarize one target's latency and loss over the last hour, per agent.

Usage: python latency_summary.py TARGET_ID
"""
import os
import sys
import time

import requests

base_url = os.environ["OBSERVIANT_URL"]
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['OBSERVIANT_API_KEY']}"

target_id = sys.argv[1]
end = int(time.time() * 1000)
response = session.get(
f"{base_url}/api/targets/{target_id}/data/latency",
params={"start": end - 3600 * 1000, "end": end},
timeout=30,
)
response.raise_for_status()

for series in response.json()["latency_series"]:
points = series["latency_success_data"]
answered = [p for p in points if p[1] >= 0]
lossy = [p for p in points if p[2] < 100]
name = series["display_name"]
if not answered:
print(f"{name}: no successful measurements in {len(points)} intervals")
continue
average = sum(p[1] for p in answered) / len(answered)
worst = max(answered, key=lambda p: p[1])
worst_time = time.strftime("%H:%M", time.gmtime(worst[0] / 1000))
print(
f"{name}: average {average:.1f} ms, worst {worst[1]:.1f} ms at {worst_time} UTC, "
f"{len(lossy)} of {len(points)} minutes with loss"
)
$ python latency_summary.py 01M4MZ5T3A76E4RBFKGBD5MP8V
dc1-probe: average 1.2 ms, worst 4.4 ms at 07:58 UTC, 2 of 60 minutes with loss
Amsterdam: average 4.1 ms, worst 6.3 ms at 08:31 UTC, 0 of 60 minutes with loss

To find the traceroute that explains a spike, pass the interval's timestamp to /mtr/nearest.