API Reference

Complete reference for the BabelWrap REST API. Manage browser sessions, perform actions with natural language, and track usage.

Base URL & Authentication

Base URL

https://api.babelwrap.com/v1

Authentication

All API requests (except /v1/health) require a Bearer token in the Authorization header:

Authorization: Bearer bw_your_api_key_here
Get your API key from the Dashboard. Keys are prefixed with bw_ and should be kept secret.

Endpoints

MethodPathDescription
POST/v1/sessionsCreate a browser session
GET/v1/sessions/{id}Get session info
DEL/v1/sessions/{id}Close session
POST/v1/sessions/{id}/navigateNavigate to URL
POST/v1/sessions/{id}/snapshotRead current page
POST/v1/sessions/{id}/clickClick element
POST/v1/sessions/{id}/fillFill input field
POST/v1/sessions/{id}/submitSubmit form
POST/v1/sessions/{id}/extractExtract structured data
POST/v1/sessions/{id}/screenshotTake screenshot
POST/v1/sessions/{id}/pressPress keyboard key
POST/v1/sessions/{id}/uploadUpload file to input
POST/v1/sessions/{id}/hoverHover over element
POST/v1/sessions/{id}/backNavigate back
POST/v1/sessions/{id}/forwardNavigate forward
POST/v1/sessions/{id}/scrollScroll page
POST/v1/sessions/{id}/wait_forWait for condition
POST/v1/sessions/{id}/batchExecute multiple actions
GET/v1/sessions/{id}/pagesList open pages
POST/v1/sessions/{id}/switch_pageSwitch tab
POST/v1/sessions/{id}/cookiesAdd cookies
GET/v1/sessions/{id}/historyGet action history
GET/v1/usageGet usage stats
GET/v1/healthHealth check (no auth)

Sessions

Sessions represent isolated browser contexts. Each session has its own cookies, localStorage, and page state. All browser interactions happen within a session.

Create Session

POST /v1/sessions

Create a new browser session. The session is immediately active and ready for actions.

Request Body

FieldTypeRequiredDescription
metadataobjectNoArbitrary key-value labels for this session (e.g., {"label": "checkout-flow"})

Response 201 Created

{
  "session_id": "d4e5f6a7-1234-5678-9abc-def012345678",
  "status": "active",
  "created_at": "2026-04-03T10:00:00Z",
  "expires_at": "2026-04-03T11:00:00Z"
}
FieldTypeDescription
session_idstring (uuid)Unique identifier for the session
statusstringAlways "active" on creation
created_atstring (ISO 8601)When the session was created
expires_atstring (ISO 8601)When the session will expire if no actions are taken (default: 1 hour)

Errors

StatusCodeDescription
429session_limit_exceededYou have reached your plan's concurrent session limit
429rate_limitedToo many requests. Wait and retry.

Examples

curl -X POST https://api.babelwrap.com/v1/sessions \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"label": "my-flow"}}'
import httpx

resp = await client.post(
    "https://api.babelwrap.com/v1/sessions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={"metadata": {"label": "my-flow"}},
)
session_id = resp.json()["session_id"]
const resp = await fetch("https://api.babelwrap.com/v1/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ metadata: { label: "my-flow" } }),
});
const { session_id } = await resp.json();

Get Session

GET /v1/sessions/{session_id}

Retrieve the current state of a session.

Path Parameters

ParameterTypeDescription
session_idstring (uuid)The session ID

Response 200 OK

{
  "session_id": "d4e5f6a7-1234-5678-9abc-def012345678",
  "status": "active",
  "current_url": "https://example.com/dashboard",
  "created_at": "2026-04-03T10:00:00Z",
  "last_active": "2026-04-03T10:05:23Z",
  "expires_at": "2026-04-03T11:05:23Z",
  "metadata": { "label": "my-flow" }
}
FieldTypeDescription
session_idstring (uuid)Unique identifier
statusstringOne of: active, closed, expired, error
current_urlstring or nullThe URL the session is currently on, or null if no navigation has occurred
created_atstring (ISO 8601)When the session was created
last_activestring (ISO 8601)When the last action was performed
expires_atstring (ISO 8601)When the session will expire (resets on each action)
metadataobject or nullUser-defined labels

