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
All Elements
Interactive Only
Compact Format
Python
JavaScript
curl "http://localhost:9867/tabs/tab_abc123/snapshot" | jq .
Query Parameters
Filter type: all (everything) or interactive (clickable/fillable elements only)
Output format: json, compact, text, yaml
Maximum tree depth (-1 for unlimited)
Truncate output to approximately N tokens
Scope snapshot to specific CSS selector
Include diff with previous snapshot
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
Array of accessibility nodes
Node Fields
Element reference (e.g., e0, e1) for use in actions
Accessibility role: button, link, textbox, heading, checkbox, etc.
Accessible name (label, text content, or aria-label)
Current value (for inputs)
Heading level (for headings)
Checked state (for checkboxes/radios)
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.
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 " \n Found 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:
Role Description Actions buttonButton element click linkHyperlink click textboxText input field type, fill, focus checkboxCheckbox input click radioRadio button click comboboxDropdown/select select headingHeading (h1-h6) - listList container - listitemList item - menuMenu container - menuitemMenu item click tabTab element click 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()
# 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