Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pinchtab/pinchtab/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Snapshot API returns the page’s accessibility tree, which provides:
  • All interactive elements (buttons, links, inputs)
  • Text content and headings
  • Element roles and names
  • References (ref) for use in actions
  • Page structure and hierarchy
Snapshots use Chrome’s Accessibility API, which is faster and more reliable than DOM parsing.

Get Snapshot

GET /tabs/{id}/snapshot
curl "http://localhost:9867/tabs/tab_abc123/snapshot" | jq .

Query Parameters

filter
string
default:"all"
Filter type: all (everything) or interactive (clickable/fillable elements only)
format
string
default:"json"
Output format: json, compact, text, yaml
depth
integer
default:"-1"
Maximum tree depth (-1 for unlimited)
maxTokens
integer
Truncate output to approximately N tokens
selector
string
Scope snapshot to specific CSS selector
diff
boolean
default:"false"
Include diff with previous snapshot
output
string
Set to file to save snapshot to disk

Response (200 OK)

{
  "url": "https://example.com",
  "title": "Example Domain",
  "nodes": [
    {
      "ref": "e0",
      "role": "heading",
      "name": "Example Domain",
      "level": 1
    },
    {
      "ref": "e1",
      "role": "textbox",
      "name": "Email address",
      "value": "",
      "required": true
    },
    {
      "ref": "e2",
      "role": "textbox",
      "name": "Password",
      "value": "",
      "required": true
    },
    {
      "ref": "e3",
      "role": "button",
      "name": "Sign in"
    }
  ],
  "count": 4
}

Response Fields

url
string
required
Current page URL
title
string
required
Page title
nodes
array
required
Array of accessibility nodes
count
integer
required
Total number of nodes

Node Fields

ref
string
required
Element reference (e.g., e0, e1) for use in actions
role
string
required
Accessibility role: button, link, textbox, heading, checkbox, etc.
name
string
required
Accessible name (label, text content, or aria-label)
value
string
Current value (for inputs)
level
integer
Heading level (for headings)
checked
boolean
Checked state (for checkboxes/radios)
required
boolean
Required field indicator

Filter Types

all (default)

Returns all elements including static text and structure.
curl "http://localhost:9867/tabs/tab_abc123/snapshot?filter=all"

interactive

Returns only actionable elements (recommended for most use cases).
curl "http://localhost:9867/tabs/tab_abc123/snapshot?filter=interactive"
Interactive elements include:
  • Buttons
  • Links
  • Text inputs
  • Checkboxes
  • Radio buttons
  • Select dropdowns
  • Clickable elements
Use filter=interactive to reduce noise and focus on actionable elements.

Output Formats

json (default)

Structured JSON with full node details.
curl "http://localhost:9867/tabs/tab_abc123/snapshot?format=json"

compact

Condensed text format, one element per line.
curl "http://localhost:9867/tabs/tab_abc123/snapshot?format=compact"
Output:
# Example Domain | https://example.com | 4 nodes
e0 heading "Example Domain" (level=1)
e1 textbox "Email address" (required)
e2 textbox "Password" (required)
e3 button "Sign in"

text

Human-readable text with indentation.
curl "http://localhost:9867/tabs/tab_abc123/snapshot?format=text"

yaml

YAML format for better readability.
curl "http://localhost:9867/tabs/tab_abc123/snapshot?format=yaml"

Advanced Features

Scope to Selector

Get snapshot of a specific part of the page:
curl "http://localhost:9867/tabs/tab_abc123/snapshot?selector=#login-form&filter=interactive"

Limit Depth

Limit tree depth to reduce output size:
curl "http://localhost:9867/tabs/tab_abc123/snapshot?depth=3&filter=interactive"

Truncate by Tokens

Limit output to approximately N tokens (useful for LLMs):
curl "http://localhost:9867/tabs/tab_abc123/snapshot?maxTokens=1000&filter=interactive"
Response includes truncated: true if content was cut.

Diff with Previous

