Skip to content

Antidetect browser platform

Run thousands of browser profiles.Each one its own device.

Every profile carries its own canvas, WebGL, audio and font signature, its own proxy, and its own isolated storage. Drive them with the automation you already have. Browsers run on your machines, and you can self-host the entire fleet.

Pick a plan and start today, or talk to us about enterprise from $5,000 once the fleet itself is the job.

What you already usePuppeteerPlaywrightSeleniumREST API and SDKOne endpointCDP WebSocketleast connectionsWhere browsers runYour machineisolated profilesYour machineisolated profilesYour machineisolated profilesAdd anotherone URL, whatever your toolingconcurrency scales with the machinesyou add
Fig. 01One endpoint, whatever your tooling. Sessions are handed to the machine holding the fewest open connections, so adding a host adds concurrency.
Device targets
Windows · macOS · Linux · Android
Browser
Chrome 135 to 150
Per-profile proxy
HTTP · HTTPS · SOCKS5 · SOCKS5H
Drive it with
Puppeteer · Playwright · Selenium · REST · SDK · CLI

01First profile

Getting started

Four steps to a running profile

Minutes, one container and one credential. The work that usually goes into making a profile believable is already done inside the browser build.
  • 01

    Get your organisation key

    One value, issued with your organisation whichever way you buy. It is the only thing the installer asks for and the only credential the platform needs to identify you.
  • 02

    Describe the device you want

    An operating system target, a Chrome major between 135 and 150, an optional Android model, and a proxy. The platform matches a browser build to that description and pins it to the profile, so it presents the same device on every launch.
  • 03

    Run a worker on a machine you already have

    Browsers execute on your hardware, not ours. One container on one host is enough to start; the same worker image is what a fleet is made of later.
  • 04

    Launch

    Open the profile from the dashboard for a window you can use, or launch it over the API and get back a CDP endpoint for Puppeteer, Playwright or Selenium.

What you do not do

  • No fingerprint values to assemble by hand
  • No timezone, locale or language to keep in sync
  • No per-page script to re-apply
  • No infrastructure to design before the first profile runs

The whole path is public documentation, so you can read exactly what a deployment involves before anyone asks you for a decision.

02Capabilities

What you get

Around 45 signals, already agreeing with each other

A profile is a device and a route: a browser build that fixes canvas, WebGL, audio and fonts, plus a proxy that decides where it appears to be. Here are six of them, and what happens when one launches.

Profilesselect one

Launching ads-us-01

  1. 01RequestPOST /launch · { "profileId": "ads-us-01" }One call, from your code or the dashboard.
  2. 02Bundle pinnedWindows 11 · Chrome 148Canvas, WebGL, WebGPU, audio and fonts come from this build, identically on every launch.
  3. 03Route attachedsocks5h://us-res-04:1080This profile's proxy alone, and DNS resolved at the exit.
  4. 04Region derivedAmerica/New_York · en-USRead from the proxy exit at launch, along with the language list and geolocation.
  5. 05Endpoint returnedws://127.0.0.1:38923/devtools/browser/<id>Attach Puppeteer, Playwright or Selenium, or drive it from the dashboard window.
Fig. 02Six rows from one organisation's profile list, with the launch each one performs. Select a profile to follow it.
Overrides injected at runtimeA detector reads a propertypage-level overridesre-applied per document and frametwo sources of truthStock Chromium underneathengine values unchangedan unpatched surface answers with the engine valueProperties in the buildA detector reads a propertyOne sourceBrowser build and profilecanvas · WebGL · WebGPU · audiofonts · client hints · device metricsnothing to re-apply, nothing to miss
Fig. 03A value that lives in the build has nothing to contradict. A value injected per page has an unpatched engine underneath it.

The device

Rendering signatures
Canvas, WebGL, WebGPU, audio, client-rect and text-rect readings come from the build. The reported GPU and the pixels it produces agree, because they are the same device.
Fonts across systems
Embedded platform font engines for Windows, macOS, Linux and Android. A macOS profile enumerates macOS fonts while running on a Linux worker, and the host's own fonts stay out of it.
Client hints and device metrics
UA-CH headers, device memory, pixel ratio, hardware concurrency and media devices match their JavaScript counterparts, so header and script readings cannot be played against each other.
Android device profiles
A first-class target, not a preview. Real device models, touch simulation, mobile metrics, orientation and a mobile keyboard viewport.
Stable, not random
One profile is one device for as long as it exists. Nothing is recomputed per launch, which is what stops an identity drifting into a cluster of near-identical ones.

