JustBrowser
← Back to Home

API documentation

JustBrowser local REST API

The desktop app serves a REST API on your own machine, on by default: with an active subscription or trial it listens on 127.0.0.1 from the moment the app starts. It can be switched off, stopped or moved to another port from the AI page in the app, and it does not start at all on a team seat or without an entitlement, so a refused connection is the first thing to check there. Use it to create profiles, assign proxies, start browsers and drive them from Playwright, Puppeteer or Selenium over CDP. This page is generated from the OpenAPI document the app itself serves, version 1.0.349: 108 operations across 15 groups. Download the OpenAPI JSON.

Local only

The API is bound to 127.0.0.1 on port 36542 (configurable in the app). Base URL:

http://127.0.0.1:36542/api/v1

There is no hosted API. The only server is the one the desktop app starts on your machine, so the app must be running, and there is nothing to call at api.justbrowser.app. The request itself never leaves 127.0.0.1, but the operation can, exactly as the same action in the app does: creating a profile while signed in asks justbrowser.app to generate the fingerprint (a local draw is used when signed out or offline), sync uploads the profile to cloud storage, teams, shares, licence refresh and the proxy catalogue call justbrowser.app, and a proxy test connects through the proxy. Nothing extra is sent because you used the API.

Authentication

Every request carries Authorization: Bearer <token>. The token is shown on the AI page in the app, which also writes a plain-text briefing of this API for pasting into an agent. A missing or wrong token returns 401.

The API is available on an active subscription and during the trial. Team seats can run shared profiles but have no API access of their own.

Profiles start headless by default

POST /profiles/{id}/start with no body launches a headless browser. Send {"headless": false} as JSON when you want a window. The response carries cdp_url, a ws://127.0.0.1:PORT/devtools/browser/… endpoint.

Responses and errors

Every endpoint under /api/v1 wraps its result in {"data": …}, except GET /docs, which returns the OpenAPI document itself. Errors come back with a non-2xx status and {"error": {"code", "message"}}. Branch on code, not the message, which varies by layer; when details is present it is an array of issue objects. Bodies are JSON; send Content-Type: application/json.

Quickstart

Four calls: create a profile, start it, connect over CDP, stop it. The start call is the one people get wrong.

Start a profile with a visible window (curl)

curl -s -X POST http://127.0.0.1:36542/api/v1/profiles/$PROFILE_ID/start \
  -H "Authorization: Bearer $JB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"headless": false}'

# {"data": {"profile_id": "…", "status": "running",
#           "cdp_url": "ws://127.0.0.1:41287/devtools/browser/…",
#           "launched_at": "2026-09-22T09:14:02Z"}}

Python with Playwright

import os, requests
from playwright.sync_api import sync_playwright

BASE = "http://127.0.0.1:36542/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['JB_TOKEN']}"}

# 1. Create a profile. A complete device identity is generated for it.
profile = requests.post(f"{BASE}/profiles", json={"name": "shop-eu-1"}, headers=HEADERS).json()["data"]

# 2. Start it. The API starts profiles HEADLESS unless you say otherwise.
r = requests.post(f"{BASE}/profiles/{profile['id']}/start", json={"headless": False}, headers=HEADERS).json()
if "error" in r:
    raise RuntimeError(f"{r['error']['code']}: {r['error']['message']}")
cdp_url = r["data"]["cdp_url"]

# 3. Drive it over CDP. Use the profile's own context — never new_context().
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(cdp_url)
    context = browser.contexts[0]
    page = context.pages[0] if context.pages else context.new_page()
    page.goto("https://example.com")
    browser.close()   # disconnects only; the profile keeps running

# 4. Stop it when you are done.
requests.post(f"{BASE}/profiles/{profile['id']}/stop", headers=HEADERS)

Node with Playwright

import { chromium } from "playwright";

const BASE = "http://127.0.0.1:36542/api/v1";
const headers = { Authorization: `Bearer ${process.env.JB_TOKEN}`, "Content-Type": "application/json" };

