Vulpine API Reference
Vulpine API is the hosted commercial API for VulpineOS browser extraction. It uses real browser sessions when available, falls back to HTTP fetches where appropriate, and returns structured JSON with usage metadata.
Base URL:
https://api.vulpineos.comLocal development:
http://localhost:8080Authentication
All /v1/* endpoints except /v1/providers require a bearer token:
Authorization: Bearer vk_live_...The health endpoint is public.
Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /health | No | Service health |
GET | /v1/providers | No | Available LLM providers and models |
GET | /v1/system/metrics | Yes | Request, error, backlog, worker-health, and artifact-retention metrics, including extraction artifacts |
POST | /v1/extract | Yes | Extract structured data from one URL |
POST | /v1/extract/authenticated | Yes | Extract from a logged-in page using a stored credential reference and expiring saved sessions |
GET | /v1/auth/sessions | Yes | List saved authenticated browser sessions |
DELETE | /v1/auth/sessions/{name} | Yes | Delete a saved authenticated browser session |
GET | /v1/auth/traces/{id} | Yes | Fetch a persisted redacted authenticated login trace |
POST | /v1/visual | Yes | Run visual extraction and return labeled screenshot metadata |
GET | /v1/extractions/{id}/result | Yes | Download the persisted extraction result payload for a historical run |
POST | /v1/batch | Yes | Queue batch extraction jobs |
GET | /v1/audio/captures | Yes | List recent browser audio capture sessions, including completed or interrupted captures with retained artifacts |
POST | /v1/audio/captures | Yes | Start a browser audio capture session |
GET | /v1/audio/captures/{id}/chunk | Yes | Read a base64-encoded audio chunk from a capture |
POST | /v1/audio/captures/{id}/stop | Yes | Stop a browser audio capture session |
GET | /v1/audio/captures/{id}/content | Yes | Download the retained raw audio artifact for a completed or interrupted capture |
POST | /v1/audio/captures/{id}/transcription/start | Yes | Start transcription for a capture |
POST | /v1/audio/captures/{id}/transcription/stop | Yes | Stop transcription for a capture |
GET | /v1/audio/captures/{id}/transcription/events | Yes | List transcript events for a capture |
GET | /v1/audio/captures/{id}/transcription/content | Yes | Download merged transcript text or structured transcript JSON |
GET | /v1/audio/captures/{id}/transcription/stream | Yes | Stream live transcript updates over SSE |
GET | /v1/mobile/devices | Yes | List Android and iOS devices plus health and registry state visible to the bridge host |
GET | /v1/mobile/workers | Yes | List hosted mobile workers registered with the control plane |
POST | /v1/mobile/workers/heartbeat | Worker token | Register or refresh a hosted mobile worker heartbeat |
POST | /v1/mobile/workers/{id}/maintenance | Yes | Enable or clear maintenance mode for a hosted mobile worker |
POST | /v1/mobile/devices/{platform}/{id}/maintenance | Yes | Enable or clear maintenance mode for a mobile device |
GET | /v1/mobile/devices/{platform}/{id}/targets | Yes | List inspectable targets for a device |
GET | /v1/mobile/sessions | Yes | List attached and recently finalized mobile session leases |
POST | /v1/mobile/sessions | Yes | Allocate an attached mobile session |
DELETE | /v1/mobile/sessions/{id} | Yes | Release an attached mobile session |
POST | /v1/mobile/sessions/{id}/renew | Yes | Renew an attached mobile session lease |
POST | /v1/mobile/sessions/{id}/targets | Yes | Open a new target on a supported session |
POST | /v1/mobile/sessions/{id}/recording/start | Yes | Start Android screen recording |
POST | /v1/mobile/sessions/{id}/recording/stop | Yes | Stop Android screen recording |
GET | /v1/mobile/recordings/{id}/content | Yes | Download a completed mobile recording |
GET | /v1/extractions | Yes | List extraction history |
GET | /v1/tasks/{id} | Yes | Poll an async task |
POST | /v1/tasks/{id}/cancel | Yes | Cancel a pending/running task |
GET | /v1/usage | Yes | Current usage and cost metadata |
GET | /v1/keys | Yes | List API keys and their scopes |
POST | /v1/keys | Yes | Create an API key with explicit scopes |
POST | /v1/monitor | Yes | Create a recurring extraction monitor |
GET | /v1/monitors | Yes | List monitors |
DELETE | /v1/monitors/{id} | Yes | Delete a monitor |
Extract
POST /v1/extractUse a JSON Schema when you know the target shape:
{
"url": "https://example.com/product/123",
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "number" }
},
"required": ["name", "price"]
}
}Use a prompt when the schema is not fixed:
{
"url": "https://example.com/catalog",
"prompt": "Extract product names, prices, and availability from this page."
}Use async mode for long jobs:
{
"url": "https://example.com/catalog",
"prompt": "Extract all products.",
"async": true
}Use pagination when the target spans multiple pages:
{
"url": "https://example.com/catalog",
"prompt": "Extract product names and prices from every catalog page.",
"options": {
"paginate": true,
"max_pages": 3
}
}Pagination merges repeated list fields across pages, de-dupes overlapping items, and enriches partial duplicates when a later page adds missing fields for the same entity, including sparse same-name records with complementary non-conflicting fields. max_pages lowers the per-request page cap; the service never follows more than five pages total.
Unsupported extraction option keys are rejected with 400 invalid_request instead of being silently ignored.
Optional schema properties normalize to null when a provider omits them or returns blank values, which keeps response shapes stable across reruns and provider variants.
Set options.model when you want a specific extraction model for that request instead of the server default.
Set options.timeout in seconds when you want to lower the per-request execution ceiling beneath the service default.
Set options.waitFor to a CSS selector, load, or networkIdle when you need browser-backed extraction to wait for a specific page condition before reading the DOM.
Set options.proxy to an HTTP proxy URL when you want HTTP-fallback extraction to fetch through a specific proxy for that request. options.proxy is not used on browser-backed extraction.
Unknown extraction option keys are rejected with 400 invalid_request instead of being ignored.
Incompatible option combinations are also rejected with 400 invalid_request: options.waitFor requires browser-backed extraction, and options.proxy requires HTTP fallback extraction.
Response shape:
{
"status": "completed",
"data": { "name": "Example Product", "price": 19.99 },
"metadata": {
"completeness": 1,
"tokens": 1240,
"cost": 0.0021,
"duration_ms": 3810,
"pages": 1
},
"free_tier_remaining": 99
}Partial responses keep the same shape with status: "partial" and metadata explaining missing fields or retry behavior.
Write endpoints use strict JSON decoding. Unknown fields or trailing JSON content return 400 invalid_request.
Authenticated Extract
POST /v1/extract/authenticatedNamed sessions are tenant-scoped. Stale saved sessions expire automatically based on the server-side AUTH_SESSION_TTL setting, authenticated extraction metadata includes whether a stale session was discarded before re-login, and successful responses include metadata.login_trace_id for later diagnosis.
Saved Auth Sessions
GET /v1/auth/sessions?q=primary&expired=falseReturns saved browser-session summaries with age, expiry state, and cookie counts. Raw cookie values are never returned.
DELETE /v1/auth/sessions/{name}Authenticated failures return error.trace_id. Both success and failure trace IDs can be retrieved through:
GET /v1/auth/traces/{id}Use this when the target is behind login and the tenant has a stored credential reference.
{
"url": "https://example.com/account",
"credential_ref": "cred_123",
"prompt": "Extract the current account balance and available credit.",
"session_name": "billing-account"
}This endpoint requires a paid plan plus private vault/browser wiring.
Mobile session leases are persisted. Session responses may include expires_at, released_at, and updated_at. After an API restart, previously attached worker-local sessions are reconciled to expired so stale loopback endpoints are not shown as live sessions.
Device inventory responses may also include health_status, failure_streak, failed_uses, and draining. Repeated attach or disconnect failures move a device into draining, and the session allocator will reject new attaches against draining devices.
Session allocation accepts either an explicit device_id or platform plus optional capabilities and sticky_key. Use sticky_key when repeated jobs should prefer the same healthy device without exposing a hard-coded device identifier to every caller.
Hosted mobile workers can publish device heartbeats with POST /v1/mobile/workers/heartbeat using the internal X-Vulpine-Worker-Token header. Inventory rows now also expose worker_id, worker_host, worker_addr, worker_healthy, last_seen_at, and registry_state so remote or stale devices can be identified without attempting a local attach.
Maintenance is explicit. POST /v1/mobile/workers/{id}/maintenance and POST /v1/mobile/devices/{platform}/{id}/maintenance persist manual maintenance flags, surface them through the inventory and worker responses, and remove that capacity from allocator placement until it is cleared.
Visual Extract
POST /v1/visualReturns labeled screenshot data plus structured visual element metadata for browser-backed extraction flows.
{
"url": "https://example.com/pricing",
"instruction": "Label the pricing cards and primary CTA.",
"include_screenshot": true
}This endpoint requires a paid plan and browser integration.
Batch
POST /v1/batchQueues up to 100 URLs with the same schema or prompt. Batch jobs are always asynchronous.
{
"urls": [
"https://example.com/a",
"https://example.com/b"
],
"schema": {
"type": "object",
"properties": {
"title": { "type": "string" }
}
}
}Response:
{
"tasks": [
{ "id": "task_1", "poll": "/v1/tasks/task_1" },
{ "id": "task_2", "poll": "/v1/tasks/task_2" }
]
}Audio Capture
POST /v1/audio/capturesStart browser audio capture:
{
"format": "pcm",
"sample_rate": 16000,
"channels": 1
}Read the next chunk:
GET /v1/audio/captures/{id}/chunk?max_bytes=65536Stop the capture:
POST /v1/audio/captures/{id}/stopDownload the retained artifact after stop:
GET /v1/audio/captures/{id}/contentThese endpoints require a paid plan and private AudioBridge wiring. Audio capture metadata is persisted so completed or interrupted artifacts remain listable and downloadable after API restarts until retention cleanup removes them.
Audio Transcription
List persisted transcription sessions:
GET /v1/audio/transcriptionsOptional query parameters:
status=active|completed|failed|stopped|interruptedq=search text
Start transcription for an active capture:
POST /v1/audio/captures/{id}/transcription/startRead transcript events:
GET /v1/audio/captures/{id}/transcription/eventsDownload merged transcript text or the structured session-plus-events export:
GET /v1/audio/captures/{id}/transcription/content?format=text
GET /v1/audio/captures/{id}/transcription/content?format=jsonStream live transcript updates with SSE:
GET /v1/audio/captures/{id}/transcription/stream?since=12Stop transcription:
POST /v1/audio/captures/{id}/transcription/stopThe transcription surface depends on a configured provider and returns transcription_unavailable if no provider is attached.
Completed or interrupted transcript event history remains queryable after API restarts, and the session list includes merged transcript preview text for quick scanning. The export endpoint returns either plain text for downstream captioning/search pipelines or the structured JSON envelope used by the dashboard and SDKs.
Mobile Devices and Sessions
List visible devices:
GET /v1/mobile/devicesList hosted workers:
GET /v1/mobile/workersAllocate a session:
POST /v1/mobile/sessions{
"platform": "android",
"device_id": "emulator-5554"
}The allocator can return local Android sessions with transport: "cdp_http",
hosted Android worker sessions with transport: "worker_control", or iOS
sessions with transport: "webkit_inspector". Target creation is available
on supported Android and iOS sessions. Android screen recording is available
for both local cdp_http sessions and hosted worker_control sessions.
Hosted worker registry data is reconciled before listing or allocation.
Workers that stop heartbeating move to stale state, attached
worker_control sessions on those devices are expired, and very old worker
rows are pruned from the registry entirely.
Long-lived CDP clients can keep a lease alive explicitly with:
POST /v1/mobile/sessions/{id}/renewWorker heartbeats can also report active_sessions, queue_depth,
max_sessions, failure_rate, and last_error. The control plane
surfaces those fields through GET /v1/mobile/workers and avoids
allocating new leases against saturated workers.
Open a new target on a supported session:
POST /v1/mobile/sessions/{id}/targetsStart or stop Android recording:
POST /v1/mobile/sessions/{id}/recording/start
POST /v1/mobile/sessions/{id}/recording/stopDownload a completed recording:
GET /v1/mobile/recordings/{id}/contentAndroid sessions expose CDP-backed actions today, including hosted worker sessions that can be attached remotely through the control plane. iOS sessions expose inventory and attached session flows, with capability difference reflected in responses.
Completed recording artifacts are retained for a limited window. The API cleans local artifacts when the owning session is released, prunes older completed recordings automatically, and deletes hosted worker temporary files after download.
Tasks
Poll an async task:
GET /v1/tasks/{id}Cancel a pending or running task:
POST /v1/tasks/{id}/cancelTask response:
{
"id": "task_123",
"status": "completed",
"data": { "title": "Example" },
"metadata": { "completeness": 1 }
}Statuses are pending, running, validating, completed, partial, or failed.
Extraction History
GET /v1/extractionsReturns persisted extraction history for the authenticated tenant across sync extraction, authenticated extraction, visual extraction, and async work. Supports status, monitor_id, failure_family, retry_class, and since_hours query parameters.
Failed rows include both the raw error string and a normalized failure object with family, retry_class, retryable, and operator_hint fields so operators can distinguish immediate retries from configuration or quota issues.
When async retry is active, the same responses also include attempts, async_retries, and last_retry_reason fields so tenant tooling can see whether a task has been retried and why.
History rows also include mode so operators can distinguish standard extract, authenticated, and visual runs. Completed rows include duration_ms, sync retries, and cached so dashboards can separate cache hits from fresh runs and quickly spot slow or noisy pages. Authenticated rows additionally expose login_trace_id when a persisted redacted login trace exists. When browser-backed evidence capture is retained, rows also expose evidence_screenshot_path with a tenant-scoped download route. Visual extraction rows can also retain visual_image_path for the annotated PNG generated by POST /v1/visual.
Download a retained screenshot artifact:
GET /v1/extractions/{id}/artifacts/screenshotReturns the retained PNG evidence screenshot for that extraction when available.
Download a persisted extraction result:
GET /v1/extractions/{id}/resultReturns the stored result payload for a completed or partial extraction run as JSON.
Download a retained visual extraction image:
GET /v1/extractions/{id}/artifacts/visual-imageReturns the retained annotated PNG for that visual extraction when available.
Usage
GET /v1/usageReturns monthly extraction count, token totals, and estimated cost metadata.
API Keys
List keys:
GET /v1/keysCreate a key:
POST /v1/keys{
"name": "Production worker",
"scopes": ["read", "write"]
}Key creation returns the secret once. Store it immediately. Supported scopes are admin, write, read, mobile, and audio. Existing legacy keys continue to behave as full-access keys until rotated or replaced.
Providers
GET /v1/providersReturns available model providers and supported model identifiers.
Monitors
Create a recurring extraction monitor:
POST /v1/monitor{
"url": "https://example.com/pricing",
"prompt": "Extract the current plan prices.",
"interval_seconds": 3600,
"max_runs": 24,
"webhook_url": "https://example.com/webhooks/vulpine"
}List monitors:
GET /v1/monitorsDelete a monitor:
DELETE /v1/monitors/{id}Current monitor scheduling enqueues recurring extraction jobs. Change detection, condition evaluation, and richer webhook change payloads are tracked separately in the paid API backlog.
Errors
Errors return JSON:
{
"error": "missing url"
}Common status codes:
| Status | Meaning |
|---|---|
400 | Invalid request body or missing required field |
401 | Missing or invalid API key |
404 | Task or monitor not found |
429 | Rate limit or quota exceeded |
500 | Internal execution error |
SDKs
TypeScript:
import { Vulpine } from '@vulpineos/sdk'
const client = new Vulpine({ apiKey: process.env.VULPINE_API_KEY })
const result = await client.extract({
url: 'https://example.com/catalog',
prompt: 'Extract product names and prices',
options: { paginate: true, max_pages: 3 }
})Python:
from vulpineos import Vulpine, ExtractRequest
client = Vulpine(api_key=os.environ["VULPINE_API_KEY"])
result = client.extract(ExtractRequest(
url="https://example.com/catalog",
prompt="Extract product names and prices",
options={"paginate": True, "max_pages": 3},
))See also
- Vulpine API Features — product surface and planned endpoint families
- Source Availability — open-source and source-closed boundaries
- MCP Browser Tools — local agent/browser control tools