Skip to content

Connect a script to a running profile

This guide shows you how to start a profile from a script and drive it with Puppeteer, Playwright or Selenium. By the end, your script will control a profile with its own fingerprint, proxy and saved session.

This is the desktop app’s local API. It only works on the computer running the app.

  • The ExitNode desktop app open and signed in.
  • A paid plan. On the Free plan, starting a profile returns tier_or_permission_required.
  • The Automation API switch on your access. See Members and roles.
  • Your API token.
  • The ID of the profile you want to drive.
  1. Click Settings in the sidebar.
  2. Find the Local automation API card. It should say Enabled.
  3. Next to Token, click Copy. Show reveals it first.
  4. Note the Endpoint. It’s normally http://127.0.0.1:48111. Click it to copy.
  5. Click Test connection. A tick and listening on the port means the API is running.

The token is also saved in a file:

  • On a Mac: ~/.exitnode/local_api_token
  • On Windows: .exitnode\local_api_token in your user folder

Treat it like a password. Send it with every request:

Authorization: Bearer <token>

Click Regenerate, then confirm. Scripts using the old token stop working until you give them the new one.

The card tells you to set local_api_enabled in config.json and restart the app.

From the app: right-click the profile, click Copy, then Copy profile ID.

From your script:

Terminal window
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:48111/api/profiles/all

This lists every profile you can access, with its id, name, status, folder and running. Add ?org=<id> to limit it to one organization.

Terminal window
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:48111/api/profiles/<ID>/start

The call waits until the browser is ready, for up to 45 seconds. It answers with:

Field What it’s for
ok true if it worked.
ws_endpoint The address Puppeteer and Playwright connect to.
debugger_address The address Selenium connects to.
debug_port The port number.
browser_pid The browser’s process ID.
already_running true if the profile was already open.

The profile appears as running in the app, as if you’d clicked Start.

const res = await fetch(`http://127.0.0.1:48111/api/profiles/${ID}/start`, {
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}` },
}).then((r) => r.json());
const browser = await puppeteer.connect({ browserWSEndpoint: res.ws_endpoint });
const page = await browser.newPage();
await page.goto('https://example.com');
const { ws_endpoint } = await (await fetch(`http://127.0.0.1:48111/api/profiles/${ID}/start`, {
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}` },
})).json();
const browser = await chromium.connectOverCDP(ws_endpoint);

Selenium uses the debugger address, not the WebSocket address.

import requests
from selenium import webdriver
headers = {"Authorization": f"Bearer {TOKEN}"}
r = requests.post(f"http://127.0.0.1:48111/api/profiles/{ID}/start", headers=headers).json()
options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", r["debugger_address"])
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
Terminal window
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:48111/api/profiles/<ID>/stop

This closes the browser and saves the session, the same as clicking Stop.

Method Path What it does
GET /api/status Shows whether the app is running and signed in, its version and how many profiles are open.
GET /api/profiles Lists profiles running now, with their connection details.
GET /api/profiles/all Lists every profile you can access.
POST /api/profiles/<ID>/start Starts a profile, or returns its details if it’s open. You can send { "org": "<id>" }.
POST /api/profiles/<ID>/stop Stops a profile.

A failed call answers with an error code. Some add a message saying what to do.

Code HTTP What to do
unauthorized 401 The token is wrong or missing. Copy it again.
app_not_signed_in 401 Sign in to the desktop app.
tier_or_permission_required 403 Your plan or role doesn’t include the API. See Billing and plans.
forbidden_origin 403 Call the API from a script, not from a web page.
profile_not_found 404 Check the ID, and that you have access to the profile.
not_active 404 You tried to stop a profile that isn’t running here.
launch_failed 409 The profile couldn’t start. It may be in use elsewhere.
browser_exited_or_no_binary 502 The browser closed while starting. Read message.
browser_not_found 503 The browser isn’t installed yet. Open Settings and click Install browser.
timeout_waiting_for_cdp 504 The browser took too long. Try again.

A connection error means the app isn’t open, or the API is Disabled.

If a profile’s proxy has a problem, the call fails with browser_exited_or_no_binary, and message gives the reason. See Launch, stop and monitor profiles for what each reason means.

Stuck on something? Contact support.