Get monitor
const url = 'https://app.tunnelhq.com/api/v1/servers/srv_42';const options = {method: 'GET', headers: {'X-Project-Id': 'prj_12', 'X-API-Key': '<X-API-Key>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://app.tunnelhq.com/api/v1/servers/srv_42 \ --header 'X-API-Key: <X-API-Key>' \ --header 'X-Project-Id: prj_12'Returns one VPN monitor in the project. Any role in the project can call it.
Need an API key? See Getting an API key.
Authorizations
Section titled “Authorizations”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters”The monitor, as srv_42 or 42.
Example
srv_42Header Parameters
Section titled “Header Parameters”The project, as prj_12 or 12. A workspace key can only reach its own workspace’s projects.
Example
prj_12Responses
Section titled “ Responses ”The monitor.
object
healthy: the latest check passed. With several locations, at least one is up and none is down.down: confirmed down. With several locations, at least one is down and none is up.partial: several locations, some up and some down.degraded: the latest check has no verdict yet (retrying, inconclusive, or a fault on TunnelHQ’s checker), or the server refused the monitor’s credentials. The dashboard shows the first case as Unknown and the second as Degraded.paused: the monitor is paused.unknown: the monitor hasn’t been checked yet. The dashboard shows it as Pending.
Always the same as status.
false when the monitor is paused.
Seconds between scheduled checks.
Latency of the latest check, in milliseconds.
Where the latest check ran from, as any or a lowercase country code. null before the first check.
The locations the monitor tests from. ["any"] unless specific locations were chosen.
Locations that are paused. Never includes any.
The latest result per location, keyed by location code. {} before the first check.
object
object
pending means no verdict yet; degraded, that the server refused the credentials;
unavailable, that no checker is available there, so the location is left out of the status.
When that location last checked, in ISO 8601 UTC. Empty if unknown.
Why the check failed, when it did.
Present only when the check ran through a relay.
Whether a partial outage alerts.
Tag names.
When the latest check ran, in UTC, as YYYY-MM-DD HH:MM:SS with no time zone marker.
Present only on monitors imported from a subscription URL. One monitor stands for every config in the URL that points at the same server.
object
The latest result for each config.
object
The config’s name in the subscription.
The config’s protocol, such as vless.
The address traffic left from, when the check passed.
The same fields per location.
object
Examples
A healthy monitor
{ "id": "srv_42", "name": "fra-wg-01", "protocol": "wireguard", "status": "healthy", "aggregate_status": "healthy", "active": true, "interval": 180, "latency_ms": 212, "region": "any", "locations": [ "any" ], "disabled_locations": [], "regional_status": { "any": { "status": "up", "last_check": "2026-09-25T14:30:02.000Z", "ping_ms": 212 } }, "notify_on_partial": false, "tags": [ "production" ], "last_checked": "2026-09-25 14:30:02"}Up in one location, down in another
{ "id": "srv_57", "name": "sg-vless-01", "protocol": "vless", "status": "partial", "aggregate_status": "partial", "active": true, "interval": 120, "latency_ms": 388, "region": "pk", "locations": [ "any", "pk" ], "disabled_locations": [], "regional_status": { "any": { "status": "up", "last_check": "2026-09-25T14:31:10.000Z", "ping_ms": 141 }, "pk": { "status": "down", "last_check": "2026-09-25T14:31:12.000Z", "ping_ms": null, "error_category": "server_not_connected" } }, "notify_on_partial": true, "tags": [], "last_checked": "2026-09-25 14:31:12"}Headers
Section titled “Headers”Requests the workspace’s plan allows per minute.
Requests left in the current minute.
Unix time, in seconds, when the current minute ends.
The monitor ID isn’t a valid ID, or the X-Project-Id header is missing or malformed.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 400, "message": "Invalid server ID"}{ "error": true, "code": 400, "message": "X-Project-Id header is required (e.g. prj_1 or numeric id)"}The API key is missing, wrong, expired, switched off, or no longer valid.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 401, "message": "API key is required. Provide X-API-Key header or Authorization: Bearer <key>"}{ "error": true, "code": 401, "message": "Invalid or expired API key"}{ "error": true, "code": 401, "message": "API key has expired"}{ "error": true, "code": 401, "message": "API key owner account is inactive"}{ "error": true, "code": 401, "message": "This API key's workspace access has been removed"}The key can’t reach this project.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 403, "message": "This API key is scoped to a different organization"}{ "error": true, "code": 403, "message": "You do not have access to this project"}The project has no such monitor, or the project doesn’t exist.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 404, "message": "Server not found"}{ "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.
object
Seconds to wait. Absent for the monthly limit.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": "Rate limit exceeded", "message": "Per-minute limit of 60 requests exceeded", "retry_after": 60}{ "error": "Daily limit exceeded", "message": "Daily limit of 25000 requests exceeded", "retry_after": 3600}{ "error": "Monthly limit exceeded", "message": "Monthly limit of 250000 requests exceeded"}{ "error": true, "code": 429, "message": "Too many failed API key attempts from this address. Wait a few seconds and try again.", "retry_after": 6}Headers
Section titled “Headers”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.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Example
{ "error": true, "code": 500, "message": "Internal server error"}