All integrations

Guard.ch API reference

Every endpoint you can call with an app key, with its fields, examples and errors.

This page lists what the API accepts and returns. For a walkthrough from app key to a running Playwright script, see the custom integration guide. To connect an AI assistant, use MCP instead.

Basics

Base URL
https://api.guard.ch/v8
Authentication
An app key in the Authorization header
Format
JSON over HTTPS
Access
Guard Analyst or Guard Team, also during the trial

Paths in this reference are relative to the base URL. Send request bodies as JSON with Content-Type: application/json. Responses can contain fields not listed here; ignore any you don't use.

Create app keys under Apps in your dashboard. A key acts for your account: the sessions it starts are yours, count toward your limits and appear in your session list.

Create an app key

Authentication

Send the key with every request:

Authorization: Bearer YOUR_API_KEY

Keys start with brl-k-v7-. The API reads them only from this header, never from the URL. GET /image is the only endpoint on this page that works without a key. A key expires after one year without use, or on the fixed date chosen when it was created, whichever comes first.

Responses and errors

Every endpoint answers with HTTP 200 and a JSON body. The body's status field tells you the outcome, so check it instead of the HTTP code:

StatusMeaning
okThe request succeeded. The other fields hold the result.
deniedThe request was refused, for example because the key is missing or expired, a value is invalid, no session slot is free or the session doesn't exist.
upgradeYour plan doesn't include this: API access itself, or an option such as a location, your own proxy or residential egress.
errorSomething went wrong on our side, or the path doesn't exist. Try again later.

Treat any status other than ok as a failure. message gives the reason in a sentence you can show to a person. Its language follows the Accept-Language header and defaults to English, so don't parse it: branch on status and, where present, code.

{
  "status": "denied",
  "message": "This browser session could not be found. Check the session ID and try again."
}

If the key is missing, invalid, revoked or expired, the answer is denied with loggedIn: false. If your email address isn't confirmed yet, it's denied with verified: false. File downloads are the only exception to HTTP 200, see Download a file.

Endpoints

EndpointDescription
POST /web/sessionsStart a session
GET /web/sessionsList your sessions
GET /web/sessions/:idRead one session
DELETE /web/sessions/:idStop a session
GET /web/sessions/:id/downloads/:downloadIdFetch a file the browser downloaded
GET /web/usageYour API usage over the last 30 days
GET /user/quotaYour plan's limits and what is in use
GET /imageBrowser images and locations

Sessions

A session is one isolated browser. Your code drives it over CDP, and a person can watch or take over in the live view. These endpoints only reach sessions of your own account, never a colleague's.

Start a session

POST https://api.guard.ch/v8/web/sessions

Starts a browser and answers immediately while the browser keeps starting in the background: its status is starting, then running. Connect Playwright's connectOverCDP or Puppeteer's browserWSEndpoint to connectUrl. If the connection is refused at first, wait until GET /web/sessions/:id reports running.

All body fields are optional:

FieldDescription
imageBrowser image ID. Any enabled image with supports_programmatic in GET /image. Default: chrome.
nameDisplay name for your session list, 1 to 64 characters. Leave it out and a name like brisk-otter-27 is generated.
urlAddress to open at the start. Only http and https are accepted.
countryLocation as a country or city code from vpnLocations in GET /image, for example de or us-dal. Without it, the session uses the data center's own connection. Can't be combined with proxy.
egressSet it to residential to leave through a residential connection instead of a data center. Add country to choose the country. The traffic counts toward your plan's residential allowance. Can't be combined with proxy.
proxyYour own proxy as protocol://[user:password@]host:port, with http, https, socks4 or socks5. Private and internal addresses are refused. Can't be combined with country or egress.
screenViewport as {"width": 1440, "height": 900}: width 320 to 1920, height 480 to 1080. Default: 1366 x 768.
idempotencyKeyRetry key of 1 to 128 characters. Repeat the request with the same app key and retry key within 10 minutes and you get the first successful response back instead of a second browser. Failed attempts aren't stored, so a retry after a refusal really tries again.

For example:

curl -X POST https://api.guard.ch/v8/web/sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "triage-4821",
    "url": "https://example.com",
    "country": "de",
    "screen": { "width": 1440, "height": 900 },
    "idempotencyKey": "ticket-4821"
  }'
{
  "status": "ok",
  "id": "brl-v-v7-q8m2r7k4d1",
  "name": "triage-4821",
  "image": "chrome",
  "connectUrl": "wss://api.guard.ch/web/brl-v-v7-q8m2r7k4d1/cdp?token=3f9c2a7d",
  "liveViewUrl": "https://guard.ch/en/viewer?id=brl-v-v7-q8m2r7k4d1",
  "createdAt": "2026-09-26T08:14:03.512Z",
  "screen": { "width": 1440, "height": 900 },
  "quota": {
    "running_session_limit": 3,
    "running_sessions_used": 1,
    "running_sessions_remaining": 2
  }
}

A successful response contains:

FieldDescription
idSession ID for the other endpoints.
nameThe name you gave, or the generated one.
imageThe image the session runs on.
connectUrlCDP WebSocket URL for Playwright or Puppeteer. It carries the session's own token, so treat it like a password.
liveViewUrlThe session's live view in Guard.ch, for watching and taking over. Opening it requires signing in to your account.
createdAtStart time in ISO 8601.
screenThe viewport the browser runs with.
quotaYour concurrent sessions including this one: running_session_limit, running_sessions_used and running_sessions_remaining.

A refused start also carries a machine-readable code:

CodeMeaning
invalid_argumentsA value can't be used, for example a malformed proxy. Most field checks answer without a code; message then names the field.
image_not_supportedThe image doesn't exist or can't run API sessions. allowedImages lists the images you can use.
image_restrictedYour plan can't start this image (status upgrade). allowedImages lists the images you can use.
quota_exceededAll your session slots are taken, an allowance is used up or your plan lacks an option you asked for. Stop a session or change the request; sending the same call again won't help.
rate_limitedYou started too many sessions in a short time. retryAfterMs says how many milliseconds to wait.
internal_errorSomething failed on our side (status error). Try again later.

List sessions

GET https://api.guard.ch/v8/web/sessions

Returns your sessions in data, newest first. Only sessions started through the API or MCP are listed. Investigations you start by hand in Guard.ch are left out, but GET /web/sessions/:id still finds them by ID.

ParameterDescription
statusall adds ended sessions from the last 30 days. Without it, only sessions that are starting or running are listed.
limitMaximum number of sessions, 1 to 200. Default: 50.
curl "https://api.guard.ch/v8/web/sessions?status=all&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "status": "ok",
  "data": [
    {
      "id": "brl-v-v7-q8m2r7k4d1",
      "name": "triage-4821",
      "image": "chrome",
      "status": "running",
      "createdAt": "2026-09-26T08:14:03.512Z",
      "endedAt": null,
      "endedReason": null,
      "expiresAt": "2026-09-27T02:14:03.512Z",
      "lastseen": "2026-09-26T08:31:40.087Z",
      "url": "https://example.com",
      "country": "de",
      "connectUrl": "wss://api.guard.ch/web/brl-v-v7-q8m2r7k4d1/cdp?token=3f9c2a7d",
      "liveViewUrl": "https://guard.ch/en/viewer?id=brl-v-v7-q8m2r7k4d1",
      "startedByKind": "agent",
      "analysisEnabled": false,
      "startedBy": { "id": 4821, "email": "[email protected]", "firstname": "Ana", "lastname": "Meier" }
    }
  ]
}

Each session has these fields:

FieldDescription
id, name, imageSession ID, display name and image, as returned when it started.
statusstarting or running while the session is live. Any other value means it has ended.
createdAt, endedAtStart and end time in ISO 8601. endedAt is null while the session runs.
endedReasonWhy it ended: stopped (by you or through the API), timeout (the maximum lifetime was reached), disconnect (nothing was connected for too long), runtime (your plan's runtime allowance is used up), error or lost (the browser failed or its machine went away). Null while it runs.
expiresAtThe latest time the session can end, set by your plan. It can end earlier, see Limits.
lastseenWhen a connected client last kept the session alive.
urlThe start address from the request, or null.
countryLocation code, or null.
connectUrlCDP WebSocket URL while the session is starting or running, otherwise null.
liveViewUrlThe live view in Guard.ch.
startedByKindagent for sessions started through the API or MCP, app for investigations started by hand.
analysisEnabledWhether live analysis is switched on right now.
startedByThe account that started the session: id, email, firstname and lastname.

Read a session

GET https://api.guard.ch/v8/web/sessions/:id

Returns one session in data, with the same fields as the list. It also finds an investigation you started by hand in Guard.ch, so a script can attach to it through its connectUrl. An unknown ID or another person's session answers denied.

Stop a session

DELETE https://api.guard.ch/v8/web/sessions/:id

Ends the session, removes its browser and frees the slot immediately. The answer is ok even if the session had already ended. endedReason becomes stopped unless the session ended earlier for another reason. This also ends investigations you started by hand, so only stop what your script started.

curl -X DELETE https://api.guard.ch/v8/web/sessions/brl-v-v7-q8m2r7k4d1 \
  -H "Authorization: Bearer YOUR_API_KEY"

Download a file

GET https://api.guard.ch/v8/web/sessions/:id/downloads/:downloadId

Returns the bytes of a file the browser downloaded during the session, with its content type and file name. The download ID (d1, d2, ...) comes from the MCP tool browser_downloads, which hands out this URL.

Unlike every other endpoint, this one answers with real HTTP status codes: 200 with the file, or 404 with status denied if the file isn't available. The file is requested again from the page with its cookies, so a one-time or expired download link answers 404.

Usage and quota

API usage

GET https://api.guard.ch/v8/web/usage

Totals for the sessions your account started through the API or MCP:

FieldDescription
activeSessionsSessions starting or running right now.
sessionsLast30dSessions started in the last 30 days.
totalMinutesLast30dTheir combined runtime in minutes; running sessions count up to now.
planYour plan: name, runningSessionLimit (concurrent sessions) and featureProgrammaticAccess.
poolsOne entry per subscription: subscription_id, plan_id, plan_name, running_session_limit and running_sessions_used. A data pack that only funds residential traffic shows a limit of 0.

Plan limits

GET https://api.guard.ch/v8/user/quota

The meters your plan enforces, across every session of your account, whether started by hand or through the API:

FieldDescription
has_planWhether an active plan applies.
running_session_limit, running_sessions_used, running_sessions_remainingConcurrent sessions: allowed, in use and free.
cycle_start, cycle_session_limit, cycle_sessions_used, cycle_sessions_remainingWhen the current billing cycle began, and the sessions counted in it. A limit of null means unlimited.
residential_cycle_mb_limit, residential_bytes_used_cycleYour residential allowance for the cycle in MB (null when unlimited or without a plan) and what is used, in bytes.
poolsThe same meters per subscription, with subscription_id, plan_id and plan_name.

Images and locations

GET https://api.guard.ch/v8/image

The catalog behind the image, country and egress fields. It needs no key.

curl https://api.guard.ch/v8/image
FieldDescription
imagesBrowser images with id, name, picture, enabled, requires_feature_restricted_images, supports_analysis and supports_programmatic. Pick one that is enabled and supports programmatic access.
vpnLocationsCountries with code, name, default (the city a bare country code resolves to) and cities, each with code and location. Both country and city codes work as country.
residentialResidential coverage: configured, and countries with the country codes it can reach (null: every country).

Limits

  • Three concurrent sessions per person. That is the limit on Guard Analyst and Guard Team, and sessions you start by hand count too. When every slot is taken, a new session is refused, not queued.
  • Sessions end on their own. A session ends about five minutes after the last client disconnects, and at expiresAt at the latest: 18 hours after it started. Stop sessions you no longer need to free their slot right away.
  • Starting sessions is rate-limited. Limits per minute, hour and day apply to each app key and each account. A refusal carries code: rate_limited and retryAfterMs.
  • Requests are rate-limited too. The API host accepts about two requests per second from one IP address, with short bursts above that. Beyond it, the answer is denied; wait a moment and try again.
  • Calls aren't billed. Neither requests nor sessions are charged by volume. Residential traffic counts toward your plan's allowance for the billing cycle.

Security

  • An app key has the same rights as your account on these endpoints. Keep it in a secret store and revoke it under Apps if it ever leaks.
  • Send keys only in the Authorization header, never in URLs, where they end up in logs and browser history.
  • Every connectUrl carries its own token and stops working when the session ends. Don't log or share it.
  • Target pages load in our infrastructure, never on the machine that calls the API.

Need help?

Questions about an endpoint, or missing something for your integration? Get in touch.

Try Guard.ch for 30 days.

Add a card to start. You will only be charged after the trial.

Start 30-day trial