Skip to content

List results

GET
/results
curl --request GET \
--url 'https://app.tunnelhq.com/api/v1/results?period=15m&limit=1000' \
--header 'X-API-Key: <X-API-Key>' \
--header 'X-Project-Id: prj_12'

Returns the project’s check results, tunnels and Uptime checks alike, newest first, up to limit. Any role in the project can call it.

Every night, results older than 24 hours are thinned: each monitor keeps its 100 newest results and the ones where its status changed. So 7d returns the last day in full and only status changes before that, and there’s no longer period.

Need an API key? See Getting an API key.

X-Project-Id
required
string

The project, as prj_12 or 12. A workspace key can only reach its own workspace’s projects.

Example
prj_12
period
string
default: 1h
Allowed values: 15m 1h 24h 7d

How far back to look. Any other value is refused with 400 “period must be 15m, 1h, 24h or 7d.”

limit
integer
default: 1000 >= 1 <= 1000

The most results to return, from 1 to 1000. Anything else is refused with 400 “limit must be a whole number from 1 to 1000.”

The results.

Media typeapplication/json
object
results
required
Array<object>
object
id
required
string
server_id
required
string
server_name
required
string
status
required

degraded means the check had no verdict; unknown, that it ran during maintenance.

string
Allowed values: healthy down degraded unknown
latency_ms
required

The check’s time, in milliseconds: for a tunnel, one round trip through it; for an Uptime check, as described for the monitor’s latency_ms. Null when the check failed or measured none.

integer | null
connect_ms

Tunnel results only. How long the tunnel took to come up, in milliseconds. Null when the check failed or measured none. For VLESS, VMess, Trojan, Shadowsocks, Hysteria2, and TUIC, it’s the time to first contact with the server.

integer | null
protocol
required

A tunnel’s protocol, or an Uptime check’s type: http, keyword, ping, port, dns, or push.

string
tested_at
required

When the check ran, in UTC, as YYYY-MM-DD HH:MM:SS with no time zone marker.

string
error_message
required

What the check said when it failed, such as “Tunnel unreachable”.

string | null
period
required

The period you sent, lowercased.

string
total
required

Results returned.

integer
Example
{
"results": [
{
"id": "hb_9912345",
"server_id": "srv_42",
"server_name": "fra-wg-01",
"status": "healthy",
"latency_ms": 157,
"connect_ms": 364,
"protocol": "wireguard",
"tested_at": "2026-09-25 14:30:02",
"error_message": null
}
],
"period": "1h",
"total": 1
}
X-RateLimit-Limit
integer

Requests the workspace’s plan allows per minute.

X-RateLimit-Remaining
integer

Requests left in the current minute.

X-RateLimit-Reset
integer

Unix time, in seconds, when the current minute ends.

A query parameter is out of range, or the X-Project-Id header is missing or malformed.

Media typeapplication/json
object
error
required
boolean
code
required

The HTTP status, repeated.

integer
message
required

What went wrong.

string
details

More detail, on some errors.

retry_after

Seconds to wait, on the 429 for wrong keys.

integer
Examples
{
"error": true,
"code": 400,
"message": "period must be 15m, 1h, 24h or 7d."
}

The API key is missing, wrong, expired, switched off, or no longer valid.

Media typeapplication/json
object
error
required
boolean
code
required

The HTTP status, repeated.

integer
message
required

What went wrong.

string
details

More detail, on some errors.

retry_after

Seconds to wait, on the 429 for wrong keys.

integer
Examples
{
"error": true,
"code": 401,
"message": "API key is required. Provide X-API-Key header or Authorization: Bearer <key>"
}

The key can’t reach this project.

Media typeapplication/json
object
error
required
boolean
code
required

The HTTP status, repeated.

integer
message
required

What went wrong.

string
details

More detail, on some errors.

retry_after

Seconds to wait, on the 429 for wrong keys.

integer
Examples
{
"error": true,
"code": 403,
"message": "This API key is scoped to a different organization"
}

No active project has this ID.

Media typeapplication/json
object
error
required
boolean
code
required

The HTTP status, repeated.

integer
message
required

What went wrong.

string
details

More detail, on some errors.

retry_after

Seconds to wait, on the 429 for wrong keys.

integer
Example
{
"error": true,
"code": 404,
"message": "Project not found or inactive"
}

A usage limit was reached, or this IP address made too many requests with a wrong key.

Usage limits are per workspace and plan: per minute (Retry-After: 60), per day (Retry-After: 3600), and per month (no Retry-After). They use their own body shape. An IP address may send 10 wrong keys a minute; one more is allowed every 6 seconds. While that allowance is used up, every request from the address is refused, even one with a valid key.

Media typeapplication/json
One of:
object
error
required
string
Allowed values: Rate limit exceeded Daily limit exceeded Monthly limit exceeded
message
required
string
retry_after

Seconds to wait. Absent for the monthly limit.

integer
Examples
{
"error": "Rate limit exceeded",
"message": "Per-minute limit of 60 requests exceeded",
"retry_after": 60
}
Retry-After
integer

Seconds to wait. 60 for the per-minute limit, 3600 for the daily limit, 6 after wrong keys. Absent for the monthly limit.

Something failed on TunnelHQ’s side.

Media typeapplication/json
object
error
required
boolean
code
required

The HTTP status, repeated.

integer
message
required

What went wrong.

string
details

More detail, on some errors.

retry_after

Seconds to wait, on the 429 for wrong keys.

integer
Example
{
"error": true,
"code": 500,
"message": "Internal server error"
}