Automation API
Home Support

Automation API

Control your Vela Browser programmatically via REST API and real-time WebSocket events. Manage tabs, windows, profiles, fingerprints, and every browser setting from any language or tool.

Authentication

All requests require an API key sent via the X-API-Key header or ?apiKey=YOUR_KEY query parameter.

Default: http://127.0.0.1:1306 — Configure in Settings → Automation

Status

GET /api/status Health check with version, uptime, counts

Returns server status, browser version, uptime, and current window/tab counts.

Response
{ "status": "running", "version": "1.0.0", "uptime": 3600.5, "windowCount": 2, "tabCount": 8, "automationPort": 1306, "bindAddress": "127.0.0.1" }
curl
curl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/status
GET /api/docs Built-in HTML documentation (no auth)

Returns a built-in HTML documentation page. No API key required.

curl
curl http://127.0.0.1:1306/api/docs

Tabs

GET /api/tabs List all tabs

Returns all open tabs across all windows. Optionally filter by window ID.

Query Parameters
ParamDescription
windowId optionalFilter tabs to a specific window
Response
[{ "id": "550e8400-...", "windowId": "...", "title": "Example", "url": "https://example.com", "isLoading": false, "canGoBack": true, "isPinned": false, "isPrivate": false, "isSuspended": false }]
curl
curl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/tabs
GET /api/tabs/:id Get tab details

Returns full details for a specific tab by ID.

curl
curl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/tabs/TAB_ID
POST /api/tabs Create a new tab

Opens a new tab. Optionally specify URL, target window, profile, or private mode.

Body
{ "url": "https://example.com", "windowId": "...", "profileId": "...", "private": false }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"url":"https://example.com"}' \ http://127.0.0.1:1306/api/tabs
DELETE /api/tabs/:id Close a tab

Closes the specified tab.

curl
curl -X DELETE -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/tabs/TAB_ID
POST /api/tabs/:id/navigate Navigate to URL

Navigates the tab to a new URL.

Body
{ "url": "https://example.com" }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"url":"https://example.com"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/navigate
POST /api/tabs/:id/reload Reload page

Reloads the current page in the tab.

curl
curl -X POST -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/tabs/TAB_ID/reload
POST /api/tabs/:id/back Go back

Navigate back in history.

POST /api/tabs/:id/forward Go forward

Navigate forward in history.

POST /api/tabs/:id/stop Stop loading

Stops the current page from loading.

POST /api/tabs/:id/activate Switch to tab

Makes this tab the active/selected tab in its window.

POST /api/tabs/:id/duplicate Duplicate tab

Creates a copy of the tab with the same URL.

POST /api/tabs/:id/pin Toggle pin

Toggles the pinned state of the tab.

POST /api/tabs/:id/mute Toggle mute

Toggles the muted state of the tab.

GET /api/tabs/:id/source Get page HTML source

Returns the full HTML source of the current page.

Response
{ "source": "<!DOCTYPE html>..." }

Page Automation

POST /api/tabs/:id/execute Execute JavaScript

Runs arbitrary JavaScript in the page context and returns the result.

Body
{ "script": "document.title" }
Response
{ "result": "Example Domain" }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"script":"document.title"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/execute
POST /api/tabs/:id/click Click element

Clicks the first element matching the CSS selector.

Body
{ "selector": "button.submit" }
POST /api/tabs/:id/fill Fill input field

Sets the value of an input field using native value setter (compatible with React, Angular, Vue).

Body
{ "selector": "input[name='email']", "value": "[email protected]" }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"selector":"input[name=email]","value":"[email protected]"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/fill
POST /api/tabs/:id/select Select dropdown option

Selects an option in a <select> element by value.

Body
{ "selector": "select#country", "value": "US" }
POST /api/tabs/:id/check Set checkbox state

Sets the checked state of a checkbox or radio input.

Body
{ "selector": "#agree", "checked": true }
POST /api/tabs/:id/type Type text character by character

Types text into the focused element with optional per-keystroke delay (simulates real typing).

Body
{ "selector": "input#search", "text": "Hello World", "delay": 50 }
ParamDescription
selectorCSS selector for the target element
textText to type
delay optionalDelay in ms between keystrokes (default: 0)
POST /api/tabs/:id/wait Wait for element

Waits until an element matching the selector appears in the DOM (uses MutationObserver).

Body
{ "selector": ".loaded", "timeout": 10000 }
ParamDescription
selectorCSS selector to wait for
timeout optionalTimeout in ms (default: 10000)
POST /api/tabs/:id/extract Extract data from elements

Extracts text content or attribute values from one or more elements.

Body
{ "selector": "a.link", "attribute": "href", "multiple": true }
Response
{ "result": ["/page1", "/page2"] }
POST /api/tabs/:id/scroll Scroll page

Scrolls to absolute coordinates or to a specific element.

Body (coordinates)
{ "x": 0, "y": 500 }
Body (selector)
{ "selector": "#footer" }
POST /api/tabs/:id/login Auto-fill credentials

Looks up saved credentials for the domain and auto-fills the login form.

Body
{ "domain": "github.com" }
GET /api/tabs/:id/screenshot Take screenshot (base64 PNG)

Captures a screenshot of the visible page and returns it as base64-encoded PNG.

Response
{ "data": "iVBORw0KGgo..." }
GET /api/tabs/:id/pdf Export page as PDF

Exports the current page as a PDF document returned as base64.

Response
{ "data": "JVBERi0..." }
GET /api/tabs/:id/cookies Get cookies

Returns all cookies for the tab's data store.

POST /api/tabs/:id/cookies Set cookies

Sets one or more cookies in the tab's data store.

Body
{ "cookies": [{ "name": "session", "value": "abc123", "domain": "example.com", "path": "/" }] }
DELETE /api/tabs/:id/cookies Clear cookies

Removes all cookies from the tab's data store.

GET /api/tabs/:id/text Get visible text

Returns the visible text content of the page (innerText of body).

Response
{ "text": "Page content here..." }
POST /api/tabs/:id/upload Upload files to file input

Programmatically sets files on a <input type="file"> element. Files are provided as base64-encoded data. This triggers the change event as if the user selected files via the native file picker.

