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.
Installation
Choose your preferred installation method:
One-Liner (Recommended)
npm
Docker
Build from Source
macOS / Linux: curl -fsSL https://pinchtab.com/install.sh | bash
Verify the installation: Requires: Node.js 18+npm install -g pinchtab
pinchtab --version
If you see “command not found”, add npm to your PATH: # Add npm binaries to PATH
export PATH = "$( npm config get prefix)/bin: $PATH "
# Add to ~/.bashrc or ~/.zshrc to persist
echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.bashrc
Requires: Dockerdocker run -d -p 9867:9867 pinchtab/pinchtab
# Verify it's running
curl http://localhost:9867/health
Requires: Go 1.25+, Git, Chrome/Chromiumgit clone https://github.com/pinchtab/pinchtab.git
cd pinchtab
go build -o pinchtab ./cmd/pinchtab
./pinchtab --version
See Building from Source for full build guide.
5-Minute Quick Start
Start the Orchestrator
Open your first terminal and start the PinchTab orchestrator: Expected output: 🦀 Pinchtab Dashboard port=9867
dashboard ready url=http://localhost:9867/dashboard
The orchestrator runs on http://127.0.0.1:9867 and manages all instances, profiles, and tabs.
Open the dashboard in your browser to see real-time status.
Create an Instance
Open a second terminal and create a headless Chrome instance: # Create headless instance (background Chrome)
INST = $( pinchtab instance launch --mode headless | jq -r '.id' )
echo "Instance created: $INST "
# Wait for Chrome to initialize (~2-5 seconds)
sleep 3
Instances are auto-allocated unique ports (9868-9968). Chrome initializes lazily on first request.
Navigate to a Website
Create a tab and navigate to a website: # Create tab and navigate
TAB_ID = $( curl -s -X POST http://localhost:9867/instances/ $INST /tabs/open \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}' | jq -r '.id' )
echo "Tab created: $TAB_ID "
Extract Page Content
Get the page structure and text content: # Get page structure with interactive elements
curl http://localhost:9867/tabs/ $TAB_ID /snapshot | jq '.nodes | map({ref, role, name}) | .[0:5]'
# Extract text content
curl http://localhost:9867/tabs/ $TAB_ID /text | jq '.text'
Example Snapshot Response
Example Text Response
[
{ "ref" : "e0" , "role" : "link" , "name" : "More information..." },
{ "ref" : "e1" , "role" : "heading" , "name" : "Example Domain" },
{ "ref" : "e2" , "role" : "paragraph" , "name" : "This domain is for use..." },
{ "ref" : "e3" , "role" : "button" , "name" : "Accept" }
]
✅ You’re running PinchTab! Continue below for common workflows.
Common First Commands
Take a Screenshot
# Save screenshot for a specific tab
curl "http://localhost:9867/tabs/ $TAB_ID /screenshot" -o page.png
# View the screenshot
open page.png # macOS
xdg-open page.png # Linux
Export as PDF
# Save PDF
curl "http://localhost:9867/tabs/ $TAB_ID /pdf?landscape=true" -o output.pdf
# View the PDF
open output.pdf # macOS
Interact with the Page
# Get page structure (snapshot)
SNAPSHOT = $( curl http://localhost:9867/tabs/ $TAB_ID /snapshot )
# Extract a reference (e.g., first button)
BUTTON_REF = $( echo " $SNAPSHOT " | jq -r '.nodes[] | select(.role=="button") | .ref' | head -1 )
# Click the button
curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-H "Content-Type: application/json" \
-d '{"kind":"click","ref":"' $BUTTON_REF '"}'
# Fill a form input
curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-H "Content-Type: application/json" \
-d '{"kind":"fill","ref":"e3","text":"user@example.com"}'
Open Multiple Tabs
# Create new tab in same instance
curl -X POST http://localhost:9867/instances/ $INST /tabs/open \
-H "Content-Type: application/json" \
-d '{"url":"https://github.com"}'
# List all tabs in instance
curl http://localhost:9867/instances/ $INST /tabs | jq .
# Get specific tab info
curl http://localhost:9867/tabs/ $TAB_ID | jq .
Understanding the Workflow
Here’s a typical automation workflow with PinchTab:
Create Instance
Chrome starts lazily on first request INST = $( pinchtab instance launch | jq -r '.id' )
sleep 2
Navigate
Create tab and navigate to page TAB_ID = $( curl -s -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://example.com"}' | jq -r '.id' )
Get Structure
See buttons, links, inputs with stable refs curl http://localhost:9867/tabs/ $TAB_ID /snapshot
Interact
Click, type, fill using element refs curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-d '{"kind":"click","ref":"e5"}'
Verify
Check page changes curl http://localhost:9867/tabs/ $TAB_ID /snapshot
Capture Result
Take screenshot or export PDF curl "http://localhost:9867/tabs/ $TAB_ID /screenshot" -o page.png
curl "http://localhost:9867/tabs/ $TAB_ID /pdf" -o report.pdf
Clean Up
Stop instance when done curl -X POST http://localhost:9867/instances/ $INST /stop
Using with Different Languages
curl (HTTP API)
Python
Node.js
PinchTab is HTTP-first, so you can use pure curl: # Health check
curl http://localhost:9867/health
# Create instance
INST = $( curl -s -X POST http://localhost:9867/instances/launch | jq -r '.id' )
sleep 2
# Navigate
curl -X POST http://localhost:9867/instances/ $INST /tabs/open \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'
# Get snapshot
curl http://localhost:9867/tabs/ $TAB_ID /snapshot
# Extract text
curl http://localhost:9867/tabs/ $TAB_ID /text
# Stop instance
curl -X POST http://localhost:9867/instances/ $INST /stop
import requests
import time
BASE = "http://localhost:9867"
# 1. Create instance
resp = requests.post( f " { BASE } /instances/launch" , json = { "mode" : "headless" })
inst_id = resp.json()[ "id" ]
print ( f "Created instance: { inst_id } " )
# Wait for Chrome to initialize
time.sleep( 2 )
# 2. Create tab by navigating
resp = requests.post( f " { BASE } /instances/ { inst_id } /tabs/open" , json = {
"url" : "https://example.com"
})
tab_id = resp.json()[ "id" ]
print ( f "Navigated: { resp.json() } " )
# 3. Get snapshot
snapshot = requests.get( f " { BASE } /tabs/ { tab_id } /snapshot" ).json()
# Print interactive elements
for elem in snapshot.get( "nodes" , []):
if elem.get( "role" ) in [ "button" , "link" ]:
print ( f " { elem[ 'ref' ] } : { elem[ 'role' ] } - { elem[ 'name' ] } " )
# 4. Click an element
requests.post( f " { BASE } /tabs/ { tab_id } /action" , json = {
"kind" : "click" ,
"ref" : "e5"
})
# 5. Get text
text = requests.get( f " { BASE } /tabs/ { tab_id } /text" ).json()
print ( f "Page text: { text[ 'text' ][: 200 ] } ..." )
# 6. Stop instance
requests.post( f " { BASE } /instances/ { inst_id } /stop" )
print ( f "Stopped instance: { inst_id } " )
const fetch = require ( 'node-fetch' );
const BASE = "http://localhost:9867" ;
async function main () {
try {
// 1. Create instance
const launchResp = await fetch ( ` ${ BASE } /instances/launch` , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ mode: "headless" })
});
const launch = await launchResp . json ();
const instId = launch . id ;
console . log ( `Created instance: ${ instId } ` );
// Wait for Chrome to initialize
await new Promise ( r => setTimeout ( r , 2000 ));
// 2. Create tab by navigating
const navResp = await fetch ( ` ${ BASE } /instances/ ${ instId } /tabs/open` , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({
url: "https://example.com"
})
});
const navData = await navResp . json ();
const tabId = navData . id ;
// 3. Get snapshot
const snapResp = await fetch ( ` ${ BASE } /tabs/ ${ tabId } /snapshot` );
const snap = await snapResp . json ();
// Print interactive elements
snap . nodes
. filter ( n => [ "button" , "link" ]. includes ( n . role ))
. forEach ( n => console . log ( ` ${ n . ref } : ${ n . role } - ${ n . name } ` ));
// 4. Click element
await fetch ( ` ${ BASE } /tabs/ ${ tabId } /action` , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({
kind: "click" ,
ref: "e5"
})
});
// 5. Get text
const textResp = await fetch ( ` ${ BASE } /tabs/ ${ tabId } /text` );
const text = await textResp . json ();
console . log ( `Page text: ${ text . text . substring ( 0 , 200 ) } ...` );
// 6. Stop instance
await fetch ( ` ${ BASE } /instances/ ${ instId } /stop` , {
method: "POST"
});
console . log ( `Stopped instance: ${ instId } ` );
} catch ( error ) {
console . error ( "Error:" , error );
}
}
main ();
Common Scenarios
# Create instance
INST = $( pinchtab instance launch | jq -r '.id' )
sleep 2
# Navigate
TAB_ID = $( curl -s -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://example.com/article"}' | jq -r '.id' )
# Extract text
curl http://localhost:9867/tabs/ $TAB_ID /text | jq '.text'
# Save to file
curl http://localhost:9867/tabs/ $TAB_ID /text | jq -r '.text' > article.txt
# Stop instance
curl -X POST http://localhost:9867/instances/ $INST /stop
# Create persistent profile
pinchtab profile create mylogin
PROF_ID = $( pinchtab profiles | jq -r '.[] | select(.name=="mylogin") | .id' )
# Start instance with profile
INST = $( curl -s -X POST http://localhost:9867/instances/start \
-d '{"profileId":"' $PROF_ID '"}' | jq -r '.id' )
sleep 2
# Login
TAB_ID = $( curl -s -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://example.com/login"}' | jq -r '.id' )
curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-d '{"kind":"fill","ref":"e3","text":"user@example.com"}'
curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-d '{"kind":"fill","ref":"e5","text":"password"}'
curl -X POST http://localhost:9867/tabs/ $TAB_ID /action \
-d '{"kind":"click","ref":"e7"}'
# Stop instance (profile saved)
curl -X POST http://localhost:9867/instances/ $INST /stop
# Later: restart with same profile (already logged in!)
INST = $( curl -s -X POST http://localhost:9867/instances/start \
-d '{"profileId":"' $PROF_ID '"}' | jq -r '.id' )
sleep 2
# Navigate to dashboard (cookies preserved)
curl -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://example.com/dashboard"}'
# Create instance
INST = $( pinchtab instance launch | jq -r '.id' )
sleep 2
TAB_ID = $( curl -s -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://reports.example.com/monthly"}' | jq -r '.id' )
# Export PDF
curl "http://localhost:9867/tabs/ $TAB_ID /pdf?landscape=true" -o report.pdf
Troubleshooting
Problem: Orchestrator not runningSolution: # Terminal 1: Start orchestrator
pinchtab
# Terminal 2: Check health (once running)
curl http://localhost:9867/health
Instance Stuck in 'starting' State
Problem: Chrome takes time to initialize (8-20 seconds)Solution: # Poll instance status
INST = $( pinchtab instance launch | jq -r '.id' )
# Wait until 'running'
while [ "$( curl -s http://localhost:9867/instances/ $INST | jq -r '.status')" != "running" ]; do
sleep 0.5
done
# Now safe to use
curl -X POST http://localhost:9867/instances/ $INST /tabs/open \
-d '{"url":"https://example.com"}'
Problem: Port 9867 (or instance port) is takenSolution: # Use different orchestrator port
BRIDGE_PORT = 9868 pinchtab
# Or kill the process using 9867
lsof -i :9867
kill -9 < PI D >
# Instance ports auto-allocated from 9868-9968, no manual config needed
Problem: Chrome/Chromium not installedSolution: # macOS
brew install chromium
# Linux (Ubuntu/Debian)
sudo apt install chromium-browser
# Or specify custom Chrome
CHROME_BIN = /path/to/chrome pinchtab
Quick Reference
Task Command Start orchestrator pinchtabHealth check curl http://localhost:9867/healthCreate instance INST=$(pinchtab instance launch | jq -r '.id')Navigate curl -X POST http://localhost:9867/instances/$INST/tabs/open -d '{"url":"..."}' See structure curl http://localhost:9867/tabs/$TAB_ID/snapshotGet text curl http://localhost:9867/tabs/$TAB_ID/textClick element curl -X POST http://localhost:9867/tabs/$TAB_ID/action -d '{"kind":"click","ref":"e5"}'Type text curl -X POST http://localhost:9867/tabs/$TAB_ID/action -d '{"kind":"type","ref":"e3","text":"hello"}'Screenshot curl "http://localhost:9867/tabs/$TAB_ID/screenshot" -o page.pngPDF export curl http://localhost:9867/tabs/$TAB_ID/pdf -o out.pdfList instances curl http://localhost:9867/instancesStop instance curl -X POST http://localhost:9867/instances/$INST/stop
Next Steps
Core Concepts Understand orchestrator, instances, profiles, and tabs
API Reference Complete HTTP API documentation
Configuration Environment variables and advanced settings
Examples Real-world automation examples
Getting Help
Happy automating! 🦀