Errors

StatusCodeDescription
404session_not_foundThe session does not exist or belongs to another user

Examples

curl https://api.babelwrap.com/v1/sessions/d4e5f6a7-1234-5678-9abc-def012345678 \
  -H "Authorization: Bearer $BABELWRAP_API_KEY"
resp = await client.get(
    f"https://api.babelwrap.com/v1/sessions/{session_id}",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
session = resp.json()
print(f"Status: {session['status']}, URL: {session['current_url']}")
const resp = await fetch(
  `https://api.babelwrap.com/v1/sessions/${session_id}`,
  { headers: { Authorization: `Bearer ${API_KEY}` } }
);
const session = await resp.json();
console.log(`Status: ${session.status}, URL: ${session.current_url}`);

Close Session

DEL /v1/sessions/{session_id}

Close a session and release all associated browser resources. The session's browser context, cookies, and page state are permanently destroyed.

Path Parameters

ParameterTypeDescription
session_idstring (uuid)The session ID to close

Response 200 OK

{
  "success": true,
  "session_id": "d4e5f6a7-1234-5678-9abc-def012345678"
}

Errors

StatusCodeDescription
404session_not_foundThe session does not exist or belongs to another user

Examples

curl -X DELETE https://api.babelwrap.com/v1/sessions/d4e5f6a7-1234-5678-9abc-def012345678 \
  -H "Authorization: Bearer $BABELWRAP_API_KEY"
resp = await client.delete(
    f"https://api.babelwrap.com/v1/sessions/{session_id}",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
print(resp.json()["success"])  # True
const resp = await fetch(
  `https://api.babelwrap.com/v1/sessions/${session_id}`,
  { method: "DELETE", headers: { Authorization: `Bearer ${API_KEY}` } }
);
const { success } = await resp.json();
Always close sessions when you are done. Unclosed sessions count towards your concurrent session limit until they expire (1 hour of inactivity).

Session Lifecycle

Created (POST /sessions)
    |
    v
  Active  <--- each action resets expiry timer
    |
    +---> Closed   (DELETE /sessions/:id) - manual cleanup
    |
    +---> Expired  (auto, after 1h inactivity) - automatic cleanup
    |
    +---> Error    (unrecoverable browser failure)
  • Active: The session is ready for actions. Each action resets the expiration timer to 1 hour from now.
  • Closed: The session was manually closed via DELETE. Resources have been freed.
  • Expired: The session was automatically closed after 1 hour of inactivity.
  • Error: The session's browser context encountered an unrecoverable error. Create a new session.

Actions

All action endpoints operate within a session and return a standard response envelope. Every action returns a snapshot of the current page state after the action completes, so the caller always knows what the page looks like.

Standard Response Envelope

All action endpoints (except extract and screenshot, which extend it) return this structure:

{
  "action_id": "a1b2c3d4-5678-9abc-def0-123456789abc",
  "success": true,
  "duration_ms": 340,
  "snapshot": {
    "url": "https://example.com",
    "title": "Example Page",
    "content": "...",
    "inputs": [],
    "actions": [],
    "navigation": [],
    "alerts": [],
    "forms": []
  },
  "error": null
}
FieldTypeDescription
action_idstring (uuid)Unique identifier for this action
successbooleanWhether the action completed successfully
duration_msintegerHow long the action took in milliseconds
snapshotobjectStructured snapshot of the page after the action (see Snapshot Format)
errorstring or nullError message if success is false

Common Action Errors

All action endpoints may return these errors:

StatusCodeDescription
404session_not_foundSession does not exist or belongs to another user
410session_expiredSession has expired or been closed
429usage_limit_exceededMonthly action limit reached
429rate_limitedToo many requests
POST /v1/sessions/{session_id}/navigate

Navigate to a URL. The browser loads the page and returns a snapshot of the result.

Request Body

FieldTypeRequiredDescription
urlstringYesThe URL to navigate to. Must include protocol (e.g., https://). Max 2048 characters.

Response 200 OK

Standard action envelope with snapshot of the loaded page.

{
  "action_id": "a1b2c3d4-5678-9abc-def0-123456789abc",
  "success": true,
  "duration_ms": 1240,
  "snapshot": {
    "url": "https://news.ycombinator.com",
    "title": "Hacker News",
    "content": "Hacker News new | past | comments | ask | show ...",
    "inputs": [],
    "actions": [
      { "id": "story-1", "label": "Show HN: BabelWrap", "type": "link" }
    ],
    "navigation": ["new", "past", "comments", "ask", "show", "jobs"],
    "alerts": [],
    "forms": []
  },
  "error": null
}

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/navigate" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://news.ycombinator.com"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/navigate",
    headers=HEADERS,
    json={"url": "https://news.ycombinator.com"},
)
snapshot = resp.json()["snapshot"]
print(f"Title: {snapshot['title']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/navigate`, {
  method: "POST",
  headers,
  body: JSON.stringify({ url: "https://news.ycombinator.com" }),
});
const { snapshot } = await resp.json();
console.log(`Title: ${snapshot.title}`);

Snapshot

POST /v1/sessions/{session_id}/snapshot

Get the current page state without performing any action. Useful for re-reading a page or checking what changed.

Request Body

Empty ({}). No parameters required.

Response 200 OK

Standard action envelope with snapshot of the current page.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/snapshot" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/snapshot",
    headers=HEADERS,
    json={},
)
snapshot = resp.json()["snapshot"]
const resp = await fetch(`${BASE}/sessions/${session_id}/snapshot`, {
  method: "POST",
  headers,
  body: JSON.stringify({}),
});
const { snapshot } = await resp.json();
Snapshot counts as an action towards your monthly usage limit.

Click

POST /v1/sessions/{session_id}/click

Click an element on the page identified by a natural language description. BabelWrap uses LLM-assisted resolution to match your description to the correct element.

Request Body

FieldTypeRequiredDescription
targetstringYesNatural language description of the element to click. Max 500 characters.
Good target examples:
  • "the Login button"
  • "first search result"
  • "Next page link"
  • "the Add to Cart button for Wireless Headphones"
Be specific -- the more descriptive the target, the more reliable the match.

Response 200 OK

Standard action envelope with snapshot of the page after clicking.

Additional Errors

StatusCodeDescription
422target_not_foundNo element matching the description was found
422ambiguous_targetMultiple elements match. Response includes candidates list.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/click" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target": "the Sign In button"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/click",
    headers=HEADERS,
    json={"target": "the Sign In button"},
)
snapshot = resp.json()["snapshot"]
const resp = await fetch(`${BASE}/sessions/${session_id}/click`, {
  method: "POST",
  headers,
  body: JSON.stringify({ target: "the Sign In button" }),
});
const { snapshot } = await resp.json();

Ambiguous Target Error Example

{
  "error": {
    "code": "ambiguous_target",
    "message": "Multiple elements match 'Submit button'. Please be more specific.",
    "candidates": [
      { "id": "submit-form-1", "label": "Submit Application", "type": "button" },
      { "id": "submit-form-2", "label": "Submit Feedback", "type": "button" }
    ]
  }
}

Fill

POST /v1/sessions/{session_id}/fill

Fill an input field with a value. The field is identified by a natural language description.

Request Body

FieldTypeRequiredDescription
targetstringYesNatural language description of the input field. Max 500 characters.
valuestringYesThe value to fill in. Max 10,000 characters.

Response 200 OK

Standard action envelope with snapshot showing the field's updated value.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/fill" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target": "Email address field", "value": "alice@example.com"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/fill",
    headers=HEADERS,
    json={"target": "Email address field", "value": "alice@example.com"},
)
snapshot = resp.json()["snapshot"]
# Verify the field was filled
email_input = next(i for i in snapshot["inputs"] if "email" in i["label"].lower())
print(f"Email value: {email_input['value']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/fill`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    target: "Email address field",
    value: "alice@example.com",
  }),
});
const { snapshot } = await resp.json();

Submit

POST /v1/sessions/{session_id}/submit

Submit a form on the current page. If target is omitted, BabelWrap submits the most prominent form.

Request Body

FieldTypeRequiredDescription
targetstringNoNatural language description of which form to submit. If omitted, submits the most prominent form.

Response 200 OK

Standard action envelope with snapshot of the resulting page (which may be a new page if the form triggers navigation).

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/submit" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target": "the login form"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/submit",
    headers=HEADERS,
    json={"target": "the login form"},
)
snapshot = resp.json()["snapshot"]
print(f"Redirected to: {snapshot['url']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/submit`, {
  method: "POST",
  headers,
  body: JSON.stringify({ target: "the login form" }),
});
const { snapshot } = await resp.json();
console.log(`Redirected to: ${snapshot.url}`);
Use submit with no target to submit the default form. This is useful on pages with a single prominent form like login or search.

Extract

POST /v1/sessions/{session_id}/extract

Extract structured data from the current page using a natural language query. Returns both the extracted data and a page snapshot.

Request Body

FieldTypeRequiredDescription
querystringYesNatural language description of what data to extract. Max 2,000 characters.

Response 200 OK

Extended action envelope with an additional data field:

{
  "action_id": "f6a7b8c9-abcd-ef01-2345-6789abcdef01",
  "success": true,
  "duration_ms": 450,
  "data": [
    { "name": "Widget A", "price": "$29.99" },
    { "name": "Widget B", "price": "$49.99" }
  ],
  "snapshot": { /* ... */ },
  "error": null
}
FieldTypeDescription
dataarray or objectThe extracted structured data. Usually an array of objects, but may be a single object for scalar queries.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/extract" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "all product names and prices"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/extract",
    headers=HEADERS,
    json={"query": "all product names and prices"},
)
products = resp.json()["data"]
for product in products:
    print(f"{product['name']}: {product['price']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/extract`, {
  method: "POST",
  headers,
  body: JSON.stringify({ query: "all product names and prices" }),
});
const { data: products } = await resp.json();
products.forEach((p) => console.log(`${p.name}: ${p.price}`));

Screenshot

POST /v1/sessions/{session_id}/screenshot

Take a screenshot of the current page. Returns a base64-encoded PNG image. Intended for debugging.

Request Body

Empty ({}). No parameters required.

Response 200 OK

Extended action envelope with an additional image field:

{
  "action_id": "a7b8c9d0-bcde-f012-3456-789abcdef012",
  "success": true,
  "duration_ms": 320,
  "image": "iVBORw0KGgoAAAANSUhEUgAAA...",
  "snapshot": { /* ... */ },
  "error": null
}
FieldTypeDescription
imagestringBase64-encoded PNG screenshot of the page

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/screenshot" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' | jq -r '.image' | base64 --decode > screenshot.png
import base64

resp = await client.post(
    f"{BASE}/sessions/{session_id}/screenshot",
    headers=HEADERS,
    json={},
)
image_b64 = resp.json()["image"]
with open("screenshot.png", "wb") as f:
    f.write(base64.b64decode(image_b64))
const resp = await fetch(`${BASE}/sessions/${session_id}/screenshot`, {
  method: "POST",
  headers,
  body: JSON.stringify({}),
});
const { image } = await resp.json();
// image is base64-encoded PNG
const buffer = Buffer.from(image, "base64");
await fs.writeFile("screenshot.png", buffer);
Screenshots count as an action towards your monthly usage limit. Use them for debugging, not as a primary data source.

Press

POST /v1/sessions/{session_id}/press

Press a keyboard key. Useful for submitting forms with Enter, navigating with Tab, dismissing dialogs with Escape, or interacting with custom keyboard shortcuts.

Request Body

FieldTypeRequiredDescription
keystringYesKey to press (e.g., Enter, Tab, Escape, ArrowDown, ArrowUp, Backspace)

Response 200 OK

Standard action envelope with snapshot of the page after the key press.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/press" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "Enter"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/press",
    headers=HEADERS,
    json={"key": "Enter"},
)
snapshot = resp.json()["snapshot"]
const resp = await fetch(`${BASE}/sessions/${session_id}/press`, {
  method: "POST",
  headers,
  body: JSON.stringify({ key: "Enter" }),
});
const { snapshot } = await resp.json();

Upload

POST /v1/sessions/{session_id}/upload

Upload a file to a file input field on the page. The file is provided as base64-encoded content and matched to the target input element using natural language.

Request Body

FieldTypeRequiredDescription
targetstringYesDescription of the file input field. Max 500 characters.
file_base64stringYesBase64-encoded file content
filenamestringYesName of the file with extension (e.g., report.pdf)

Response 200 OK

Standard action envelope with snapshot of the page after the file is attached.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/upload" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "the resume upload field",
    "file_base64": "JVBERi0xLjQgMSAwIG9i...",
    "filename": "resume.pdf"
  }'
import base64

with open("resume.pdf", "rb") as f:
    file_b64 = base64.b64encode(f.read()).decode()

resp = await client.post(
    f"{BASE}/sessions/{session_id}/upload",
    headers=HEADERS,
    json={
        "target": "the resume upload field",
        "file_base64": file_b64,
        "filename": "resume.pdf",
    },
)
snapshot = resp.json()["snapshot"]
const fs = await import("fs/promises");
const fileBuffer = await fs.readFile("resume.pdf");
const fileBase64 = fileBuffer.toString("base64");

const resp = await fetch(`${BASE}/sessions/${session_id}/upload`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    target: "the resume upload field",
    file_base64: fileBase64,
    filename: "resume.pdf",
  }),
});
const { snapshot } = await resp.json();

Hover

POST /v1/sessions/{session_id}/hover

Hover over an element on the page. Useful for revealing tooltips, dropdown menus, or triggering hover states.

Request Body

FieldTypeRequiredDescription
targetstringYesNatural language description of the element to hover over. Max 500 characters.

Response 200 OK

Standard action envelope with snapshot of the page after hovering (may include newly visible tooltips or menus).

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/hover" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target": "the user avatar in the top right"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/hover",
    headers=HEADERS,
    json={"target": "the user avatar in the top right"},
)
snapshot = resp.json()["snapshot"]
# Check for newly visible dropdown menu
const resp = await fetch(`${BASE}/sessions/${session_id}/hover`, {
  method: "POST",
  headers,
  body: JSON.stringify({ target: "the user avatar in the top right" }),
});
const { snapshot } = await resp.json();

Back

POST /v1/sessions/{session_id}/back

Navigate back in the browser history, equivalent to clicking the browser's back button.

Request Body

Empty ({}). No parameters required.

Response 200 OK

Standard action envelope with snapshot of the previous page.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/back" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/back",
    headers=HEADERS,
    json={},
)
snapshot = resp.json()["snapshot"]
print(f"Back to: {snapshot['url']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/back`, {
  method: "POST",
  headers,
  body: JSON.stringify({}),
});
const { snapshot } = await resp.json();
console.log(`Back to: ${snapshot.url}`);

Forward

POST /v1/sessions/{session_id}/forward

Navigate forward in the browser history, equivalent to clicking the browser's forward button.

Request Body

Empty ({}). No parameters required.

Response 200 OK

Standard action envelope with snapshot of the next page in history.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/forward" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/forward",
    headers=HEADERS,
    json={},
)
snapshot = resp.json()["snapshot"]
print(f"Forward to: {snapshot['url']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/forward`, {
  method: "POST",
  headers,
  body: JSON.stringify({}),
});
const { snapshot } = await resp.json();
console.log(`Forward to: ${snapshot.url}`);

Scroll

POST /v1/sessions/{session_id}/scroll

Scroll the page up or down. Useful for loading lazy content, reaching elements below the fold, or navigating long pages.

Request Body

FieldTypeRequiredDescription
directionstringNoScroll direction: "up" or "down". Default: "down".
amountstringNoScroll amount: "page", "half", or a pixel count (e.g., "500"). Default: "page".

Response 200 OK

Standard action envelope with snapshot of the page after scrolling (content may have changed if lazy-loading is present).

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/scroll" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"direction": "down", "amount": "page"}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/scroll",
    headers=HEADERS,
    json={"direction": "down", "amount": "half"},
)
snapshot = resp.json()["snapshot"]
const resp = await fetch(`${BASE}/sessions/${session_id}/scroll`, {
  method: "POST",
  headers,
  body: JSON.stringify({ direction: "down", amount: "half" }),
});
const { snapshot } = await resp.json();

Wait For

POST /v1/sessions/{session_id}/wait_for

Wait for a condition to be met before returning. Useful for waiting on dynamic content, AJAX responses, or navigation changes. Provide at least one of text, selector, or url_contains.

Request Body

FieldTypeRequiredDescription
textstringNoWait for this text to appear on the page
selectorstringNoWait for a CSS selector to be present in the DOM
url_containsstringNoWait for the URL to contain this string
timeout_msintegerNoMaximum time to wait in milliseconds. Default: 10000 (10 seconds).

Response 200 OK

Standard action envelope with snapshot of the page once the condition is met.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/wait_for" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Results loaded", "timeout_ms": 5000}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/wait_for",
    headers=HEADERS,
    json={"text": "Results loaded", "timeout_ms": 5000},
)
snapshot = resp.json()["snapshot"]
const resp = await fetch(`${BASE}/sessions/${session_id}/wait_for`, {
  method: "POST",
  headers,
  body: JSON.stringify({ text: "Results loaded", timeout_ms: 5000 }),
});
const { snapshot } = await resp.json();
If the condition is not met within the timeout, the action returns "success": false with an error message. The snapshot still reflects the page state at timeout.

Batch

POST /v1/sessions/{session_id}/batch

Execute multiple actions in sequence within a single API call. Each action in the batch is executed one after another. Useful for reducing round trips when performing multi-step flows.

Request Body

FieldTypeRequiredDescription
actionsarrayYesArray of action objects. Each object has action (string, required) and optional fields: target, value, key, query.
continue_on_errorbooleanNoIf true, continue executing remaining actions after a failure. Default: false.

Response 200 OK

Returns an array of action results, one per action in the batch:

{
  "success": true,
  "results": [
    {
      "action_id": "a1b2c3d4-...",
      "success": true,
      "duration_ms": 120,
      "snapshot": { /* ... */ },
      "error": null
    },
    {
      "action_id": "b2c3d4e5-...",
      "success": true,
      "duration_ms": 85,
      "snapshot": { /* ... */ },
      "error": null
    }
  ]
}

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/batch" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actions": [
      {"action": "fill", "target": "email field", "value": "alice@example.com"},
      {"action": "fill", "target": "password field", "value": "secret123"},
      {"action": "click", "target": "Sign In button"}
    ]
  }'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/batch",
    headers=HEADERS,
    json={
        "actions": [
            {"action": "fill", "target": "email field", "value": "alice@example.com"},
            {"action": "fill", "target": "password field", "value": "secret123"},
            {"action": "click", "target": "Sign In button"},
        ],
    },
)
results = resp.json()["results"]
for r in results:
    print(f"{r['action_id']}: {r['success']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/batch`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    actions: [
      { action: "fill", target: "email field", value: "alice@example.com" },
      { action: "fill", target: "password field", value: "secret123" },
      { action: "click", target: "Sign In button" },
    ],
  }),
});
const { results } = await resp.json();
results.forEach((r) => console.log(`${r.action_id}: ${r.success}`));
Each action in a batch counts individually towards your monthly usage. A batch of 3 actions consumes 3 action credits.

List Pages

GET /v1/sessions/{session_id}/pages

List all open pages (tabs) in the session. Returns the index, URL, and title of each page. Use the index with switch_page to change the active tab.

Request Body

No request body. This is a GET request.

Response 200 OK

{
  "pages": [
    { "index": 0, "url": "https://example.com", "title": "Example" },
    { "index": 1, "url": "https://example.com/about", "title": "About Us" }
  ],
  "active_index": 0
}