The network

A proxy per profile
HTTP, HTTPS, SOCKS5 and SOCKS5H, assigned per profile rather than per machine, so two identities never share an exit address by accident.
Remote DNS over socks5hOurs alone
With socks5h the hostname is resolved at the exit, so your resolver never sees the lookup. No other antidetect platform ships it, and the comparison page cites the page and date behind that.
Region follows the exit IP
Timezone, locale, language list and geolocation are read from the proxy at launch. Standard across the category; the point is that it is correct here without anyone remembering to update it.
WebRTC leak prevention
On by default. The candidate set reflects the proxy route rather than the host, and it is a coherent set rather than an empty one, because no candidates at all is its own tell.
QUIC and STUN tunnelled
Both run over UDP, which does not travel through an HTTP proxy. They are tunnelled over SOCKS5 so they cannot carry the host's real position out around the session.
SourceProxy exit addressHTTP · HTTPS · SOCKS5 · SOCKS5HTimezoneLocaleLanguagesGeolocationno manual entryWebRTC leak preventionon by default, decided independently of the proxy
Fig. 04Set the proxy and four signals follow it. WebRTC prevention is decided independently, so it holds whatever the proxy type.

Automation

The tools you have
Puppeteer, Playwright and Selenium connect over CDP. Every launch returns a debugger endpoint; nothing needs a bespoke driver.
REST API and typed SDK
Provision profiles, launch sessions and read usage over HTTP, with a per-plan request rate you can see before you buy.
Command line
Drive profiles and sessions from a terminal or a CI job without opening the dashboard.

Teams and fleet

Roles
Owner, admin and member, enforced on the API and not just in the interface. Members are invited by email against a plan limit.
Bulk operations
Delete profiles in bulk, and copy one into a new identity with a single call. Creation is one profile per request, so a hundred of them is a loop rather than one call.
Cluster mode
One WebSocket endpoint in front of every worker. Automation connects to a single URL and the fleet decides which machine serves it.

Every capability above is documented in the same detail it is described in here, so it can be checked against your own requirements before a conversation starts.

03Scale

Concurrency

One URL in front of every machine you own

Automation points at a single endpoint. Sessions land on the worker holding the fewest open connections, so a long-running job does not pull more work onto a busy machine, and capacity grows by adding hosts rather than by rewriting anything.
First connectionAutomationpuppeteer.connectone endpointEdge route/launchupstream cluster_workersleast_connproxy_next_upstream error timeout http_502 http_503proxy_next_upstream_tries 3veil-worker-01open connections 12veil-worker-02open connections 3selectedveil-worker-03open connections 9Reconnects and session actionsSame client/w/veil-worker-02/stable per-worker pathveil-worker-02
Fig. 05Connection counts are an example. Selection is by open connections, and up to three machines are tried before a launch is reported as failed.

Routes the fleet answers

/launch
A new session, placed on the least busy machine
/launch/:referenceId
A new session against a stored profile
/browsers/:id
Attach to a session already running
/w/<worker-id>/
Anything you want pinned to one machine

Shipped defaults

Distribution
least connections, not round robin
Upstream retries
3 candidates before failing
Endpoints to configure
1
To add concurrency
add a host

How many profiles run at once is bounded by the machines you point at the endpoint, and by your plan, or by the volume an enterprise deployment is sized for.

The size of the fleet is one of the things that sets the price, so it is worth knowing the number before the pricing section.

04Pricing

What it costs

Plans start with a card. Enterprise starts at $5,000.

Two ways to buy. Most account work runs on a published plan and never needs a conversation. Enterprise starts where the profile count, the fleet behind it, the seats around it or the deployment itself becomes somebody's job.

Where you start

Plans

Sign up, choose a plan, pay by card, run profiles the same day. Profiles, seats and API rate are set by the plan, and the amounts on the pricing page are read from the live plan records rather than retyped into a marketing table.

See the plans

Starting at

$5,000

Enterprise

Fleet size, seats and the support around them set the rest. No billing period is stated on the number, because what you are deploying decides it.

You talk to someone first, and a demo is arranged with sales. Neither plans nor enterprise carry a free trial.

What enterprise includes