const { data: profile } = await (await fetch(`${BASE}/profiles`, { method: "POST", headers, body: JSON.stringify({ name: "shop-eu-1" }) })).json();
const started = await (await fetch(`${BASE}/profiles/${profile.id}/start`, { method: "POST", headers, body: JSON.stringify({ headless: false }) })).json();
if (started.error) throw new Error(`${started.error.code}: ${started.error.message}`);

const browser = await chromium.connectOverCDP(started.data.cdp_url);
const context = browser.contexts()[0];            // the profile's own context — never browser.newContext()
const page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://example.com");
await browser.close();                            // disconnects only; the profile keeps running

await fetch(`${BASE}/profiles/${profile.id}/stop`, { method: "POST", headers });

Selenium

# GET /profiles/{id}/selenium returns the debugger address of a RUNNING profile:
# {"data": {"selenium": {"debugger_address": "127.0.0.1:41287", "capabilities": {...}}}}
#
# ChromeDriver must match the profile's Chromium major. Selenium Manager picks a
# driver for the Chrome installed on your machine, not for JustBrowser, so pin it —
# an unpinned driver fails with "This version of ChromeDriver only supports Chrome
# version N". GET /profiles/{id}/chromium tells you which core the profile runs.
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.browser_version = "152"                                            # or Service(executable_path=".../chromedriver-152")
opts.add_experimental_option("debuggerAddress", "127.0.0.1:41287")   # from the response above
driver = webdriver.Chrome(options=opts)                                 # attaches; does not launch a browser
# driver.quit() only disconnects — call POST /profiles/{id}/stop to end the session.
# Stock ChromeDriver defines window.cdc_* globals on every page it drives, which
# anti-bot scripts look for. Prefer Playwright or Puppeteer for detection-sensitive work.

Error shape

{"error": {"code": "VALIDATION_ERROR", "message": "Request validation failed", "details": [{"instancePath": "/name", "keyword": "minLength", "message": "must NOT have fewer than 1 characters"}]}}   // 400 — details is an array; the message varies by layer
{"error": {"code": "INVALID_JSON", "message": "Request body is not valid JSON"}}   // 400
{"error": {"code": "UNAUTHORIZED", "message": "Missing Authorization header. Include: Authorization: Bearer <token>"}}   // 401
{"error": {"code": "NOT_FOUND", "message": "Profile with id 'x' not found"}}   // 404
{"error": {"code": "PROFILE_IN_USE", "message": "Profile is currently locked by another device (in use by <user> on <device>).", "details": {"heldBy": {…}}}}   // 409

Rules that save an afternoon

  • Use the profile's own context. After connectOverCDP, work in contexts()[0]. Calling newContext()opens an incognito context inside the same browser process: it keeps the profile's fingerprint and proxy, which are set process-wide at launch, but starts with none of its cookies or storage, and nothing done in it is saved to the profile.
  • Closing your client does not stop the profile, in Playwright and Selenium. Playwright's browser.close()on a connectOverCDP browser only disconnects, and Selenium's driver.quit() leaves the browser running. Puppeteer is the exception: browser.close() on a connected browser sends Browser.close and shuts the profile down; use browser.disconnect() to detach. Call POST /profiles/{id}/stop to end a session.
  • A second client can attach while the app holds its own connection. On a headless start (the default) the app keeps its own CDP session open for the life of the profile and yours attaches alongside it on the same cdp_url. With {"headless": false} the app detaches right after launch, so your client is the only one attached.
  • Headless is not headed. A headless session has no real window, so screen and viewport geometry follow headless defaults rather than a desktop. Attaching a CDP client does not by itself change what pages see: navigator.bluetooth, getBattery, mediaDevices and keyboard are all present with a debugger attached, and navigator.webdriver is false either way. Send {"headless": false}when the session must look like a person's browser.
  • Bulk creation, not CSV. POST /profiles/bulk creates up to 100 profiles per call from a naming pattern; POST /profiles/import restores an encrypted archive. There is no CSV importer.
  • The cross-device lock applies to visible launches only. With {"headless": false}, a synced profile that is open on another device returns 409 PROFILE_IN_USE (the holder is in details.heldBy) until that device stops it or its lock goes stale after 60 seconds without a heartbeat. The default headless start neither takes nor checks the lock, so it will run a profile that is open elsewhere. DELETE /profiles/{id}/lock is a steal, not a release: owner-only, it signs the holding device out (its open browsers close within about a minute) and moves the lock to the caller; a local-only profile answers {"forced": false}.

