Automation API
A local HTTP API for driving profiles from your own scripts: start a profile with its full fingerprint and proxy applied, and get back a real Chrome DevTools Protocol endpoint to attach Puppeteer, Playwright, or anything else that speaks CDP.
Base URL & auth
The API is bound to 127.0.0.1 only: nothing outside your machine can reach it. Find the port
and your token in the app's ⚙ Settings panel, which also has a ready-to-copy connection snippet.
| Base URL | http://127.0.0.1:<port> |
|---|---|
| Auth header | Authorization: Bearer <token> or X-API-Token: <token> |
Endpoints
GET/profiles
List every profile with its id, name, status, and whether it's currently running.
POST/profiles/:id/start
Launches the profile with its full fingerprint and proxy applied, and returns its CDP
webSocketDebuggerUrl. Add ?headless=true to launch without a visible window.
402 if the profile is frozen (over your account's current plan limit),
upgrade or free up a slot to launch it.
POST/profiles/:id/stop
Stops the launched browser for that profile.
GET/profiles/:id/status
Returns whether the profile is currently running and its CDP endpoint if so.
Creating profiles, proxies and groups
The endpoints above drive profiles that already exist. These make them, so a script, or an AI assistant through the MCP server, can set a workspace up rather than only work in one that is already set up.
Everything here goes through the same code as the buttons in the app. Your plan limit, the name rules, the operating-system and browser-version checks and the proxy format checks all apply exactly as they would if you clicked Create.
POST/profiles
Creates one profile, or a numbered batch with count (maximum 200). Each gets its own
coherent fingerprint and its own storage: a batch is that many different machines, not one identity
repeated. Body: name (required), count, os
(Windows or macOS), proxyId, group,
tags, startUrls, notes.
402. A batch is a 200: the profiles it already
made are kept and stoppedAtLimit says where it stopped, because losing forty successful
creations when the forty-first hits the cap would be the worse outcome. Check that field: a
200 here does not mean you got everything you asked for.
proxyId that does not exist is refused with a 400 naming
the id, rather than stored as given. “Make twenty profiles on this proxy” with a stale or
mistyped id used to produce twenty profiles that all looked correct and none of which could open,
and you found out one at a time, at launch. The app’s own UI cannot reach that state because it
picks from a list; an id arriving here is free text. Omit proxyId for a direct
connection.
PATCH/profiles/:id
Rename a profile, move it to a group, re-tag it, change its start URLs, or bind and unbind a proxy
(proxyId: null makes it direct; an id that does not exist is refused the same way it is
on create). Two fields are deliberately not accepted: the
fingerprint seed, because changing it would quietly make the profile a different
computer while keeping its name and its cookies, and the running status, which only
actually launching a browser should set.
GET/proxies
Every proxy, with the country and timezone it resolved to once tested. Credentials are never returned.
POST/proxies
Add proxies. Send list with a pasted provider list: one per line, in any of the formats
the import dialog accepts (host:port, host:port:user:pass,
user:pass@host:port, scheme://…), and the reply reports every line it could
not read, so a list of fifty that yields forty-seven proxies tells you which three to fix. Or send one
proxy as host, port, type, username,
password.
GET/groups
The groups, with how many profiles are in each.
POST/groups
Creates a group. Put profiles in it with PATCH /profiles/:id or when creating them.
Example: connect with Puppeteer
const res = await fetch('http://127.0.0.1:PORT/profiles/PROFILE_ID/start', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_TOKEN' }
})
const { webSocketDebuggerUrl } = await res.json()
const browser = await puppeteer.connect({ browserWSEndpoint: webSocketDebuggerUrl })
const [page] = await browser.pages()
await page.goto('https://example.com')
The same webSocketDebuggerUrl works with Playwright's chromium.connectOverCDP() or
any Selenium CDP-based driver.