Documentation

Overview

Use the service

Billing

Reference

Overview

Quickstart

From an API key to a published capture, a browser session, an analytical job, a managed service and an MCP client.

1. Create an API key

Sign in at the panel, open API keys and create one. The panel shows the key once; copy it into your shell or secret store:

export BROWSERGRID_API_KEY='bgk_...'

Use your actual key in place of bgk_.... The examples below run in the same Bash shell and require curl, jq and uuidgen. All grid API requests use https://grid-lab.zyxio.xyz; artifact files come from the signed download URL returned by the API.

Check Credit on the same page. Add credit or set up monthly invoicing before starting. See Credit and billing.

2. Capture a page

Submit a batch. This one renders a single page with render-fetch, which needs the exact hosts the page may load from:

BATCH=$(curl -fsS https://grid-lab.zyxio.xyz/batches \
  -H "x-api-key: $BROWSERGRID_API_KEY" \
  -H "content-type: application/json" \
  -d '{"urls": ["https://example.com/"], "script": "render-fetch", "params": {"allowedHosts": ["example.com"]}}')
BATCH_ID=$(printf '%s' "$BATCH" | jq -er '.batchId')

Poll until completed + failed equals total. Workers start on demand, so the first results of a quiet grid can take a minute or two.

while :; do
  BATCH_STATUS=$(curl -fsS "https://grid-lab.zyxio.xyz/batches/$BATCH_ID" \
    -H "x-api-key: $BROWSERGRID_API_KEY") || break
  printf '%s\n' "$BATCH_STATUS" | jq '.counts'
  if printf '%s' "$BATCH_STATUS" | jq -e '.counts.completed + .counts.failed == .total' >/dev/null; then
    break
  fi
  sleep 2
done

Each entry of jobs has a jobId. For the completed item, ask for a download URL of its rendered HTML and fetch it within 15 minutes. If the capture failed, inspect failedReason in BATCH_STATUS first.

if CAPTURE_JOB_ID=$(printf '%s' "$BATCH_STATUS" | jq -er '.jobs[] | select(.state == "completed") | .jobId'); then
  ARTIFACT=$(curl -fsS "https://grid-lab.zyxio.xyz/artifacts/$CAPTURE_JOB_ID/html" \
    -H "x-api-key: $BROWSERGRID_API_KEY")
  ARTIFACT_URL=$(printf '%s' "$ARTIFACT" | jq -er '.url')
  curl -fsS -o page.html "$ARTIFACT_URL"
fi

This item costs €0.0008 when it is published. If it fails, it costs nothing.

3. Lease a browser

SESSION=$(curl -fsS https://grid-lab.zyxio.xyz/sessions \
  -H "x-api-key: $BROWSERGRID_API_KEY" \
  -H "content-type: application/json" \
  -d '{"ttlMinutes": 10}')
SESSION_ID=$(printf '%s' "$SESSION" | jq -er '.sessionId')
export CDP_URL=$(printf '%s' "$SESSION" | jq -er '.cdpUrl')

Keep CDP_URL private: the address is returned only at creation. Poll until your session is running with an attestation matching the requested browser profile, then connect before its lease expires:

while :; do
  SESSIONS=$(curl -fsS https://grid-lab.zyxio.xyz/sessions \
    -H "x-api-key: $BROWSERGRID_API_KEY") || break
  if printf '%s' "$SESSIONS" | jq -e --arg id "$SESSION_ID" \
    '.[] | select(.sessionId == $id and .status == "running" and .attestation.profileId == .profileId)' >/dev/null; then
    break
  fi
  if ! printf '%s' "$SESSIONS" | jq -e --arg id "$SESSION_ID" '.[] | select(.sessionId == $id)' >/dev/null; then
    printf '%s\n' 'The session ended; lease a new browser.'
    break
  fi
  sleep 2
done

In a Node.js program with playwright installed:

import { chromium } from 'playwright'
const browser = await chromium.connectOverCDP(process.env.CDP_URL)

Release it when you are done, on every exit path:

curl -fsS -X DELETE "https://grid-lab.zyxio.xyz/sessions/$SESSION_ID" \
  -H "x-api-key: $BROWSERGRID_API_KEY"

You pay €0.07 per hour from the moment the browser is ready until you release it; start-up is free.

4. Run an analytical job

Use the prebuilt Python 3.12 runtime immediately. See Analytical jobs for all four runtimes and custom images.

JOB=$(curl -fsS https://grid-lab.zyxio.xyz/jobs \
  -H "x-api-key: $BROWSERGRID_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "image": "registry.grid.zyxio.xyz/zyxioxyz/browsergrid-compute-python-3.12@sha256:5ca0336a5d50df4e398a635cf8766ebcd13ab681c064c3cf59577bbf87c5644c",
    "command": ["python", "-c", "print(6 * 7)"],
    "resources": {
      "cpu": {"request": "1", "limit": "1"},
      "memory": {"request": "1Gi", "limit": "1Gi"},
      "ephemeralStorage": {"request": "256Mi", "limit": "1Gi"}
    },
    "runningDeadlineSeconds": 600,
    "capabilities": {"networkProfile": "none"},
    "env": {}, "labels": {}, "idempotencyKey": "quickstart-python-1"
  }')
JOB_ID=$(printf '%s' "$JOB" | jq -er '.jobId')

Poll GET /jobs/{jobId} until the job finishes, then read GET /jobs/{jobId}/logs:

while :; do
  JOB_STATUS=$(curl -fsS "https://grid-lab.zyxio.xyz/jobs/$JOB_ID" \
    -H "x-api-key: $BROWSERGRID_API_KEY") || break
  JOB_PHASE=$(printf '%s' "$JOB_STATUS" | jq -er '.phase')
  printf '%s\n' "$JOB_PHASE"
  case "$JOB_PHASE" in
    SUCCEEDED|FAILED|TIMED_OUT|CANCELLED) break ;;
  esac
  sleep 2
done
curl -fsS "https://grid-lab.zyxio.xyz/jobs/$JOB_ID/logs" \
  -H "x-api-key: $BROWSERGRID_API_KEY"

A successful run prints 42. Logs are available after the attempt ends; if it could not start, inspect termination in JOB_STATUS. Resending the same body and idempotencyKey returns the same job; change the key for another run.

This job reserves 7,733 µ€, about €0.0077 (1 vCPU and 1 GiB for 600 seconds), and is charged only for the time it actually ran.

5. Provision a managed service

PostgreSQL + PgBouncer, Valkey / Redis, MLflow and LiteLLM are live and self-service. Choose one with SERVICE below, or use your managed services.

SERVICE=postgres # postgres, redis, mlflow or litellm
SERVICE_REQUEST_ID=$(uuidgen)
SERVICE_REQUEST=$(jq -n --arg requestId "$SERVICE_REQUEST_ID" --arg service "$SERVICE" \
  '{requestId: $requestId, service: $service, name: ("example-" + $service), note: "Application workspace"}')
SERVICE_INSTANCE=$(curl -fsS https://grid-lab.zyxio.xyz/managed-services/instances \
  -H "x-api-key: $BROWSERGRID_API_KEY" \
  -H "content-type: application/json" \
  -d "$SERVICE_REQUEST")
INSTANCE_ID=$(printf '%s' "$SERVICE_INSTANCE" | jq -er '.id')
printf '%s\n' "$SERVICE_INSTANCE" | jq '{id, service, state, requestedAction, statusNote}'

Omitting profile selects the private platform. Keep SERVICE_REQUEST_ID and the same body when retrying a request after a connection failure: the same request returns the existing instance. Use a new UUID for a new instance.

A 201 response means the instance is ready; 202 means it is not ready. Poll its status until setup completes. If it becomes blocked, read statusNote and open your managed services.

while :; do
  SERVICE_INSTANCE=$(curl -fsS "https://grid-lab.zyxio.xyz/managed-services/instances/$INSTANCE_ID" \
    -H "x-api-key: $BROWSERGRID_API_KEY") || break
  printf '%s\n' "$SERVICE_INSTANCE" | jq '{state, requestedAction, statusNote}'
  SERVICE_STATE=$(printf '%s' "$SERVICE_INSTANCE" | jq -r '.state')
  case "$SERVICE_STATE" in
    ready|blocked|suspended|retired) break ;;
  esac
  sleep 2
done

Once state is ready and requestedAction is none, download the credentials. The subshell saves them with private file permissions; move the file into your application's secret store.

printf '%s' "$SERVICE_INSTANCE" | jq -e '.state == "ready" and .requestedAction == "none"' >/dev/null && (
  umask 077
  curl -fsS -X POST "https://grid-lab.zyxio.xyz/managed-services/instances/$INSTANCE_ID/credentials" \
    -H "x-api-key: $BROWSERGRID_API_KEY" \
    -o "managed-service-$INSTANCE_ID.json"
)

Tier-1 base prices are €49.00 for PostgreSQL, €15.00 for Valkey / Redis, €39.00 for MLflow and €29.00 for LiteLLM per workspace-month, prorated per active millisecond. Active time continues to accrue until suspension or retirement completes. Managed services shows connection and lifecycle examples, including how to stop the example instance's plan.

6. Connect an MCP client

With a key, register the endpoint with its header. For Claude Code:

claude mcp add --transport http browsergrid https://grid-lab.zyxio.xyz/mcp \
  --header "x-api-key: $BROWSERGRID_API_KEY"

Or sign in with Zyxio instead of a key, once Zyxio has linked that sign-in to your account: register the URL alone and follow the client's sign-in prompt. See MCP.