Reference

All paths are relative to http://127.0.0.1:36542/api/v1.

PROFILES

Create, list, update and delete profiles. Every profile is generated with a complete device identity.

List profiles

List all profiles with optional filters

Parameters

searchquery · stringSearch by profile name
statusquery · stringone of "stopped", "running", "error", "launching"
folder_idquery · string
sort_byquery · stringone of "name", "status", "last_used_at", "detection_score", "created_at"default "created_at"
sort_directionquery · stringone of "asc", "desc"default "desc"

Responses

Create a profile

Create a new browser profile with full configuration

Request body (JSON, required)

name*stringProfile name
folder_idstringFolder ID to assign
notesstringProfile notes

Responses

  • 201Profile created

    Response body

Get a profile

Get a single profile by ID

Parameters

id*path · string

Responses

  • 200Profile details
  • 404Profile not found

Update a profile

Update an existing profile

Parameters

id*path · string

Request body (JSON)

namestring
folder_idstring
notesstring

Responses

  • 200Profile updated
  • 404Profile not found

Delete a profile

Delete a profile. Cannot delete a running profile — stop the browser first.

Parameters

id*path · string

Responses

  • 200Profile deleted
  • 404Profile not found
  • 409Profile is running

Get profile runtime status

Get the current runtime status of a profile including browser and proxy info.

Parameters

id*path · string

Responses

  • 200Profile status
  • 404Profile not found

Create up to 100 from a naming pattern

Request body (JSON, required)

count*integer
naming_patternstring
template_idstring
osstringone of "windows", "macos", "linux"
browserstringone of "chrome", "firefox"
localestring
device_typestringone of "desktop", "mobile"
proxy_idstring
folder_idstring
tag_idsarray of string

Responses

  • 200Create up to 100 from a naming pattern

RUNNING A BROWSER

Launch a profile, get its CDP endpoint for Playwright, Puppeteer or Selenium, and stop it.

Start a profile browser

Launch a browser instance for a profile and return the CDP WebSocket URL for automation.

Parameters

id*path · string

Request body (JSON)

headlessbooleanLaunch in headless mode (default: true for API)default true

Responses

  • 200Browser launched

    Response body

    dataobject
    profile_idstring
    statusstringone of "running"
    cdp_urlstringCDP WebSocket URL — use with Playwright connectOverCDP() or Puppeteer connect()
    launched_atstring (date-time)
  • 400Profile has no fingerprint
  • 404Profile not found
  • 409Profile already running
  • 429Max concurrent profiles reached

Stop a profile browser

Stop a running browser instance for a profile.

Parameters

id*path · string

Responses

  • 200Browser stopped

    Response body

    dataobject
    profile_idstring
    statusstringone of "stopped"
    stopped_atstring (date-time)
  • 404Profile not running

Get CDP WebSocket URL

Get the Chrome DevTools Protocol WebSocket URL for a running profile. Use this URL with Playwright chromium.connectOverCDP() or Puppeteer puppeteer.connect().

Parameters

id*path · string

Responses

  • 200CDP connection info

    Response body

    dataobject
    profile_idstring
    cdp_urlstring
    browserobject
    runningboolean
    pidnumber
    launched_atstring (date-time)
  • 404Profile not running

Get Selenium connection info

Get Selenium RemoteWebDriver-compatible connection info for a running profile. Returns the debugger address and Chrome capabilities needed for Selenium integration.

Parameters

id*path · string