The platform, on your machines
Browser builds, profile bundles, workers and the fleet manager, deployed on infrastructure you provision.
Profiles at the volume you need
Sized to the operation rather than metered into a plan. Concurrency follows the machines you run.
Team access and roles
Seats, role separation enforced on the API, and audited operations for every change to the fleet.
Engineers on the other end
The people who built the thing you are deploying, during deployment and afterwards.

What decides the number

  • How many profiles you hold, and how many run at once
  • How many machines the fleet spans, and who provisions them
  • How many seats need rights separated from each other
  • API volume
  • Whether we deploy alongside you or hand over the documentation

Bring the fleet you intend to run and the platforms you intend to run it against, and the first call ends with a number and a demo rather than a brochure. If the fleet is not the job yet, a plan needs no call at all.

05Self-hosting

The part nobody else offers

Or run the entire platform yourself

Every other antidetect vendor runs the browsers for you, each one checked against their own documentation on a stated date, which is why none of them can hand you the software that operates them. One command installs the edge, the API and the fleet manager on a host you already have, and a five-step wizard finishes it.
Install
$ curl -fsSL https://veilbrowser.net/install.sh | VEIL_KEY=<org-key> bash

One value to supply. The installer derives the docker group, the internal key and an auth secret.

Then five steps, in this order

  1. 01An admin account, with no default password to rotate later
  2. 02The cluster name and the hostnames the edge will answer for
  3. 03HTTPS from Let's Encrypt, a Cloudflare origin certificate, or your own key pair
  4. 04The first worker, joined to the cluster network
  5. 05Review, and the config is rendered and stamped

Before the last step

The origin lock stays off and port 80 serves the wizard until setup is stamped complete, so a misconfigured lock cannot shut you out of your own installation.

What stays yours, and what does not

Browsers, sessions and egress run on your hardware. Your organisation, plans, profile metadata and licence checks live in ours. That is the whole split, and it is drawn rather than described because a security review will ask for the drawing anyway.

Your infrastructureVeilBrowser cloudtrust boundaryFleet managerEdge and API:443 · 127.0.0.1:4180WorkersBrowsers execute hereveil-worker-<host> · :38923EgressYour proxiesHTTP · HTTPS · SOCKS5 · SOCKS5Htarget sitesAccountsPlansProfile metadataLicence verification123
  1. 1Licence and plan verificationoutbound
  2. 2Encrypted profile bundlesinbound
  3. 3Browser build downloadsinbound
Fig. 06Three connections cross the boundary. Browser traffic is not one of them; it leaves through the proxies you configure.
Containerveil-cluster-managerWorkersAutomationPuppeteer, PlaywrightCDP over WebSocketOperatorsManager interfaceTLS, session authwss /launchhttpsEdge, OpenRestyTLS, routing, Lua:443 · :80Fleet manager, BunCluster, fleet, operations127.0.0.1:4180loopbackveil-cm-updatercompose profile: updater, off by defaultproxyveil-worker-01:38923 · /launch · /healthveil-worker-02:38923 · /launch · /healthveil-worker-03:38923 · /launch · /healthFurther workers, same shapeGET /fleet/status · every 30 sVeilBrowser cloudAccounts, plans, licencereached outbound only
Fig. 07The shipping topology, with the ports each process binds. The self-update sidecar is dashed because the default installer does not enable it.

The self-hosting documentation goes further than these drawings do, and the installer above is the one it describes.

06Operations

At three in the morning

What the fleet does when nobody is watching

Five behaviours a review will ask about, each with the shipped interval, timeout or retry count that governs it. Open a row for the drawing; the claim is on the row either way.
01A dead host and a dead API are not the same incidentEvery host is polled every 30 seconds. A failed status request opens a plain TCP connection before deciding what is wrong, and hosts you stopped on purpose are skipped rather than reported.
Schedulerfleet-poll*/30 * * * * *Probe 1GET /fleet/statusx-api-key · timeout 5 s200, body parsedtimeout or errorProbe 2TCP connectprobe port · timeout 2 sconnection acceptedrefusedSnapshotreachablemetrics storedSnapshotapi-deadhost up, API silentSnapshothost-downno TCP at all30 s30 s30 severy host, every tick

Recorded

reachable

reachable: true · activeSessions, load and version stored

The status body is kept as the snapshot, so the fleet view is never older than 30 seconds.