Body
{ "selector": "input[type=file]", "files": [{ "name": "photo.png", "data": "iVBORw0KGgo...", "type": "image/png" }] }
ParamDescription
selectorCSS selector for the file input element
filesArray of file objects
files[].nameFile name (e.g. "report.pdf")
files[].dataBase64-encoded file content
files[].type optionalMIME type (default: application/octet-stream)
Response
{ "success": true, "filesSet": 1 }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"selector":"input[type=file]","files":[{"name":"test.txt","data":"SGVsbG8gV29ybGQ=","type":"text/plain"}]}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/upload

Windows

GET /api/windows List all windows

Returns all open browser windows.

Response
[{ "id": "...", "tabCount": 5, "isPrivate": false, "profileId": "...", "currentTabId": "..." }]
GET /api/windows/:id Get window details

Returns details for a specific window.

POST /api/windows Open new window

Opens a new browser window, optionally with a specific profile or in private mode. Returns the new window ID and active tab ID.

Body
{ "profileId": "...", "private": false }
Response
{ "opened": true, "windowId": "UUID", "tabId": "UUID" }
Supports creating many windows concurrently (e.g. bulk automation of 20+ accounts at once). In GUI mode, real windows are opened. In hidden/headless mode, windows are created instantly without UI. All three modes return windowId and tabId reliably.
POST /api/windows/:id/activate Activate window (bring to front)

Brings the window to the foreground and activates the Vela app. In headed mode, makes the window the key window and calls NSApplication.activate(ignoringOtherApps: true). In hidden/headless mode this is a no-op.

Response
{ "activated": true }
Useful for ensuring headed-mode windows are visible during automation. Without activation, macOS may keep API-created windows behind other apps.
DELETE /api/windows/:id Close window

Closes the specified browser window and all its tabs. Uses a direct NSWindow reference for reliable closing, and always deregisters the window from the API even if the physical close fails.

Window → Active Tab (Convenience)

All tab operations are mirrored at the window level. These endpoints automatically target the window's active tab, so you don't need to look up the tab ID first. Same request/response format as the tab equivalents.

POST /api/windows/:id/navigate Navigate active tab

Navigate the window's active tab to a URL. Body: { "url": "https://..." }

POST/api/windows/:id/reloadReload active tab

Reload the current page on the window's active tab.

POST/api/windows/:id/backGo back on active tab

Navigate back in the active tab's history.

POST/api/windows/:id/forwardGo forward on active tab

Navigate forward in the active tab's history.

POST/api/windows/:id/stopStop loading

Stop loading the page on the active tab.

POST/api/windows/:id/executeRun JavaScript on active tab

Execute JavaScript on the active tab. Body: { "script": "document.title" }

POST/api/windows/:id/clickClick element on active tab

Click an element. Body: { "selector": "button.submit" }

POST/api/windows/:id/fillFill input on active tab

Fill an input field. Body: { "selector": "input[name='email']", "value": "[email protected]" }

POST/api/windows/:id/selectSelect dropdown on active tab

Select a dropdown option. Body: { "selector": "select#country", "value": "US" }

POST/api/windows/:id/checkToggle checkbox on active tab

Set checkbox state. Body: { "selector": "#agree", "checked": true }

POST/api/windows/:id/typeType text on active tab

Type text character by character. Body: { "selector": "#search", "text": "hello", "delay": 50 }

POST/api/windows/:id/waitWait for element on active tab

Wait for an element to appear. Body: { "selector": ".loaded", "timeout": 10000 }

POST/api/windows/:id/extractExtract content from active tab

Extract text or attributes. Body: { "selector": "h1", "attribute": "textContent", "multiple": false }

POST/api/windows/:id/scrollScroll on active tab

Scroll by offset or to element. Body: { "y": 500 } or { "selector": "#footer" }

POST/api/windows/:id/loginAuto-fill credentials on active tab

Fill saved credentials. Body: { "domain": "example.com" }

GET/api/windows/:id/screenshotScreenshot active tab

Capture a screenshot of the active tab as base64 PNG.

GET/api/windows/:id/pdfExport PDF from active tab

Export the active tab as a base64-encoded PDF.

GET/api/windows/:id/cookiesGet cookies from active tab

Get cookies for the active tab's current domain.

POST/api/windows/:id/cookiesSet cookies on active tab

Set cookies. Body: { "cookies": [{ "name": "...", "value": "...", "domain": "..." }] }

DELETE/api/windows/:id/cookiesClear cookies on active tab

Clear all cookies for the active tab's current domain.

GET/api/windows/:id/textGet visible text from active tab

Get all visible text content from the active tab.

POST/api/windows/:id/uploadUpload files on active tab

Upload files to a file input. Body: { "selector": "input[type=file]", "files": [{ "name": "doc.pdf", "data": "base64...", "type": "application/pdf" }] }

POST/api/windows/:id/mouse/moveMove cursor on active tab

Move mouse cursor with easing on the active tab. Body: { "x": 500, "y": 300 } or { "selector": "#btn" } + optional "duration", "steps", "easing" (ease|linear|human).

POST/api/windows/:id/mouse/clickClick on active tab

Full click sequence on active tab. Body: { "selector": "#btn" } + optional "button" (left|right|middle), "doubleClick".

POST/api/windows/:id/mouse/downMouse down on active tab

Dispatches pointerdown + mousedown on the active tab.

POST/api/windows/:id/mouse/upMouse up on active tab

Dispatches pointerup + mouseup on the active tab.

POST/api/windows/:id/mouse/randomRandom mouse movement on active tab

Human-like random mouse movement on the active tab. Body: { "duration": 3000 } + optional "area", "speed".

POST/api/windows/:id/mouse/pathMouse path on active tab

Move along waypoints on the active tab. Body: { "points": [{"x":100,"y":100}, ...], "duration": 1000 }

Profiles

GET /api/profiles List all profiles

Returns all browser profiles with their configuration.