Compare with the previous snapshot to detect changes:
curl "http://localhost:9867/tabs/tab_abc123/snapshot?diff=true&filter=interactive"
Response:
{
  "diff": true,
  "added": [...],
  "changed": [...],
  "removed": [...],
  "counts": {
    "added": 2,
    "changed": 1,
    "removed": 0,
    "total": 10
  }
}

Save to File

Save snapshot to disk instead of returning in response:
curl "http://localhost:9867/tabs/tab_abc123/snapshot?output=file&format=json"
Response:
{
  "path": "/path/to/snapshot-20260303-123456.json",
  "size": 5432,
  "format": "json",
  "timestamp": "20260303-123456"
}

Complete Workflow

import requests

BASE = "http://localhost:9867"

# Start instance and open tab
inst = requests.post(f"{BASE}/instances/start",
    json={"mode": "headed"}).json()
tab = requests.post(f"{BASE}/instances/{inst['id']}/tabs/open",
    json={"url": "https://example.com/login"}).json()

tab_id = tab["tabId"]

# Get interactive elements
snapshot = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot",
    params={"filter": "interactive"}
).json()

print(f"Page: {snapshot['title']}")
print(f"Elements: {snapshot['count']}\n")

# Print all interactive elements
for node in snapshot["nodes"]:
    role = node["role"]
    name = node["name"]
    ref = node["ref"]
    
    extra = []
    if "value" in node:
        extra.append(f"value={node['value']}")
    if node.get("required"):
        extra.append("required")
    if node.get("checked") is not None:
        extra.append(f"checked={node['checked']}")
    
    extra_str = f" ({', '.join(extra)})" if extra else ""
    print(f"{ref}: {role} '{name}'{extra_str}")

# Find email input by name
email_ref = next(
    (n["ref"] for n in snapshot["nodes"] 
     if n["role"] == "textbox" and "email" in n["name"].lower()),
    None
)

if email_ref:
    print(f"\nFound email input: {email_ref}")
    
    # Fill email using the ref
    requests.post(f"{BASE}/tabs/{tab_id}/action",
        json={"kind": "fill", "ref": email_ref, "text": "user@example.com"})

# Cleanup
requests.post(f"{BASE}/instances/{inst['id']}/stop")

Element Roles

Common accessibility roles you’ll encounter:
RoleDescriptionActions
buttonButton elementclick
linkHyperlinkclick
textboxText input fieldtype, fill, focus
checkboxCheckbox inputclick
radioRadio buttonclick
comboboxDropdown/selectselect
headingHeading (h1-h6)-
listList container-
listitemList item-
menuMenu container-
menuitemMenu itemclick
tabTab elementclick
tabpanelTab panel content-

Error Handling

Tab Not Found (404)

curl "http://localhost:9867/tabs/nonexistent/snapshot"
Response:
{
  "error": "tab not found",
  "statusCode": 404
}

Selector Not Found (400)

curl "http://localhost:9867/tabs/tab_abc123/snapshot?selector=#nonexistent"
Response:
{
  "error": "selector: selector \"#nonexistent\" not found",
  "statusCode": 400
}

Best Practices

Use Interactive Filter

For automation tasks, always use filter=interactive:
# ✅ Good: Only actionable elements
snapshot = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot?filter=interactive"
).json()

# ❌ Bad: Too much noise
snapshot = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot"
).json()

Refresh After Navigation

Take a new snapshot after navigating:
# Navigate
requests.post(f"{BASE}/tabs/{tab_id}/navigate",
    json={"url": "https://example.com/page2"})

# MUST take new snapshot
snapshot = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot?filter=interactive"
).json()

Use Compact Format for Logging

# Get compact format for logging
resp = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot?format=compact&filter=interactive"
)
print(resp.text)  # Easy to read text format

Scope Large Pages

For pages with many elements, scope to relevant sections:
# Scope to login form only
snapshot = requests.get(
    f"{BASE}/tabs/{tab_id}/snapshot",
    params={
        "selector": "#login-form",
        "filter": "interactive"
    }
).json()

Next Steps

Actions API

Use refs from snapshots in actions

Evaluation

Execute JavaScript for custom queries

Navigation

Navigate before taking snapshots

Screenshots

Visual page inspection