Responses

  • 200Selenium connection info

    Response body

    dataobject
    profile_idstring
    cdp_urlstring
    seleniumobject
    remote_urlstringThe DevTools HTTP endpoint (http://127.0.0.1:PORT), not a Selenium Grid / RemoteWebDriver URL — attach with the debuggerAddress capability
    debugger_addressstringChrome debugger address (host:port)
    capabilitiesobjectChrome capabilities for WebDriver
    browserobject
    runningboolean
    pidnumber
    launched_atstring (date-time)
  • 404Profile not running

PROFILE CONFIG

Which Chromium core a profile runs, its DNS-over-HTTPS settings, and matching its identity to a proxy's real location.

Which core this profile pins

Parameters

id*path · string

Responses

  • 200Which core this profile pins

Upgrade it (stop the profile first)

Parameters

id*path · string

Request body (JSON, required)

version*string

Responses

  • 200Upgrade it (stop the profile first)

DNS-over-HTTPS config

Parameters

id*path · string

Responses

  • 200DNS-over-HTTPS config

Set it

Parameters

id*path · string

Request body (JSON)

providerstringone of "cloudflare", "google", "quad9", "custom", "disabled"
modestringone of "automatic", "secure", "off"
custom_urlstringnull

Responses

  • 200Set it

Where a proxy actually exits

Parameters

id*path · string

Responses

  • 200Where a proxy actually exits

Check/apply fingerprint vs proxy geo

Parameters

id*path · string

Request body (JSON, required)

proxy_id*string
applyboolean

Responses

  • 200Check/apply fingerprint vs proxy geo

FINGERPRINT

Read, regenerate, validate and repair a profile's identity, and reuse shapes as templates.

Get a profile fingerprint

The full spoof config applied at launch: platform, screen, hardware, WebGL, fonts, timezone, locale.

Parameters

id*path · string

Responses

  • 200Fingerprint config
  • 404Profile has no fingerprint

Regenerate a fingerprint

Replaces the profile fingerprint with a freshly generated, internally consistent one. Every field is optional.

Parameters

id*path · string

Request body (JSON)

osstringone of "windows", "macos", "linux"
browserstringone of "chrome", "firefox"
localestring
device_typestringone of "desktop", "mobile"

Responses

  • 200The new fingerprint config

Check fingerprint consistency

Runs the consistency rules and returns a 0-100 score plus the failing rules. Worth calling after any fingerprint edit.

Parameters

id*path · string

Responses

  • 200Score and issue list

Auto-fix fingerprint issues

Applies the automatic fix for every fixable rule reported by /validate.

Parameters

id*path · string

Responses

  • 200Fixes applied

Saved fingerprint templates + presets

Responses

  • 200Saved fingerprint templates + presets

One template

Parameters

id*path · string

Responses

  • 200One template

Apply a template (body: template_id)

Parameters

id*path · string

Request body (JSON, required)

template_id*string

Responses

  • 200Apply a template (body: template_id)

Save this profile's shape as a template

Parameters

id*path · string

Request body (JSON, required)

name*string
descriptionstring
categorystring

Responses

  • 200Save this profile's shape as a template

PROXIES

Manage proxies (HTTP, HTTPS, SOCKS5 with authentication), test them, and assign one per profile.

Create a proxy

Credentials are encrypted on write and never read back over this API.

Request body (JSON, required)

namestring
protocol*stringone of "http", "https", "socks5"
host*string
port*number
usernamestring
passwordstring

Responses

  • 201Created proxy

Get a proxy

Parameters

id*path · string

Responses

  • 200Proxy

Update a proxy

Parameters

id*path · string

Request body (JSON): object

Responses

  • 200Updated proxy

Delete a proxy

Parameters

id*path · string

Responses

  • 200Deleted

Test a proxy

Live connectivity check. Returns latency and the geo the upstream actually exits from — use it to confirm a proxy still matches its profile timezone.

Parameters

id*path · string

Responses

  • 200Test result with latency, IP and geo

Get the proxy assigned to a profile

Parameters

id*path · string

Responses

  • 200Assigned proxy, or null

Assign a proxy to a profile

Parameters

id*path · string

Request body (JSON, required)

proxy_id*string

Responses

  • 200The newly assigned proxy

BATCH

The same operations across many profiles in one call, with per-profile results.

Start several profiles

Launch a set of profiles in one call, respecting the concurrency limit. Prefer this over N calls to /profiles/{id}/start.

Request body (JSON, required)

profile_ids*array of string
headlessboolean

Responses

  • 200Per-profile launch results

Stop several profiles

Request body (JSON, required)

profile_ids*array of string

Responses

  • 200Per-profile stop results

Delete many

Request body (JSON, required)

profile_ids*array of string

Responses

  • 200Delete many

Add/remove one tag across many

Request body (JSON, required)

profile_ids*array of string
tag_id*string
action*stringone of "add", "remove"

Responses

  • 200Add/remove one tag across many

Move many into a folder

Request body (JSON, required)

profile_ids*array of string
folder_idstringnull

Responses

  • 200Move many into a folder

Spread a proxy pool across many

Request body (JSON, required)

profile_ids*array of string
proxy_ids*array of string
strategystringone of "round-robin", "random"

Responses

  • 200Spread a proxy pool across many

DETECTION

Run the detection suite against a profile, read its history and health, and auto-fix what it finds.

Run the 18-vector suite (slow, launches a browser)

Parameters

id*path · string

Responses

  • 200Run the 18-vector suite (slow, launches a browser)

Composite health score

Parameters

id*path · string

Responses

  • 200Composite health score

COOKIES

Import cookies into a profile or export what it holds.

Export the profile's cookies

Parameters

id*path · string

Responses

  • 200Export the profile's cookies

Import (body: cookies[] or content)

Parameters

id*path · string

Request body (JSON)

cookiesarray of object
contentstring
formatstringone of "json", "netscape", "auto"

Responses

  • 200Import (body: cookies[] or content)

WARMUP

Cookie warm-up: the profile browses real sites in a visible window to build history.

Start (runs visibly, minutes)

Parameters

id*path · string

Request body (JSON): objectnull

Responses

  • 200Start (runs visibly, minutes)

LOGINS

Saved logins per profile. Passwords are never returned.

A profile's saved logins (no passwords)

Parameters

id*path · string

Responses

  • 200A profile's saved logins (no passwords)

Save one

Parameters

id*path · string

Request body (JSON, required)

site_url*string
username*string
password*string
notesstring

Responses

  • 200Save one

Update one

Parameters

id*path · string

Request body (JSON)

site_urlstring
usernamestring
passwordstring
notesstring

Responses

  • 200Update one

Delete one

Parameters

id*path · string

Responses

  • 200Delete one

Remove the 2FA seed

Parameters

id*path · string

Responses

  • 200Remove the 2FA seed

BACKUP

Encrypted export and restore of a single profile.

Encrypted archive to a file path

Parameters

id*path · string

Request body (JSON, required)

file_path*string
passphrase*string
include_user_databoolean

Responses

  • 200Encrypted archive to a file path

Restore one from a file path

Request body (JSON, required)

file_path*string
passphrase*string
new_namestring

Responses

  • 200Restore one from a file path

SYNC

Cloud sync status and settings, and the cross-device lock.

Sync status of every profile

Responses

  • 200Sync status of every profile

Sync status of one

Parameters

id*path · string

Responses

  • 200Sync status of one

Push it to the cloud now

Parameters

id*path · string

Responses

  • 200Push it to the cloud now

Change them

Request body (JSON)

enabledboolean

Responses

  • 200Change them

Force-release a cross-device lock

Parameters

id*path · string

Responses

  • 200Force-release a cross-device lock

TEAMS

Teams, members, roles, shared profiles, invitations, direct shares and notifications.

List teams

Responses

  • 200List teams
post/teams

Create one

Request body (JSON, required)

name*string
descriptionstring

Responses

  • 200Create one

Rename it

Parameters

id*path · string

Request body (JSON): object

Responses

  • 200Rename it

Delete it

Parameters

id*path · string

Responses

  • 200Delete it

Invite by email

Parameters

id*path · string

Request body (JSON, required)

email*string
rolestring

Responses

  • 200Invite by email

Change a role

Parameters

id*path · string
memberId*path · string

Request body (JSON, required)

role*string

Responses

  • 200Change a role

Profiles shared with the team

Parameters

id*path · string

Responses

  • 200Profiles shared with the team

Share one (push it first)

Parameters

id*path · string

Request body (JSON, required)

profile_id*string
profile_namestring

Responses

  • 200Share one (push it first)

Team activity (?limit=)

Parameters

id*path · string

Responses

  • 200Team activity (?limit=)

Direct shares in and out

Responses

  • 200Direct shares in and out

Revoke one

Parameters

id*path · string

Responses

  • 200Revoke one

In-app notifications + unread count

Responses

  • 200In-app notifications + unread count

ORGANISATION

Folders, tags, templates and duplication.

List tags

Responses

  • 200Tag list
post/tags

Create a tag

Request body (JSON, required)

name*string
colorstring

Responses

  • 201Created tag

Replace a profile's tags

Sets the complete tag list — tags omitted here are removed.

Parameters

id*path · string

Request body (JSON, required)

tag_ids*array of string

Responses

  • 200The resulting tag list

List folders

Responses

  • 200Folder list

Create a folder

Request body (JSON, required)

name*string
parent_idstringnull

Responses

  • 200Create a folder

Delete a template

Parameters

id*path · string

Responses

  • 200Delete a template

Rename / move a folder

Parameters

id*path · string

Request body (JSON): object

Responses

  • 200Rename / move a folder

Delete a folder

Parameters

id*path · string

Responses

  • 200Delete a folder

Rename / recolour a tag

Parameters

id*path · string

Request body (JSON): object

Responses

  • 200Rename / recolour a tag

Delete a tag

Parameters

id*path · string

Responses

  • 200Delete a tag

Clone a profile

Parameters

id*path · string

Request body (JSON)

namestring
copy_user_databoolean
copy_proxyboolean
regenerate_fingerprintboolean

Responses

  • 200Clone a profile

META

Account, licence, settings, diagnostics and this document.

System status

Get system status including running profile count and app version.

Responses

  • 200System status info

API documentation

Returns this OpenAPI-style JSON schema.

Responses

  • 200OpenAPI schema

Plan, profile limit, feature flags

Responses

  • 200Plan, profile limit, feature flags

App settings (secrets redacted)

Responses

  • 200App settings (secrets redacted)

Change settings (API-control keys refused)

Request body (JSON): object

Responses

  • 200Change settings (API-control keys refused)

Signed-in user + auth status

Responses

  • 200Signed-in user + auth status

Chromium install, fingerprint data, stealth

Responses

  • 200Chromium install, fingerprint data, stealth

Schemas

Error

Fields

errorobject
code*string
message*string
detailsanyArray of issue objects for VALIDATION_ERROR; an object for other errors that carry context

Profile

Fields

idstring (uuid)
namestring
folder_idstring
notesstring
statusstringone of "stopped", "running", "error", "launching"
consistency_scorenumber
detection_scorenumber
last_used_atstring
created_atstring (date-time)
updated_atstring (date-time)

Proxy

Credentials are never returned. `has_credentials` says whether the proxy authenticates.

Fields

idstring
namestring
protocolstringone of "http", "https", "socks5"
hoststring
portnumber
has_credentialsboolean
country_codestring
geo_countrystring
geo_citystring
geo_timezonestring
last_statusstringone of "untested", "connected", "failed"
last_latency_msnumber
last_tested_atstring
assigned_profile_countnumber

This reference is generated from the app's route definitions and committed with the code; a test in the repository fails whenever the routes and this file disagree, so it stays in step with the source. Your installed app serves its own copy at GET http://127.0.0.1:36542/api/v1/docs, the authoritative document for the version you are running (this page was generated from 1.0.349). The AI page in the app offers a plain-text briefing for agents, built from the same endpoint catalogue with your port and token filled in. Questions or a gap you hit: [email protected].

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy