Quickstart
Three short walk-throughs: drive a cloud browser with Playwright, turn a URL into Markdown, and hand a task to the AI agent.
Pick one of the three below. Each takes about five minutes, and all three start with an API key. You need a terminal, and Node 18 or later or Python if you use an SDK.
- Browser sessions: a cloud browser you drive with Playwright.
- Web API: a web page as Markdown, with one request.
- AI agent: a task in plain words, done for you.
Get an API key
Create an account. We email you a 6-digit code: enter it in the console to confirm your email (it works for 15 minutes; signing in later never asks for it). Then create an API key in the console. Put the key and the API address in your shell, so every example below can use them:
export BOXLINE_API_KEY="your-api-key"export BOXLINE_API_URL="https://api.boxline.dev"The SDKs, the CLI and the MCP server read both variables. Without BOXLINE_API_URL they use the public API, https://api.boxline.dev. Set it only for another API, such as one on your own computer.
Browser sessions
A session is one isolated machine with a Chrome browser. You connect Playwright to it over the Chrome DevTools Protocol (CDP), the same way you would drive a local Chrome. A session can also have a shell, with or without the browser: see The three kinds of session.
1. Install
npm install @boxline/sdk playwrightpip install boxline-sdk playwrightIn Python you install boxline-sdk and import boxline. Both SDKs are open source: see the Node SDK and Python SDK pages for the repositories and the registries.
The CLI tabs below use the boxline command. Install it with npm install -g @boxline/cli (or run it once with npx @boxline/cli), then run boxline login. See the CLI page.
2. Start a session and drive it
This creates a session, prints the link to watch it live, opens a page, and stops the session at the end.
# Create a session and keep its id and connectUrl.curl -X POST https://api.boxline.dev/v1/sessions \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"browser": true}'# Connect Playwright to the connectUrl from the response (quickstart.mjs below).CONNECT_URL="wss://…" node quickstart.mjs# Stop the session when you are done. Billing stops at that second.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \ -H "x-api-key: $BOXLINE_API_KEY"import { Boxline } from "@boxline/sdk";import { chromium } from "playwright";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst session = await bx.sessions.create();console.log("Watch it live:", session.liveUrl);try { const browser = await chromium.connectOverCDP(session.connectUrl!); const context = browser.contexts()[0]; const page = context.pages()[0] ?? (await context.newPage()); await page.goto("https://example.com"); console.log("Title:", await page.title()); await browser.close();} finally { await session.stop(); // billing stops here}from boxline import Boxlinefrom playwright.sync_api import sync_playwrightbx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URL# Leaving the "with" block stops the session.with bx.sessions.create() as session: print("Watch it live:", session.live_url) with sync_playwright() as p: browser = p.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] if context.pages else context.new_page() page.goto("https://example.com") print("Title:", page.title())With curl, this is the small script that connects to the session:
import { chromium } from "playwright";const browser = await chromium.connectOverCDP(process.env.CONNECT_URL!);const context = browser.contexts()[0];const page = context.pages()[0] ?? (await context.newPage());await page.goto("https://example.com");console.log("Title:", await page.title());await browser.close();3. What you get back
Creating a session returns the session object:
{ "id": "3f6c2a9e-8b1d-4c7a-9e2f-5d0b7a1c4e88", "status": "RUNNING", "projectId": "…", "region": "…", "browser": true, "shell": false, "keepAlive": false, "timeout": 300, "idleTimeout": null, "createdAt": "2026-09-28T12:00:00.000Z", "startedAt": "2026-09-28T12:00:00.000Z", "expiresAt": "2026-09-28T12:05:00.000Z", "stoppedAt": null, "stopReason": null, "deletesAt": null, "deletedAt": null, "deleteReason": null, "connectUrl": "wss://…/v1/connect?sessionId=3f6c2a9e-8b1d-4c7a-9e2f-5d0b7a1c4e88&token=…", "liveUrl": "https://…/live/3f6c2a9e-8b1d-4c7a-9e2f-5d0b7a1c4e88?token=…", "terminalUrl": null, "proxy": null, "captcha": "ask", "attention": null, "blockAds": false, "blockedRequests": 0, "cookieBanners": "reject", "extensions": [], "browserSettings": { "mode": "standard", "locale": null, "timezone": null }, "viewport": { "width": 1280, "height": 720 }, "trajectoriesEligible": false, "workspacePath": "/workspace", "profileId": null, "profilePersist": false, "userMetadata": {}, "checkpointAt": null, "recoveries": 0, "error": null, "recordSession": true, "hasRecording": false, "setup": [], "setupStatus": "none", "setupError": null, "env": [], "credentials": [], "usage": { "seconds": 0, "costUsd": 0 }}idis the session’s id (a UUID). Every other session call uses it.connectUrlis the CDP address of the session’s browser.liveUrlopens the live view. Open it while the script runs to watch the browser, and click in it to take over.
Both URLs carry a signed token. Anyone who has them can control the browser, so treat them like passwords. Getting the session again (GET /v1/sessions/:id) returns fresh links every time. See Live view.
Web API
When you only need to read a page, skip the session. POST /v1/fetch opens the URL in a real browser in a sandbox, and returns the page as Markdown, HTML or text.
curl -X POST https://api.boxline.dev/v1/fetch \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"url": "https://example.com", "format": "markdown"}'import { Boxline } from "@boxline/sdk";const bx = new Boxline();const page = await bx.fetch("https://example.com", { format: "markdown" });console.log(page.title);console.log(page.content);from boxline import Boxlinebx = Boxline()page = bx.fetch("https://example.com", format="markdown")print(page["title"])print(page["content"])boxline fetch https://example.com # the page as Markdown, on stdout{ "url": "https://example.com", "finalUrl": "https://example.com/", "status": 200, "title": "Example Domain", "content": "# Example Domain\n\nThis domain is for use in …", "captcha": null, "ms": 1840}finalUrl is the address after redirects, status the page’s HTTP status, and captcha the kind of CAPTCHA that was on the page, if any. The same request style gives you a screenshot, a PDF, structured data or a whole site crawl. See the Web overview.
AI agent
Describe a task in plain words. The agent works in a session of its own, with a browser, a shell or both, until it has an answer. You can watch it in the live view. Choose the kind of session with session: see The run’s own session.
# Start the run.curl -X POST https://api.boxline.dev/v1/agent/runs \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"task": "Open example.com and say in two sentences what the page is for."}'# Poll it with the id from the response until status is "completed" or "failed".curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \ -H "x-api-key: $BOXLINE_API_KEY"import { Boxline } from "@boxline/sdk";const bx = new Boxline();const { id, sessionId } = await bx.agent.run({ task: "Open example.com and say in two sentences what the page is for.",});const session = await bx.sessions.get(sessionId);console.log("Watch it live:", session.liveUrl);const run = await bx.agent.wait(id); // polls until the run finishesconsole.log(run.status, run.result);from boxline import Boxlinebx = Boxline()started = bx.agent.run(task="Open example.com and say in two sentences what the page is for.")session = bx.sessions.get(started["sessionId"])print("Watch it live:", session.live_url)run = bx.agent.wait(started["id"]) # polls until the run finishesprint(run["status"], run["result"])# Shows each step as it happens; the answer comes last, on stdout.boxline run "Open example.com and say in two sentences what the page is for."result holds the agent’s answer, and steps every message and tool call. The run stops its own session when it ends. Its model cost is in usage.costUsd, on top of the session’s time.
Need to sign in, or keep a password out of the model’s sight? See variables and pausing a run for a person.
Next steps
- Add a shellRun Python or Node next to the browser.
- Work with filesDownloads, uploads and the /workspace disk.
- Stay signed inReuse cookies and storage across sessions with browser profiles.
- Use the SDKsEvery method of the Node and Python SDKs.
Everything Boxline does is also plain HTTP. See the API reference.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.