Examples

curl "https://api.babelwrap.com/v1/sessions/$SESSION_ID/pages" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY"
resp = await client.get(
    f"{BASE}/sessions/{session_id}/pages",
    headers=HEADERS,
)
data = resp.json()
for page in data["pages"]:
    print(f"[{page['index']}] {page['title']} - {page['url']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/pages`, {
  headers,
});
const { pages, active_index } = await resp.json();
pages.forEach((p) => console.log(`[${p.index}] ${p.title} - ${p.url}`));

Switch Page

POST /v1/sessions/{session_id}/switch_page

Switch the active page (tab) in the session. Use pages to list available tabs and get the index.

Request Body

FieldTypeRequiredDescription
indexintegerYesIndex of the page to switch to (from the pages endpoint)

Response 200 OK

Standard action envelope with snapshot of the newly active page.

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/switch_page" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"index": 1}'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/switch_page",
    headers=HEADERS,
    json={"index": 1},
)
snapshot = resp.json()["snapshot"]
print(f"Switched to: {snapshot['title']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/switch_page`, {
  method: "POST",
  headers,
  body: JSON.stringify({ index: 1 }),
});
const { snapshot } = await resp.json();
console.log(`Switched to: ${snapshot.title}`);

Add Cookies

POST /v1/sessions/{session_id}/cookies

Add cookies to the session's browser context. Useful for setting authentication tokens, session IDs, or other cookies before navigating to a site.

Request Body

FieldTypeRequiredDescription
cookiesarrayYesArray of cookie objects. Each object should include name, value, domain, and optionally path, secure, httpOnly, sameSite, expires.

Response 200 OK

{
  "success": true,
  "cookies_added": 2
}

Examples

curl -X POST "https://api.babelwrap.com/v1/sessions/$SESSION_ID/cookies" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cookies": [
      {"name": "session_token", "value": "abc123", "domain": ".example.com"},
      {"name": "prefs", "value": "dark_mode=1", "domain": ".example.com", "path": "/"}
    ]
  }'
resp = await client.post(
    f"{BASE}/sessions/{session_id}/cookies",
    headers=HEADERS,
    json={
        "cookies": [
            {"name": "session_token", "value": "abc123", "domain": ".example.com"},
            {"name": "prefs", "value": "dark_mode=1", "domain": ".example.com", "path": "/"},
        ],
    },
)
print(f"Added {resp.json()['cookies_added']} cookies")
const resp = await fetch(`${BASE}/sessions/${session_id}/cookies`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    cookies: [
      { name: "session_token", value: "abc123", domain: ".example.com" },
      { name: "prefs", value: "dark_mode=1", domain: ".example.com", path: "/" },
    ],
  }),
});
const { cookies_added } = await resp.json();
console.log(`Added ${cookies_added} cookies`);
Set cookies before navigating to the target site. This is especially useful for bypassing login flows when you already have valid session tokens.

Action History

GET /v1/sessions/{session_id}/history

Get a chronological list of all actions performed in this session. Useful for debugging, auditing, or replaying workflows.

Request Body

No request body. This is a GET request.

Response 200 OK

{
  "session_id": "d4e5f6a7-1234-5678-9abc-def012345678",
  "actions": [
    {
      "action_id": "a1b2c3d4-...",
      "action": "navigate",
      "params": { "url": "https://example.com" },
      "success": true,
      "duration_ms": 1240,
      "timestamp": "2026-04-03T10:01:00Z"
    },
    {
      "action_id": "b2c3d4e5-...",
      "action": "click",
      "params": { "target": "Sign In button" },
      "success": true,
      "duration_ms": 340,
      "timestamp": "2026-04-03T10:01:05Z"
    }
  ]
}

Examples

curl "https://api.babelwrap.com/v1/sessions/$SESSION_ID/history" \
  -H "Authorization: Bearer $BABELWRAP_API_KEY"
resp = await client.get(
    f"{BASE}/sessions/{session_id}/history",
    headers=HEADERS,
)
history = resp.json()["actions"]
for entry in history:
    print(f"{entry['timestamp']} {entry['action']} - {entry['success']}")
