PBX Local API v1
A loopback HTTP/JSON API for creating, editing, validating, launching, stopping, and automating PBX virtual browser profiles from local software.
http://127.0.0.1:8787The configured port can be changed. The Python SDK can discover the installed PBX configuration automatically.
Download the Python SDK
Dependency-free synchronous client. Includes installed-PBX discovery, structured API errors, lifecycle polling, profile CRUD, proxy, fingerprint, diagnostics, and batch helpers.
Quick SDK usage
from pbx_local_api import PBXLocalAPI, PBXAPIError
# Best option when your script runs on the same Windows installation:
pbx = PBXLocalAPI.from_installed_pbx()
print(pbx.health())
print(pbx.capabilities())
print(pbx.list_environments())
draft = pbx.generate_draft({"os": "win64"})
print(draft)
# API start is automation-only:
pbx.start_selenium("env_001")
pbx.wait_until_running("env_001")
print(pbx.status("env_001"))
print(pbx.automation("env_001"))
pbx.stop("env_001")
pbx.wait_until_stopped("env_001")
Important API rules
GET /v1/healthis public.- Every other Local API route requires authentication.
- API profile start is asynchronous and must explicitly select
cdporselenium. - Manual browser launch remains a UI-only action.
- Selenium publishes its WebDriver endpoint. PBX's internal Chromium CDP attachment endpoint is not a public Local API endpoint.
- Profile creation/run requests can be rejected by PBX policy and may return HTTP
403or429.
Authentication
Protected routes accept either authentication header:
Authorization: Bearer <PBX_API_KEY>
or:
X-PBX-API-Key: <PBX_API_KEY>
Installed-PBX discovery for Python
PBXLocalAPI.from_installed_pbx() reads the Local API configuration from the current Windows PBX installation. The SDK uses the installed API key and the configured Local API port, including the PBX_LOCAL_API_PORT override when present.
Use the API from different languages
Every PBX route is normal HTTP + JSON, so any language with an HTTP client can use it. These reusable helpers let you call all methods in the endpoint table below.
from pbx_local_api import PBXLocalAPI, PBXAPIError
# Best option when your script runs on the same Windows installation:
pbx = PBXLocalAPI.from_installed_pbx()
print(pbx.health())
print(pbx.capabilities())
print(pbx.list_environments())
draft = pbx.generate_draft({"os": "win64"})
print(draft)
# API start is automation-only:
pbx.start_selenium("env_001")
pbx.wait_until_running("env_001")
print(pbx.status("env_001"))
print(pbx.automation("env_001"))
pbx.stop("env_001")
pbx.wait_until_stopped("env_001")$Base = "http://127.0.0.1:8787"
$Key = "YOUR_PBX_API_KEY"
$Headers = @{ Authorization = "Bearer $Key" }
function Invoke-PBX {
param(
[string]$Method,
[string]$Path,
$Body = $null,
[switch]$Public
)
$args = @{
Method = $Method
Uri = "$Base$Path"
}
if (-not $Public) { $args.Headers = $Headers }
if ($null -ne $Body) {
$args.ContentType = "application/json"
$args.Body = ($Body | ConvertTo-Json -Depth 20)
}
Invoke-RestMethod @args
}
Invoke-PBX GET "/v1/health" -Public
Invoke-PBX GET "/v1/environments"
$draft = Invoke-PBX POST "/v1/environments/draft" @{ os = "win64" }
Invoke-PBX POST "/v1/environments/env_001/start" @{
automation = "selenium"
}
Invoke-PBX GET "/v1/environments/env_001/status"
Invoke-PBX POST "/v1/environments/env_001/stop" @{}# Public health
curl http://127.0.0.1:8787/v1/health
# Authenticated request
curl -H "Authorization: Bearer YOUR_PBX_API_KEY" \
http://127.0.0.1:8787/v1/environments
# Draft
curl -X POST \
-H "Authorization: Bearer YOUR_PBX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"os":"win64"}' \
http://127.0.0.1:8787/v1/environments/draft
# Start with Selenium
curl -X POST \
-H "Authorization: Bearer YOUR_PBX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"automation":"selenium"}' \
http://127.0.0.1:8787/v1/environments/env_001/startconst BASE = "http://127.0.0.1:8787";
const API_KEY = "YOUR_PBX_API_KEY";
async function pbx(method, path, body = undefined, auth = true) {
const headers = { "Accept": "application/json" };
if (auth) headers["Authorization"] = `Bearer ${API_KEY}`;
if (body !== undefined) headers["Content-Type"] = "application/json";
const res = await fetch(BASE + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body)
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data));
return data;
}
console.log(await pbx("GET", "/v1/health", undefined, false));
console.log(await pbx("GET", "/v1/environments"));
const draft = await pbx("POST", "/v1/environments/draft", { os: "win64" });
await pbx("POST", "/v1/environments/env_001/start", {
automation: "cdp"
});
console.log(await pbx("GET", "/v1/environments/env_001/status"));using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var baseUrl = "http://127.0.0.1:8787";
var apiKey = "YOUR_PBX_API_KEY";
using var http = new HttpClient();
async Task PBX(
HttpMethod method,
string path,
object? body = null,
bool auth = true)
{
using var req = new HttpRequestMessage(method, baseUrl + path);
if (auth)
req.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
if (body is not null)
req.Content = new StringContent(
JsonSerializer.Serialize(body),
Encoding.UTF8,
"application/json");
using var res = await http.SendAsync(req);
var text = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
return text;
}
Console.WriteLine(await PBX(HttpMethod.Get, "/v1/health", auth:false));
Console.WriteLine(await PBX(HttpMethod.Get, "/v1/environments"));
Console.WriteLine(await PBX(
HttpMethod.Post,
"/v1/environments/draft",
new { os = "win64" }));
await PBX(
HttpMethod.Post,
"/v1/environments/env_001/start",
new { automation = "selenium" }); = 300)
throw new Exception("PBX API HTTP " . $status . ": " . $result);
return json_decode($result, true);
}
print_r(pbx("GET", "/v1/health", null, false));
print_r(pbx("GET", "/v1/environments"));
print_r(pbx("POST", "/v1/environments/draft", ["os" => "win64"]));
pbx("POST", "/v1/environments/env_001/start", [
"automation" => "cdp"
]);Complete endpoint reference
The API retains the route name /v1/environments for compatibility. In the product documentation these objects are called PBX virtual profiles.
| Method | Route | Auth | Purpose |
|---|---|---|---|
| GET | /v1/health | Public | Health check |
| GET | /v1/capabilities | Auth | Supported capabilities |
| GET | /v1/diagnostics | Auth | Current diagnostics |
| POST | /v1/diagnostics/export | Auth | Export diagnostics |
| GET | /v1/environments | Auth | List saved virtual profiles |
| GET | /v1/environments/{id} | Auth | Get one virtual profile |
| POST | /v1/environments/draft | Auth | Generate unsaved draft |
| POST | /v1/environments | Auth | Create/save a profile |
| POST | /v1/environments/batch | Auth | Create multiple profiles (1–100) |
| PATCH | /v1/environments/{id} | Auth | Partially update a profile |
| DELETE | /v1/environments/{id} | Auth | Delete a profile |
| POST | /v1/environments/{id}/clone | Auth | Clone a profile |
| POST | /v1/environments/{id}/validate | Auth | Validate a profile |
| GET | /v1/environments/{id}/status | Auth | Read lifecycle/automation status |
| POST | /v1/environments/{id}/start | Auth | Start profile using CDP or Selenium |
| POST | /v1/environments/{id}/stop | Auth | Stop profile |
| POST | /v1/proxy/check | Auth | Check a proxy configuration |
| PATCH | /v1/environments/{id}/proxy | Auth | Update profile proxy |
| POST | /v1/environments/{id}/proxy/check | Auth | Check assigned profile proxy |
| PATCH | /v1/environments/{id}/fingerprint | Auth | Update supported fingerprint settings |
| POST | /v1/environments/{id}/fingerprint/randomize | Auth | Randomize supported fingerprint fields |
| PATCH | /v1/environments/batch | Auth | Batch update profiles |
| POST | /v1/environments/batch/delete | Auth | Batch delete profiles |
| POST | /v1/environments/batch/fingerprint/randomize | Auth | Batch fingerprint randomization |
| POST | /v1/environments/batch/start | Auth | Batch start with CDP/Selenium automation |
| POST | /v1/environments/batch/stop | Auth | Batch stop |
Complete Python SDK method reference
The downloadable module exposes the following public helpers:
| Group | Python method | Underlying operation |
|---|---|---|
| Service / diagnostics | health() | GET /v1/health |
| Service / diagnostics | capabilities() | GET /v1/capabilities |
| Service / diagnostics | diagnostics() | GET /v1/diagnostics |
| Service / diagnostics | export_diagnostics() | POST /v1/diagnostics/export |
| Profiles | list_environments() | GET /v1/environments |
| Profiles | get_environment(id) | GET /v1/environments/{id} |
| Profiles | generate_draft(options) | POST /v1/environments/draft |
| Profiles | create_environment(options) | POST /v1/environments |
| Profiles | create_batch(count, template, name_prefix=None) | POST /v1/environments/batch |
| Profiles | update_environment(id, patch) | PATCH /v1/environments/{id} |
| Profiles | delete_environment(id) | DELETE /v1/environments/{id} |
| Profiles | clone_environment(id, overrides=None) | POST /v1/environments/{id}/clone |
| Profiles | validate_environment(id) | POST /v1/environments/{id}/validate |
| Lifecycle | status(id) | GET /v1/environments/{id}/status |
| Lifecycle | start(id, automation='cdp'|'selenium') | POST /v1/environments/{id}/start |
| Lifecycle helper | start_cdp(id) | Client helper |
| Lifecycle helper | start_selenium(id) | Client helper |
| Lifecycle helper | wait_for_status(id, desired, ...) | Polling helper |
| Lifecycle helper | wait_until_running(id, ...) | Polling helper |
| Lifecycle helper | wait_until_stopped(id, ...) | Polling helper |
| Lifecycle helper | automation(id) | Reads status and returns automation object |
| Lifecycle | stop(id) | POST /v1/environments/{id}/stop |
| Proxy | check_proxy(proxy) | POST /v1/proxy/check |
| Proxy | update_proxy(id, proxy) | PATCH /v1/environments/{id}/proxy |
| Proxy | set_direct_network(id) | PATCH /v1/environments/{id}/proxy |
| Proxy | check_environment_proxy(id) | POST /v1/environments/{id}/proxy/check |
| Fingerprint | update_fingerprint(id, patch) | PATCH /v1/environments/{id}/fingerprint |
| Fingerprint | randomize_fingerprint(id, fields=None) | POST /v1/environments/{id}/fingerprint/randomize |
| Batch | batch_update(ids, patch) | PATCH /v1/environments/batch |
| Batch | batch_delete(ids) | POST /v1/environments/batch/delete |
| Batch | batch_randomize_fingerprint(ids, fields=None) | POST /v1/environments/batch/fingerprint/randomize |
| Batch | batch_start(ids, automation=...) | POST /v1/environments/batch/start |
| Batch | batch_stop(ids) | POST /v1/environments/batch/stop |
Common request bodies
Generate a draft
POST /v1/environments/draft
{ "os": "win64" }
Create multiple profiles
POST /v1/environments/batch
{
"count": 5,
"template": { "os": "win64" },
"name_prefix": "QA"
}
The Python SDK validates count between 1 and 100.
Start a profile
POST /v1/environments/env_001/start
{
"automation": "selenium"
}
Use "cdp" or "selenium". API starts are automation launches; there is no Local API manual-start mode.
Update proxy
PATCH /v1/environments/env_001/proxy
{
"proxy": {
"enabled": true,
"host": "proxy.example",
"port": 1080
}
}
Set direct network
PATCH /v1/environments/env_001/proxy
{
"proxy": {
"enabled": false
}
}
Randomize selected supported fingerprint fields
POST /v1/environments/env_001/fingerprint/randomize
{
"fields": ["language", "timezone"]
}
Batch update
PATCH /v1/environments/batch
{
"environment_ids": ["env_001", "env_002"],
"patch": {
"name": "Updated"
}
}
Batch start
POST /v1/environments/batch/start
{
"environment_ids": ["env_001", "env_002"],
"automation": "cdp"
}
GET /v1/capabilities, generated drafts, and existing profile documents as the authoritative shape for the PBX version installed on the machine.Errors and lifecycle handling
The Python SDK raises PBXAPIError with status, code, message, and the parsed API response when available. Connection failures and timeouts use status 0.
from pbx_local_api import PBXLocalAPI, PBXAPIError
pbx = PBXLocalAPI.from_installed_pbx()
try:
pbx.start_selenium("env_001")
running = pbx.wait_until_running("env_001", timeout=60)
print(running)
except PBXAPIError as exc:
print("status :", exc.status)
print("code :", exc.code)
print("message:", exc.message)
Async start
Do not assume the browser is already ready when the start request returns. Poll /status or use the Python SDK's wait_until_running(). For Selenium, read the published automation information from profile status instead of attempting to use PBX's internal Chromium attachment endpoint.
Security
- Keep the Local API on loopback/local access.
- Do not publish the API key in websites, public JavaScript, repositories, screenshots, or logs.
- Prefer the Python SDK's installed-PBX discovery when your integration runs locally on the same Windows user account.
- Handle HTTP 403, 409, 429, connection errors, and lifecycle failures explicitly.
- Query
/v1/capabilitieswhen building integrations that must work across PBX versions.