Fig. 08Select a case to follow the path the poller takes.
02A rejected config never reaches trafficLoad balancer changes are rendered from the database, tested by the same binary that will serve them, and moved into place by one atomic rename. A validation failure ends the operation with the previous file still serving.
Candidate configStep 1Renderfrom databaseStep 2Difflive vs nextStep 3Validateopenresty -tStep 4Swapatomic renameStep 5Reloadsignal, no restartnginx.conf.stagingcut-overone rename() callAnswering requestsprevious config, still servingnew configno interval in which nothing is serving
operation
kind: "render-config"
status
succeeded
authority
admin only
Fig. 09Both outcomes against the same serving lane. The lower lane never has a gap.
03Workers drain before they rollAn update leaves the launch pool first, waits up to 120 seconds for the sessions already running, and does not receive traffic again until it reports healthy.

Operation steps

step ids as recorded: drain, sessions, pull, replace, address, health, re-enable, refresh

Launch poolworker-01worker-02held outworker-03Live sessions on worker-024 still runningdrain budget 120 sRunning imagepreviousnewHealthhealthy

01 Drain trafficThe worker is taken out of the launch pool first, so no new session can land on a machine that is about to be replaced.

Fig. 10Select a step to read the worker's state at that point. Step names are the ones written to the operation record.
04The origin does not explain itself to scannersRequests that skip the proxy are closed with no response at all, and a handshake for a hostname the cluster does not serve gets a certificate with nothing to do with your domain.
Case 1Arrives via Cloudflaresource inside published rangesEdge decision$cf_ok = 1CF-Connecting-IP trustedResultServedreal client IP restoredCase 2Straight to the IPany other source addressEdge decision$cf_ok = 0return 444Result444, closedno body, no redirect, no bannerCase 3Unknown hostnameTLS with unmatched SNIEdge decisiondefault_serverdecoy certificateResultDecoy, then 444scanners index the decoyalso availablessl_verify_client onmutual TLS between the proxy and the originresty.acme autosslLet's Encrypt issued and renewed in process
Fig. 11HTTP 444 closes the connection without answering, which is a different signal to a scanner than a 403 page.
05Every mutating action is a record you can readApplying a config, rolling a worker, issuing a certificate: each writes a record before it starts and streams its log while it runs. Roles are enforced on the API, not only in the interface.
GET /api/operations/:id/log/streamtext/event-stream
  1. 15:04:02steprender started
  2. 15:04:02log1 server block, 3 upstream servers
  3. 15:04:02steprender ok
  4. 15:04:02stepvalidate started
  5. 15:04:03logconfiguration file test is successful
  6. 15:04:03stepvalidate ok
  7. 15:04:03stepswap started
  8. 15:04:03lognginx.conf.staging renamed onto nginx.conf
  9. 15:04:03stepswap ok
  10. 15:04:04stepreload started
  11. 15:04:04logreload signal accepted
  12. 15:04:04statussucceeded

Example stream from a config apply. Four event types are sent: log, step, progress and status.

Capabilities available to the admin, operator and viewer roles
Capabilityadminoperatorviewer
Read the fleet, operations and configurationAllowedAllowedAllowed
Change cluster and fleet settingsAllowedAllowedNot allowed
Stop, start and update workersAllowedAllowedNot allowed
Preview a rendered configAllowedAllowedNot allowed
Apply a rendered configAllowedNot allowedNot allowed
Manage users and TLSAllowedNot allowedNot allowed

If a security questionnaire is waiting on the other side of this decision, the answers are in the documentation rather than in a call.

07Limits

Not finished

Two things a review will find

They are here, in the same typeface as everything else, because the alternative is you finding them in week three of an evaluation.
  • Not yet

    Automated remote worker provisioning

    The interface does not provision workers on other machines for you. Workers on the manager's own host are created and replaced for you. A worker on a separate machine is started there first and then registered, which means adding capacity to a second host is still a task with a human in it.
  • Off by default

    The self-update sidecar

    The component that updates the manager itself ships as a compose profile that the default installer does not turn on. Until you enable it, the manager will not replace its own container, and updating it is something you do rather than something that happens.

Both are operational rather than structural, and both are on the roadmap we will walk you through rather than discover together later.

Bring us the fleet you want to run.

How many profiles, on how many machines, against which platforms. The first call answers the commercial question and shows you the thing running, and nothing on this page needs a call to verify.

Entry
Plans, or enterprise from $5,000
To evaluate
Sign up on a plan, or arrange a demo with sales
To read first
The installer and every default, documented publicly

Every interval, limit and exclusion on this page is the shipped one.