Response
[{ "id": "...", "name": "Work", "icon": "briefcase", "color": "blue", "fingerprintSeed": 12345, "fingerprintEnabled": true, "spoofLanguage": "en-US,en", "spoofTimezone": "America/New_York", "userAgentId": "chrome_mac", "proxyConfig": null }]
GET /api/profiles/:id Get profile details

Returns full details for a specific profile.

POST /api/profiles Create profile with all settings

Creates a new browser profile with isolated cookies, storage, and fingerprint. Accepts ALL profile settings on creation.

Body (all fields optional except name)
{ "name": "Shopping", "icon": "cart", "color": "#10B981", "userAgentId": "chrome_mac", "fingerprintEnabled": true, "fingerprintSeed": 123456, "proxyConfig": { "type": "SOCKS5", "host": "proxy.example.com", "port": 1080, "username": "user", "password": "pass", "isEnabled": true }, "dnsProvider": "cloudflare", "contentBlockerEnabled": true, "blockTrackers": true, "blockAds": true, "blockPopups": true, "httpsFirstEnabled": true, "torEnabled": false, "autofillEnabled": true, "savePasswordEnabled": true, "spoofLanguage": "en-US,en", "spoofTimezone": "America/New_York", "sessionCookies": [{ "name": "session", "value": "abc123", "domain": ".example.com", "path": "/", "isSecure": true }] }
ParamDescription
nameProfile name (required)
icon optionalSF Symbol name (person.circle, briefcase, house, cart, etc.)
color optionalHex color (e.g. #4A90D9)
proxyConfig optionalPer-profile proxy: type (HTTP/SOCKS5), host, port, username, password, isEnabled
dnsProvider optionalDNS: system, cloudflare, google, quad9, adguard, opendns, cleanbrowsing, nextdns
sessionCookies optionalArray of cookies to store with the profile
privacy flags optionalcontentBlockerEnabled, blockTrackers, blockAds, blockPopups, httpsFirstEnabled, torEnabled
autofillEnabled optionalEnable/disable password autofill for this profile (null = use global)
savePasswordEnabled optionalEnable/disable save-password prompts for this profile (null = use global)
spoofLanguage optionalOverride navigator.languages / Accept-Language, e.g. "en-US,en" or "fr-FR,fr" (null = use browser default)
spoofTimezone optionalOverride reported timezone via Intl/Date APIs, e.g. "America/New_York" or "Europe/Paris" (null = use system timezone)
curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"name":"Shopping","icon":"cart","color":"#10B981","dnsProvider":"cloudflare","proxyConfig":{"type":"SOCKS5","host":"proxy.example.com","port":1080,"isEnabled":true}}' \ http://127.0.0.1:1306/api/profiles
PUT /api/profiles/:id Update profile

Partially update any profile field (name, icon, color, UA, fingerprint, proxy, DNS, privacy settings).

Body (partial update)
{ "name": "New Name", "fingerprintEnabled": true }
DELETE /api/profiles/:id Delete profile

Deletes a profile. Cannot delete the default profile.

POST /api/profiles/:id/activate Switch window to profile

Switches a window to use this profile.

Body
{ "windowId": "..." }
POST /api/profiles/:id/duplicate Duplicate profile

Creates a copy of the profile with a new fingerprint seed.

POST /api/profiles/:id/regenerate-fingerprint New fingerprint seed

Generates a new random fingerprint seed for the profile.

User Agents

GET /api/user-agents List 48+ UA presets

Returns all available user agent presets organized by category (Desktop, Mobile, Bots, etc.).

Response
[{ "id": "chrome_mac", "name": "Chrome on macOS", "value": "Mozilla/5.0 ...", "category": "Desktop", "isSafeForLogin": true }]
GET /api/user-agents/current Get current global UA

Returns the currently active global user agent.

PUT /api/user-agents/current Set global UA
Body
{ "id": "iphone16promax" }
PUT /api/profiles/:id/user-agent Set profile UA
Body
{ "id": "chrome_windows" }

Fingerprints

GET /api/fingerprint Global fingerprint status
Response
{ "enabled": true, "seed": 42 }
PUT /api/fingerprint Toggle fingerprint protection
Body
{ "enabled": true }
GET /api/profiles/:id/fingerprint Profile fingerprint config

Returns the fingerprint configuration for a specific profile.

Response
{ "enabled": true, "seed": 12345, "spoofLanguage": "en-US,en", "spoofTimezone": "America/New_York" }
PUT /api/profiles/:id/fingerprint Update profile fingerprint
Body
{ "enabled": true, "seed": 99999, "spoofLanguage": "en-US,en", "spoofTimezone": "America/New_York" }
ParamDescription
enabled optionalEnable/disable fingerprint protection
seed optionalInteger seed for deterministic fingerprint values
spoofLanguage optionalOverride navigator.languages, e.g. "en-US,en" (null to clear)
spoofTimezone optionalOverride timezone via Intl/Date APIs, e.g. "America/New_York" (null to clear)
POST /api/profiles/:id/fingerprint/regenerate New random seed

Generates a new random fingerprint seed for the profile.

Settings

GET /api/settings List all settings

Returns all vela_* UserDefaults settings as key-value pairs.

Response
[ { "key": "vela_automation_port", "value": 1306 }, { "key": "vela_dark_mode", "value": true } ]
GET /api/settings/:key Get specific setting

Returns the value for a single setting key.

PUT /api/settings/:key Update setting
Body
{ "value": true }
curl
curl -X PUT -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"value": true}' \ http://127.0.0.1:1306/api/settings/vela_dark_mode

Bookmarks

GET /api/bookmarks List bookmarks

Returns all bookmarks. Optionally filter by folder.

ParamDescription
folderId optionalFilter to a specific folder
POST /api/bookmarks Create bookmark
Body
{ "title": "Example", "url": "https://example.com", "folderId": "..." }
PUT /api/bookmarks/:id Update bookmark

Update a bookmark's title, URL, or folder.

DELETE /api/bookmarks/:id Delete bookmark

Removes a bookmark.

GET /api/bookmarks/folders List folders

Returns all bookmark folders.

POST /api/bookmarks/folders Create folder
Body
{ "name": "Dev Tools", "parentId": "..." }
DELETE /api/bookmarks/folders/:id Delete folder

Removes a bookmark folder.

History

GET /api/history List history entries
ParamDescription
limit optionalMax results (default: 100)
query optionalSearch text filter
GET /api/history/top Top visited sites

Returns the most frequently visited sites.

DELETE /api/history Clear all history

Permanently deletes all browsing history.

DELETE /api/history/:id Delete single entry

Removes a single history entry by ID.

Downloads

GET /api/downloads List downloads
Response
[{ "id": "...", "fileName": "file.zip", "url": "https://...", "state": "completed", "progress": 1.0, "totalBytes": 1048576, "downloadedBytes": 1048576 }]
GET /api/downloads/:id Get download details

Returns details for a single download including progress, state, and byte counts.

Response
{ "id": "...", "fileName": "file.zip", "url": "https://...", "state": "downloading", "progress": 0.45, "totalBytes": 1048576, "downloadedBytes": 471859 }
POST /api/downloads Start download

Starts downloading a file from the given URL.

Body
{ "url": "https://example.com/file.zip", "fileName": "custom.zip" }
ParamDescription
urlURL of the file to download
fileName optionalCustom file name (defaults to URL's last path component)
POST /api/downloads/:id/pause Pause download

Pauses an in-progress download. The download can be resumed later with the resume endpoint.

curl
curl -X POST -H "X-API-Key: YOUR_KEY" \ http://127.0.0.1:1306/api/downloads/DOWNLOAD_ID/pause
POST /api/downloads/:id/resume Resume download

Resumes a paused download from where it left off.

curl
curl -X POST -H "X-API-Key: YOUR_KEY" \ http://127.0.0.1:1306/api/downloads/DOWNLOAD_ID/resume
DELETE /api/downloads/:id Cancel/remove download

Cancels an in-progress download or removes a completed one.

Passwords

GET /api/passwords List credentials (passwords hidden)

Lists all saved credentials. Passwords are not included in the response for security.

Response
[{ "id": "...", "domain": "github.com", "username": "[email protected]", "title": "GitHub" }]
GET /api/passwords/:id Get full credential (requires extra header)

Returns the full credential including password. Requires the X-Password-Access: true header for additional security.

curl
curl -H "X-API-Key: YOUR_KEY" -H "X-Password-Access: true" \ http://127.0.0.1:1306/api/passwords/PASS_ID
POST /api/passwords/search Search by domain
Body
{ "domain": "github.com" }
POST /api/passwords/generate Generate secure password
Body
{ "length": 24, "symbols": true }
Response
{ "password": "xK9#mP2$vL7@nQ4..." }

Profile Autofill

GET /api/profiles/:id/autofill Get per-profile autofill settings

Returns autofill and save-password settings for a specific profile. null values mean the profile uses the global setting.

Response
{ "profileId": "...", "autofillEnabled": null, "savePasswordEnabled": null, "note": "null values mean the profile uses global settings" }
curl
curl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/profiles/PROFILE_ID/autofill
PUT /api/profiles/:id/autofill Update per-profile autofill settings

Enable or disable autofill and save-password prompts for a specific profile. Pass resetToGlobal: true to clear overrides and inherit global settings.

Body
{ "autofillEnabled": false, "savePasswordEnabled": false }
ParamDescription
autofillEnabled optionalEnable/disable password autofill for this profile
savePasswordEnabled optionalEnable/disable save-password prompts for this profile
resetToGlobal optionalSet to true to clear overrides and use global settings
curl
curl -X PUT -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"autofillEnabled":false,"savePasswordEnabled":false}' \ http://127.0.0.1:1306/api/profiles/PROFILE_ID/autofill

Content Blocker

GET /api/content-blocker Get content blocker status
Response
{ "enabled": true, "blockAds": true, "blockTrackers": true, "blockPopups": true, "extraBlockTrackers": false, "extraBlockAds": false, "automaticCookieConsent": true }
PUT /api/content-blocker Update content blocker
Body
{ "enabled": true, "blockAds": true, "blockTrackers": true, "blockPopups": false, "extraBlockTrackers": true, "extraBlockAds": false, "automaticCookieConsent": true }

Note: extraBlockTrackers enables 80+ additional tracker domains. extraBlockAds enables 60+ additional ad networks. Both are off by default. automaticCookieConsent handles cookie consent dialogs on supported sites.

Profile Cookies

GET /api/profiles/:id/cookies Get stored session cookies

Returns all session cookies stored in the profile configuration.

Response
{ "cookies": [{ "id": "...", "name": "session", "value": "abc123", "domain": ".example.com", "path": "/", "isSecure": true, "isHttpOnly": false }], "count": 1 }
PUT /api/profiles/:id/cookies Set session cookies

Replaces all stored session cookies for the profile.

Body
{ "cookies": [{ "name": "session", "value": "abc123", "domain": ".example.com", "path": "/", "isSecure": true, "isHttpOnly": false }] }
curl
curl -X PUT -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"cookies":[{"name":"session","value":"abc123","domain":".example.com"}]}' \ http://127.0.0.1:1306/api/profiles/PROFILE_ID/cookies
DELETE /api/profiles/:id/cookies Clear session cookies

Removes all stored session cookies from the profile.

POST /api/profiles/:id/cookies/inject Inject cookies into live data store

Takes the profile's stored session cookies and injects them into the live WKWebsiteDataStore, making them immediately available to web pages.

Response
{ "injected": 5 }
GET /api/profiles/:id/cookies/live Get ALL live cookies from data store

Returns all cookies currently in the profile's live data store (not just stored ones). Optionally filter by domain.

ParamDescription
domain optionalFilter cookies to a specific domain
curl
curl -H "X-API-Key: YOUR_KEY" "http://127.0.0.1:1306/api/profiles/PROFILE_ID/cookies/live?domain=example.com"

Profile Export / Import

POST /api/profiles/:id/export Export profile as JSON

Exports a complete profile including all settings, proxy config, DNS, privacy flags, session cookies, and ALL live cookies from the data store. The output can be fed directly into the import endpoint.

Response (truncated)
{ "id": "...", "name": "Work", "proxyConfig": { ... }, "dnsProvider": "cloudflare", "sessionCookies": [...], "liveCookies": [...], "liveCookieCount": 42, "exportedAt": "2025-01-15T10:30:00Z" }
curl
curl -X POST -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/profiles/PROFILE_ID/export
POST /api/profiles/import Import profile from JSON

Creates a new profile from JSON data (same format as export). Live cookies are injected into the new profile's data store.

Body
// Pass the full JSON from the export endpoint as the body { "name": "Work", "proxyConfig": {...}, "liveCookies": [...] }
POST /api/profiles/import-url Import profile from remote URL

Downloads a profile ZIP from a remote URL (Dropbox, Google Drive, direct links) and imports it.

Body
{ "url": "https://dropbox.com/s/.../profile.zip" }

Profile Size & Data

GET /api/profiles/:id/size Get profile data store size

Returns the number of data records, cookies, and data type breakdown for a profile's storage.

Response
{ "profileId": "...", "totalRecords": 15, "cookieCount": 42, "dataTypes": { "WKWebsiteDataTypeCookies": 8, "WKWebsiteDataTypeLocalStorage": 5, "WKWebsiteDataTypeDiskCache": 2 }, "displayNames": ["example.com", "google.com"] }
DELETE /api/profiles/:id/data Clear all profile browsing data

Removes ALL browsing data for the profile (cookies, cache, local storage, indexed databases, etc.).

curl
curl -X DELETE -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/profiles/PROFILE_ID/data

Profile Permissions / Privacy

GET /api/profiles/:id/permissions Get privacy settings

Returns all privacy/permission settings for the profile. null values mean the profile inherits the global setting.

Response
{ "contentBlockerEnabled": true, "blockTrackers": true, "blockAds": true, "blockPopups": null, "httpsFirstEnabled": true, "torEnabled": false, "fingerprintEnabled": true, "automaticCookieConsent": true }
PUT /api/profiles/:id/permissions Update privacy settings

Update privacy/permission settings for the profile. Pass "resetToGlobal": true to clear all overrides and use global settings.

Body
{ "contentBlockerEnabled": true, "blockTrackers": true, "blockAds": false, "httpsFirstEnabled": true, "torEnabled": false, "fingerprintEnabled": true, "automaticCookieConsent": true }
Reset to global
{ "resetToGlobal": true }

Global Proxy

GET /api/proxy Get global proxy config

Returns the global proxy configuration (applies to profiles without their own proxy).

Response
{ "enabled": true, "type": "HTTP", "host": "proxy.example.com", "port": 8080, "username": "user", "password": "pass" }
PUT /api/proxy Set global proxy

Update the global proxy configuration. All fields are optional (partial update).

Body
{ "enabled": true, "type": "SOCKS5", "host": "proxy.example.com", "port": 1080, "username": "user", "password": "pass" }
curl
curl -X PUT -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"enabled":true,"type":"SOCKS5","host":"proxy.example.com","port":1080}' \ http://127.0.0.1:1306/api/proxy

Global DNS

GET /api/dns Get DNS provider + list all

Returns the current DNS provider and lists all available providers with details.

Response
{ "current": "cloudflare", "currentName": "Cloudflare", "providers": [{ "id": "cloudflare", "name": "Cloudflare", "detail": "1.1.1.1, 1.0.0.1", "isCurrent": true }] }
PUT /api/dns Set DNS provider

Changes the global DNS provider.

Body
{ "provider": "cloudflare" }
Valid providersDNS Servers
systemOS/router default
cloudflare1.1.1.1, 1.0.0.1
google8.8.8.8, 8.8.4.4
quad99.9.9.9, 149.112.112.112
adguard94.140.14.14, 94.140.15.15
opendns208.67.222.222, 208.67.220.220
cleanbrowsing185.228.168.9, 185.228.169.9
nextdns45.90.28.0, 45.90.30.0
POST /api/dns/probe Measure DNS latency

Measures the latency to a specific DNS provider in milliseconds.

Body
{ "provider": "cloudflare" }
Response
{ "provider": "cloudflare", "latencyMs": 12, "reachable": true }
POST /api/dns/probe-all Measure all DNS providers

Tests latency for all DNS providers, sorted by fastest first.

Response
{ "results": [ { "provider": "cloudflare", "latencyMs": 12, "reachable": true }, { "provider": "google", "latencyMs": 18, "reachable": true } ] }

Tor

GET /api/tor Full Tor status

Returns comprehensive Tor status including connection state, SOCKS proxy details, and circuit info.

Response
{ "enabled": true, "isConnected": true, "isConnecting": false, "circuitInfo": "Connected to Tor network", "socksHost": "127.0.0.1", "socksPort": 9150, "controlPort": 9151, "isTorInstalled": true, "errorMessage": null }
PUT /api/tor Enable/disable Tor
Body
{ "enabled": true }
POST /api/tor/new-circuit Request new Tor identity

Requests a new Tor circuit (new exit IP). Tor must be connected. Sends SIGNAL NEWNYM to the Tor control port.

curl
curl -X POST -H "X-API-Key: YOUR_KEY" http://127.0.0.1:1306/api/tor/new-circuit
Response
{ "requested": true }

Mouse Simulation

POST /api/tabs/:id/mouse/move Move mouse cursor with easing

Smoothly moves the simulated mouse cursor to a target position or CSS selector. Dispatches pointermove and mousemove events along the path. Supports multiple easing modes including human-like movement with random jitter.

Request Body
{ "x": 500, "y": 300, "duration": 200, "steps": 10, "easing": "ease" }
ParamTypeDescription
xnumberTarget X coordinate (or use selector)
ynumberTarget Y coordinate (or use selector)
selectorstringCSS selector — moves to element center (alternative to x/y)
durationnumberMovement duration in ms (default: 100)
stepsnumberNumber of intermediate steps (default: 10)
easingstring"linear", "ease", or "human" (adds ±3px jitter)
cURL Example
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"selector":"#submit-btn","duration":300,"easing":"human"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/mouse/move
Response
{ "success": true, "x": 500, "y": 300, "element": "button" }
POST /api/tabs/:id/mouse/click Full click sequence at position or selector

Dispatches a complete click event sequence at the target: pointerdown → mousedown → pointerup → mouseup → click. Optionally fires dblclick for double-click.

Request Body
{ "selector": "#login-btn", "button": "left", "doubleClick": false }
ParamTypeDescription
x / ynumberTarget coordinates (or use selector)
selectorstringCSS selector — clicks element center
buttonstring"left" (default), "right", or "middle"
doubleClickbooleanAlso fire dblclick event (default: false)
Response
{ "success": true, "x": 250, "y": 180, "element": "button" }
POST /api/tabs/:id/mouse/down Mouse button down

Dispatches pointerdown and mousedown events at the target position. Use with /mouse/up for drag operations.

Request Body
{ "x": 200, "y": 150, "button": "left" }
Response
{ "success": true, "x": 200, "y": 150, "element": "div" }
POST /api/tabs/:id/mouse/up Mouse button up

Dispatches pointerup and mouseup events at the target position. Use after /mouse/down to complete drag operations.

Request Body
{ "x": 400, "y": 300, "button": "left" }
Response
{ "success": true, "x": 400, "y": 300, "element": "div" }
POST /api/tabs/:id/mouse/random Random human-like mouse movement

Generates random human-like mouse movements for a specified duration. Moves between random targets within a bounding area with ease-in-out curves and micro-jitter. Useful for anti-detection and making automated sessions appear natural.

Request Body
{ "duration": 3000, "area": { "x": 100, "y": 100, "width": 800, "height": 600 }, "speed": "normal" }
ParamTypeDescription
durationnumberHow long to move in milliseconds (required)
areaobjectBounding box { x, y, width, height } — defaults to viewport
speedstring"slow" (80ms), "normal" (40ms), or "fast" (20ms) step intervals
cURL Example
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"duration":5000,"speed":"slow"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/mouse/random
Response
{ "success": true, "moves": 87, "finalX": 432.5, "finalY": 287.3 }
POST /api/tabs/:id/mouse/path Move along array of waypoints

Moves the mouse cursor along a sequence of waypoints with proportional timing based on segment distances. Each segment uses the specified easing function.

Request Body
{ "points": [ { "x": 100, "y": 100 }, { "x": 300, "y": 200 }, { "x": 500, "y": 150 } ], "duration": 1000, "easing": "human" }
ParamTypeDescription
pointsarrayArray of { x, y } waypoints (minimum 2, required)
durationnumberTotal duration in ms (default: 500)
easingstring"linear", "ease", or "human"
Response
{ "success": true, "x": 500, "y": 150, "points": 3 }

Element Queries

POST/api/tabs/:id/element/visibleCheck if element is visible

Check if an element is visible (has dimensions, not hidden/display:none).

Body
{ "selector": "#submit-btn" }
Response
{ "visible": true }
POST/api/tabs/:id/element/enabledCheck if element is enabled

Check if an element is not disabled.

Body
{ "selector": "button" }
POST/api/tabs/:id/element/checkedCheck if checkbox/radio is checked

Check the checked state of an input element.

POST/api/tabs/:id/element/hiddenCheck if element is hidden

Inverse of visible — returns true if element has no dimensions or is hidden.

POST/api/tabs/:id/element/countCount matching elements

Count elements matching the selector.

Response
{ "count": 5 }
POST/api/tabs/:id/element/bounding-boxGet element bounding rectangle

Returns x, y, width, height, top, right, bottom, left.

POST/api/tabs/:id/element/inner-htmlGet element innerHTML

Returns the innerHTML of the first matching element.

POST/api/tabs/:id/element/inner-textGet element innerText

Returns the rendered innerText of the element.

POST/api/tabs/:id/element/text-contentGet element textContent

Returns the raw textContent of the element (includes hidden text).

POST/api/tabs/:id/element/attributeGet element attribute

Get a specific attribute value.

Body
{ "selector": "a", "name": "href" }
POST/api/tabs/:id/element/screenshotScreenshot a specific element

Take a screenshot of a specific element (base64 PNG). macOS only.

Element Actions

POST/api/tabs/:id/hoverHover over element

Dispatch pointerenter/mouseover/mouseenter events on element center.

POST/api/tabs/:id/focusFocus element

Call el.focus() on the matched element.

POST/api/tabs/:id/dblclickDouble-click element

Dispatch full mousedown/mouseup/click/dblclick sequence.

POST/api/tabs/:id/pressPress key on element

Dispatch keydown/keypress/keyup on element or active element.

Body
{ "key": "Enter", "selector": "input" }
POST/api/tabs/:id/uncheckUncheck checkbox

Set checked=false and dispatch change event.

GET/api/tabs/:id/contentGet full page HTML

Returns document.documentElement.outerHTML.

GET/api/tabs/:id/page-titleGet page title

Returns the current tab title.

Keyboard Input

POST/api/tabs/:id/keyboard/pressPress key with optional modifiers

Dispatch keydown/keypress/keyup on the active element.

Body
{ "key": "a", "modifiers": ["Control"] }
POST/api/tabs/:id/keyboard/insert-textInsert text at cursor

Insert text using execCommand or value setter.

POST/api/tabs/:id/keyboard/downKey down event

Dispatch keydown only.

POST/api/tabs/:id/keyboard/upKey up event

Dispatch keyup only.

POST/api/tabs/:id/mouse/wheelMouse wheel scroll

Dispatch WheelEvent.

Body
{ "deltaX": 0, "deltaY": 100 }

Native OS Input

Dispatch input via macOS native events — produces isTrusted: true events that are indistinguishable from real user input. In headed mode, uses CGEvent (native OS events posted to screen coordinates). In hidden/headless mode, injects NSEvent directly into the WKWebView — still produces isTrusted: true events without needing a visible window. All modes now return "native": true. Safe for 20+ concurrent hidden instances (pasteboard access is serialized). Headed mode requires Accessibility permission (System Settings > Privacy & Security > Accessibility).

POST/api/tabs/:id/native/clickNative mouse click (isTrusted:true)

Click an element or coordinates using real OS input events. The element is scrolled into view first. In headed mode, the window is activated and a CGEvent mouse click is posted at screen coordinates. In hidden/headless mode, an NSEvent is injected directly into the WKWebView (still isTrusted:true). Both modes work reliably for concurrent instances.

Body
{ "selector": "#submit-btn" } // or { "x": 150, "y": 300, "button": "left", "doubleClick": false }
Response
{ "success": true, "native": true, "x": 150, "y": 300, "screenX": 650, "screenY": 400 }
Curl
curl -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{"selector": "#submit-btn"}' \ http://127.0.0.1:1306/api/tabs/TAB_ID/native/click
POST/api/tabs/:id/native/typeNative keyboard typing

Type text character by character using real OS keyboard events. Each keystroke produces isTrusted: true. Optional selector focuses the element first. Delay controls milliseconds between keystrokes.

Body
{ "text": "hello world", "selector": "#search", "delay": 50 }
Response
{ "success": true, "native": true, "length": 11 }
POST/api/tabs/:id/native/pressNative key press with modifiers

Press a key with optional modifiers using real OS keyboard events. Supports: Enter, Tab, Escape, Backspace, Space, Arrow keys, F1-F12, Home, End, PageUp, PageDown, all letters and digits. Modifiers: Command, Control, Shift, Alt/Option.

Body
{ "key": "Enter", "modifiers": ["Command"] }
Response
{ "success": true, "native": true, "key": "Enter", "modifiers": ["Command"] }
POST/api/tabs/:id/native/pasteNative paste via clipboard + Cmd+V

Sets the system clipboard with text (and optionally HTML), then sends a real Cmd+V keystroke. In headed mode, activates the window and uses CGEvent. In hidden/headless mode, injects NSEvent Cmd+V directly into the WKWebView (still isTrusted:true). Pasteboard access is serialized for safe use with 20+ concurrent hidden instances.

Body
{ "text": "Hello", "html": "<b>Hello</b>", "selector": ".editor" }
Response
{ "success": true, "native": true, "length": 5, "hasHtml": true }
POST/api/tabs/:id/native/moveNative mouse move with interpolation

Move the mouse cursor using OS events with smooth interpolation (ease-in-out). Steps controls intermediate points, duration is total time in ms.

Body
{ "selector": "#target", "steps": 10, "duration": 200 } // or { "x": 300, "y": 400 }
Response
{ "success": true, "native": true, "x": 300, "y": 400 }

localStorage / sessionStorage

POST/api/tabs/:id/storage/local/getGet localStorage value

Get a value from localStorage.

Body
{ "key": "token" }
POST/api/tabs/:id/storage/local/setSet localStorage value
Body
{ "key": "token", "value": "abc123" }
POST/api/tabs/:id/storage/local/removeRemove localStorage key

Remove a key from localStorage.

POST/api/tabs/:id/storage/local/clearClear all localStorage

Clear all localStorage entries.

POST/api/tabs/:id/storage/session/getGet sessionStorage value

Get a value from sessionStorage.

POST/api/tabs/:id/storage/session/setSet sessionStorage value

Set a value in sessionStorage.

POST/api/tabs/:id/storage/session/removeRemove sessionStorage key

Remove a key from sessionStorage.

POST/api/tabs/:id/storage/session/clearClear all sessionStorage

Clear all sessionStorage entries.

Wait Functions

POST/api/tabs/:id/wait-for-functionWait for JS expression to be truthy

Polls a JS expression until it returns a truthy value. Max timeout: 300s.

Body
{ "expression": "document.querySelector('.loaded')", "timeout": 30000, "pollingInterval": 100 }
POST/api/tabs/:id/wait-for-timeoutWait for specified duration

Simple sleep/delay. Max: 300s.

Body
{ "timeout": 2000 }
POST/api/tabs/:id/wait-for-navigationWait for URL to change and page to load

Polls until the tab URL changes and loading completes.

POST/api/tabs/:id/wait-for-load-stateWait for page load state

Wait for "load", "domcontentloaded", or "networkidle" state.

Body
{ "state": "domcontentloaded" }
POST/api/tabs/:id/wait-for-urlWait for URL to match pattern

Polls until the tab URL contains the given pattern.

Body
{ "url": "dashboard" }

Frames

GET/api/tabs/:id/framesList all frames

Enumerate all frames with index, name, and URL. Cross-origin frames show "(cross-origin)".

POST/api/tabs/:id/frames/:index/executeExecute JS in frame

Run JavaScript in a specific same-origin frame by index. Returns 403 for cross-origin frames.

POST/api/tabs/:id/frames/:index/clickClick element in frame

Click an element within a same-origin child frame. Returns 403 for cross-origin frames.

Accessibility

GET/api/tabs/:id/accessibilityAccessibility tree snapshot

Walk the DOM tree and return roles, aria labels, text content, IDs, and class names as a nested JSON tree.

Emulation

PUT/api/tabs/:id/emulation/geolocationOverride geolocation

Override navigator.geolocation for the tab.

Body
{ "latitude": 40.7128, "longitude": -74.006, "accuracy": 100 }
PUT/api/tabs/:id/emulation/timezoneOverride timezone

Override Intl.DateTimeFormat timezone.

Body
{ "timezoneId": "America/New_York" }
PUT/api/tabs/:id/emulation/localeOverride locale

Override navigator.language and navigator.languages.

Body
{ "locale": "fr-FR" }
PUT/api/tabs/:id/emulation/color-schemeSet color scheme

Force light or dark mode for the page.

Body
{ "colorScheme": "dark" }

Dialog Handling

PUT/api/tabs/:id/dialog/configConfigure auto-response for dialogs

Set how alerts/confirms/prompts are handled.

Body
{ "action": "accept", "promptText": "yes" }
GET/api/tabs/:id/dialogCheck for pending dialog

Check if there is a dialog waiting for resolution.

POST/api/tabs/:id/dialog/acceptAccept pending dialog

Accept the pending dialog (OK/Yes). Optionally provide promptText for prompt dialogs.

POST/api/tabs/:id/dialog/dismissDismiss pending dialog

Dismiss the pending dialog (Cancel/No).

Network Monitoring

POST/api/tabs/:id/network/enableStart intercepting fetch/XHR requests

Monkey-patches fetch() and XMLHttpRequest to log all requests with URL, method, status, and timing.

GET/api/tabs/:id/network/logGet network request log

Returns logged requests. Use ?clear=true to clear the log after reading.

POST/api/tabs/:id/network/disableStop network monitoring

Clear network log and disable monitoring.

Console Capture

POST/api/tabs/:id/console/enableStart capturing console output

Intercepts console.log, warn, error, and info. Also captures uncaught JS errors.

GET/api/tabs/:id/console/logGet console entries

Returns captured console entries. Filter with ?level=error or clear with ?clear=true.

POST/api/tabs/:id/console/disableStop console capture

Clear console log and stop capturing.

Tracing

POST/api/tracing/startStart recording trace

Start a trace session. Returns a traceId to use with stop.

Body
{ "tabId": "...", "screenshots": true, "network": true }
POST/api/tracing/stopStop trace and get results

Stop the trace and return all recorded actions with timestamps.

Body
{ "traceId": "..." }

WebSocket Events

WS ws://host:port/api/events?apiKey=... Real-time events stream

Connect via WebSocket to receive real-time browser events. API key is passed as a query parameter.

Connection
ws://127.0.0.1:1306/api/events?apiKey=YOUR_KEY
Available Events
EventDescription
tab.createdA new tab was opened
tab.closedA tab was closed
tab.activatedActive tab changed
tab.updatedTab title or URL changed
page.loadingPage started loading
page.loadedPage finished loading
page.progressPage load progress updated
navigation.changedURL changed in a tab
window.openedA new window was opened
window.closedA window was closed
profile.switchedWindow switched to a different profile
download.startedA download began
download.completedA download finished
download.failedA download failed
console.logConsole log captured
console.warnConsole warning captured
console.errorConsole error captured
js.errorUncaught JavaScript error
dialog.openedA dialog (alert/confirm/prompt) appeared
dialog.closedA dialog was accepted or dismissed
network.requestA network request was initiated
network.responseA network response was received
Event Format
{ "event": "tab.created", "data": { "tabId": "550e8400-...", "windowId": "...", "url": "https://example.com" }, "timestamp": "2025-01-15T10:30:00Z" }
JavaScript Example
const ws = new WebSocket('ws://127.0.0.1:1306/api/events?apiKey=YOUR_KEY'); ws.onmessage = (event) => { const msg = JSON.parse(event.data); console.log(msg.event, msg.data); }; ws.onopen = () => console.log('Connected'); ws.onclose = () => console.log('Disconnected');
Python Example
import asyncio, websockets, json async def listen(): uri = "ws://127.0.0.1:1306/api/events?apiKey=YOUR_KEY" async with websockets.connect(uri) as ws: async for message in ws: event = json.loads(message) print(f"{event['event']}: {event['data']}") asyncio.run(listen())

Hidden & Headless Mode

Run Vela without a visible UI on macOS — no windows, no Dock icon, just the full Automation API. Perfect for CI/CD pipelines, automated testing, web scraping, and server-side browser control.

Hidden Mode (Recommended)

Runs a full headed browser with real GUI rendering, but all windows are invisible. Sessions, cookies, and localStorage persist normally. Websites cannot detect hidden mode since it uses the exact same rendering pipeline as headed mode.

open -n -g -a Vela --args --hidden

The -g flag prevents bringing the app to the foreground. Hidden mode defaults to port 1307 (GUI uses 1306) so both APIs run without conflict. Sessions and cookies persist exactly like headed mode.

Headless Mode (Persistent)

Runs without any visible UI — no dock icon, no windows. WebKit is pre-warmed for fast startup. When a profile is used, sessions are fully persistent (cookies, localStorage, etc.) — like Chrome's headless mode. Without a profile, uses non-persistent storage (ephemeral sessions).

open -n -a Vela --args --headless

Or run the binary directly:

/Applications/Vela.app/Contents/MacOS/Vela --headless

Optional Flags

--port <number> Override API port (default: 1307 hidden/headless, 1306 normal)
--bind <address> Override bind address (default: 127.0.0.1). Use 0.0.0.0 for network access.

Stdout Output

When launched, connection details are printed to stdout for easy parsing:

Vela Hidden Mode API: http://127.0.0.1:1307 Key: aBcDeFgHiJkLmNoPqRsTuVwXyZ012345

Example: Hidden Mode Automation

# 1. Launch hidden (alongside GUI with -n -g) open -n -g -a Vela --args --hidden # 2. Create a tab and navigate curl -X POST -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com"}' \ http://127.0.0.1:1307/api/tabs # 3. Wait for page load curl -X POST -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"state":"load"}' \ http://127.0.0.1:1307/api/tabs/TAB_ID/wait-for-load-state # 4. Take a screenshot curl -H "X-API-Key: YOUR_KEY" \ http://127.0.0.1:1307/api/tabs/TAB_ID/screenshot # 5. Get visible text curl -H "X-API-Key: YOUR_KEY" \ http://127.0.0.1:1307/api/tabs/TAB_ID/text

Python Example

import subprocess, requests, time # Launch Vela hidden (use -n -g to run alongside GUI without focus) proc = subprocess.Popen( ["open", "-n", "-g", "-a", "Vela", "--args", "--hidden"], stdout=subprocess.PIPE, text=True ) time.sleep(2) # Wait for server to start API = "http://127.0.0.1:1307" # Hidden/headless defaults to port 1307 KEY = "YOUR_API_KEY" headers = {"X-API-Key": KEY, "Content-Type": "application/json"} # Create tab and navigate tab = requests.post(f"{API}/api/tabs", json={"url": "https://example.com"}, headers=headers).json() # Wait for load requests.post(f"{API}/api/tabs/{tab['id']}/wait-for-load-state", json={"state": "load"}, headers=headers) # Get page text text = requests.get(f"{API}/api/tabs/{tab['id']}/text", headers=headers).json() print(text["text"])

Note: All 175+ API endpoints work identically in hidden, headless, and normal mode. Stop with pkill -f "Vela.*--hidden", pkill -f "Vela.*--headless", or kill <pid>.