const resp = await fetch(`${BASE}/sessions/${session_id}/history`, {
  headers,
});
const { actions } = await resp.json();
actions.forEach((a) => console.log(`${a.timestamp} ${a.action} - ${a.success}`));

Usage & Health

These endpoints let you check your current usage against plan limits and verify the API is operational.

Get Usage

GET /v1/usage

Returns current-month usage statistics for the authenticated user, including action counts, active sessions, and plan limits.

Response 200 OK

{
  "plan": "usage",
  "period": "2026-04",
  "actions_used": 1243,
  "actions_limit": null,
  "sessions_active": 2,
  "sessions_limit": 50,
  "reset_date": "2026-05-01",
  "estimated_cost_usd": 12.43
}
FieldTypeDescription
planstringCurrent plan: free or usage
periodstringCurrent billing period in YYYY-MM format
actions_usedintegerNumber of actions used this period
actions_limitinteger or nullMaximum actions allowed this period. null for usage plan (metered billing).
sessions_activeintegerNumber of currently active sessions
sessions_limitinteger or nullMaximum concurrent sessions allowed. null for unlimited.
reset_datestringDate when usage counters reset (first of next month, YYYY-MM-DD)

Examples

curl https://api.babelwrap.com/v1/usage \
  -H "Authorization: Bearer $BABELWRAP_API_KEY"
resp = await client.get(
    "https://api.babelwrap.com/v1/usage",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
usage = resp.json()
print(f"Plan: {usage['plan']}")
print(f"Actions: {usage['actions_used']}/{usage['actions_limit']}")
print(f"Sessions: {usage['sessions_active']}/{usage['sessions_limit']}")
print(f"Resets: {usage['reset_date']}")
const resp = await fetch("https://api.babelwrap.com/v1/usage", {
  headers: { Authorization: `Bearer ${API_KEY}` },
});
const usage = await resp.json();
console.log(`Plan: ${usage.plan}`);
console.log(`Actions: ${usage.actions_used}/${usage.actions_limit}`);
console.log(`Sessions: ${usage.sessions_active}/${usage.sessions_limit}`);
console.log(`Resets: ${usage.reset_date}`);

Plan Limits Reference

PlanActions / MonthConcurrent SessionsOverage Rate
Free5002N/A (hard limit)
Usage-basedUnlimited50$0.01/action

Health Check

GET /v1/health

Returns the current system status. This endpoint does not require authentication and is suitable for uptime monitoring.

Response 200 OK

{
  "status": "ok",
  "version": "0.1.0",
  "timestamp": "2026-04-03T12:00:00Z"
}
FieldTypeDescription
statusstringSystem status. "ok" when healthy.
versionstringCurrent API version
timestampstring (ISO 8601)Current server time

Examples

curl https://api.babelwrap.com/v1/health
resp = await client.get("https://api.babelwrap.com/v1/health")
health = resp.json()
print(f"Status: {health['status']}, Version: {health['version']}")
const resp = await fetch("https://api.babelwrap.com/v1/health");
const health = await resp.json();
console.log(`Status: ${health.status}, Version: ${health.version}`);
Use the health endpoint with an uptime monitoring service (e.g., BetterUptime, Pingdom) to get alerted if the API goes down.

Error Format

All API errors follow a consistent JSON format:

{
  "error": {
    "code": "session_not_found",
    "message": "Session d4e5f6a7-1234-5678-9abc-def012345678 not found"
  }
}
FieldTypeDescription
error.codestringMachine-readable error code
error.messagestringHuman-readable error description
error.candidatesarray or nullOnly present for ambiguous_target errors. Lists the candidate elements.

HTTP Status Codes

StatusMeaning
200Success
201Created (session creation)
400Bad request (invalid JSON, missing required fields)
401Unauthorized (missing or invalid API key)
404Not found (session does not exist)
410Gone (session expired or closed)
422Unprocessable entity (target not found, ambiguous target)
429Too many requests (rate limit or usage limit exceeded)
500Internal server error