Skip to content

Run tests on several monitors

POST
/test/batch
curl --request POST \
--url https://app.tunnelhq.com/api/v1/test/batch \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <X-API-Key>' \
--header 'X-Project-Id: prj_12' \
--data '{ "server_ids": [ "srv_42", "srv_57" ] }'

Queues a test of each monitor and returns at once, without waiting for results. Needs a role that can edit monitors (Owner, Admin, or Manager). Each monitor tested uses one on-demand test from the plan’s monthly quota; the request is refused only when none is left.

Read the results with List a monitor’s results. successful and failed count the tests queued, not their outcomes. The top-level batch_id is a label; it can’t be looked up.

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
Media typeapplication/json
object
server_ids
required

The monitors, as srv_42 or 42. Every one must be in the project.

Array<string | integer>
>= 1 items
region

Where to test from: any, or a lowercase country code such as pk. A country needs the Pro plan or above. List regions shows the locations available now.

string
default: any /^([a-z]{2}|any)$/
region_fallback

What happens when the location has no checker available. any runs the test from the general pool instead, and the response doesn’t say so; fail refuses it. Any other value counts as any.

string
default: any
Allowed values: any fail
Example
{
"server_ids": [
"srv_42",
"srv_57"
]
}

The tests are queued, one queue job per protocol.

Media typeapplication/json
object
batch_id
required
string
results
required
Array<object>
object
protocol
string
batch_id

The queue job for this protocol.

string
count

Monitors queued for this protocol.

integer
error

Why this protocol’s tests couldn’t be queued.

string
total
required

Monitors in the request.

integer
successful
required

Tests queued.

integer
failed
required

Tests that couldn’t be queued.

integer
Example
{
"batch_id": "batch-1790346600000",
"results": [
{
"protocol": "wireguard",
"batch_id": "batch-wireguard-1790346600000",
"count": 1
},
{
"protocol": "vless",
"batch_id": "batch-vless-1790346600001",
"count": 1
}
],
"total": 2,
"successful": 2,
"failed": 0
}
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.

server_ids or region is invalid.

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": "Missing or invalid server_ids"
}

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>"
}

Testing from a specific location needs the Pro plan or above.

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": 402,
"message": "Region targeting requires the Pro plan or above."
}

A monitor isn’t in the project, or the role or quota doesn’t allow it.

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": "Some servers are not accessible",
"details": {
"invalid": [
"srv_99"
]
}
}

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"
}