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.