Skip to Content
Vulpine API Reference

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.com

Local development:

http://localhost:8080

Authentication

All /v1/* endpoints except /v1/providers require a bearer token:

Authorization: Bearer vk_live_...

The health endpoint is public.

Endpoints

MethodPathAuthPurpose
GET/healthNoService health
GET/v1/providersNoAvailable LLM providers and models
GET/v1/system/metricsYesRequest, error, backlog, worker-health, and artifact-retention metrics, including extraction artifacts
POST/v1/extractYesExtract structured data from one URL
POST/v1/extract/authenticatedYesExtract from a logged-in page using a stored credential reference and expiring saved sessions
GET/v1/auth/sessionsYesList saved authenticated browser sessions
DELETE/v1/auth/sessions/{name}YesDelete a saved authenticated browser session
GET/v1/auth/traces/{id}YesFetch a persisted redacted authenticated login trace
POST/v1/visualYesRun visual extraction and return labeled screenshot metadata
GET/v1/extractions/{id}/resultYesDownload the persisted extraction result payload for a historical run
POST/v1/batchYesQueue batch extraction jobs
GET/v1/audio/capturesYesList recent browser audio capture sessions, including completed or interrupted captures with retained artifacts
POST/v1/audio/capturesYesStart a browser audio capture session
GET/v1/audio/captures/{id}/chunkYesRead a base64-encoded audio chunk from a capture
POST/v1/audio/captures/{id}/stopYesStop a browser audio capture session
GET/v1/audio/captures/{id}/contentYesDownload the retained raw audio artifact for a completed or interrupted capture
POST/v1/audio/captures/{id}/transcription/startYesStart transcription for a capture
POST/v1/audio/captures/{id}/transcription/stopYesStop transcription for a capture
GET/v1/audio/captures/{id}/transcription/eventsYesList transcript events for a capture
GET/v1/audio/captures/{id}/transcription/contentYesDownload merged transcript text or structured transcript JSON
GET/v1/audio/captures/{id}/transcription/streamYesStream live transcript updates over SSE
GET/v1/mobile/devicesYesList Android and iOS devices plus health and registry state visible to the bridge host
GET/v1/mobile/workersYesList hosted mobile workers registered with the control plane
POST/v1/mobile/workers/heartbeatWorker tokenRegister or refresh a hosted mobile worker heartbeat
POST/v1/mobile/workers/{id}/maintenanceYesEnable or clear maintenance mode for a hosted mobile worker
POST/v1/mobile/devices/{platform}/{id}/maintenanceYesEnable or clear maintenance mode for a mobile device
GET/v1/mobile/devices/{platform}/{id}/targetsYesList inspectable targets for a device
GET/v1/mobile/sessionsYesList attached and recently finalized mobile session leases
POST/v1/mobile/sessionsYesAllocate an attached mobile session
DELETE/v1/mobile/sessions/{id}YesRelease an attached mobile session
POST/v1/mobile/sessions/{id}/renewYesRenew an attached mobile session lease
POST/v1/mobile/sessions/{id}/targetsYesOpen a new target on a supported session
POST/v1/mobile/sessions/{id}/recording/startYesStart Android screen recording
POST/v1/mobile/sessions/{id}/recording/stopYesStop Android screen recording
GET/v1/mobile/recordings/{id}/contentYesDownload a completed mobile recording
GET/v1/extractionsYesList extraction history
GET/v1/tasks/{id}YesPoll an async task
POST/v1/tasks/{id}/cancelYesCancel a pending/running task
GET/v1/usageYesCurrent usage and cost metadata
GET/v1/keysYesList API keys and their scopes
POST/v1/keysYesCreate an API key with explicit scopes
POST/v1/monitorYesCreate a recurring extraction monitor
GET/v1/monitorsYesList monitors
DELETE/v1/monitors/{id}YesDelete a monitor

Extract

POST /v1/extract

Use 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/authenticated

Named 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=false

Returns 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/visual

Returns 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/batch

Queues 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/captures

Start browser audio capture:

{ "format": "pcm", "sample_rate": 16000, "channels": 1 }

Read the next chunk:

GET /v1/audio/captures/{id}/chunk?max_bytes=65536

Stop the capture:

POST /v1/audio/captures/{id}/stop

Download the retained artifact after stop:

GET /v1/audio/captures/{id}/content

These 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/transcriptions

Optional query parameters:

  • status=active|completed|failed|stopped|interrupted
  • q=search text

Start transcription for an active capture:

POST /v1/audio/captures/{id}/transcription/start

Read transcript events:

GET /v1/audio/captures/{id}/transcription/events

Download 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=json

Stream live transcript updates with SSE:

GET /v1/audio/captures/{id}/transcription/stream?since=12

Stop transcription:

POST /v1/audio/captures/{id}/transcription/stop

The 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/devices

List hosted workers:

GET /v1/mobile/workers

Allocate 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}/renew

Worker 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}/targets

Start or stop Android recording:

POST /v1/mobile/sessions/{id}/recording/start POST /v1/mobile/sessions/{id}/recording/stop

Download a completed recording:

GET /v1/mobile/recordings/{id}/content

Android 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}/cancel

Task 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/extractions

Returns 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/screenshot

Returns the retained PNG evidence screenshot for that extraction when available.

Download a persisted extraction result:

GET /v1/extractions/{id}/result

Returns 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-image

Returns the retained annotated PNG for that visual extraction when available.

Usage

GET /v1/usage

Returns monthly extraction count, token totals, and estimated cost metadata.

API Keys

List keys:

GET /v1/keys

Create 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/providers

Returns 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/monitors

Delete 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:

StatusMeaning
400Invalid request body or missing required field
401Missing or invalid API key
404Task or monitor not found
429Rate limit or quota exceeded
500Internal 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

Last updated on