Automating Antidetect Browsers: Local APIs, Playwright Bindings and Profile Ops

Every one of the seven major antidetect browsers binds an HTTP server to localhost and will start a profile for you over it. Kameleo listens on 5050, AdsPower on 50325, Octo Browser on 58888. Almost none of the review coverage for antidetect browsers mentions this, because automation gets a checkmark in a comparison row next to the fingerprint matrix and the pricing table. So teams click through a GUI to launch forty profiles one at a time, work a twenty-line script does better.
This guide covers the engineering side of the category: how the local control API pattern works, what the start call returns on each vendor, how to attach Playwright or Puppeteer to the result, and the operational layer nobody writes about. Profiles are state, so they need versioning and a backup path. Concurrency turns out to be a purchasing decision. And a proxy has to be bound to one profile for any of it to hold together.
Scope note: this is about data collection and testing. Running sessions on sites you are authorized to access, verifying how your own product renders across device profiles, collecting public pages that block generic headless traffic. The account-farming use case that dominates this category's marketing is not what this page is for.
The local control API pattern
Every one of these products is the same architecture with different naming. A desktop application or daemon runs on your machine. It holds a library of profiles, each of which is a fingerprint definition plus a browser data directory. It binds an HTTP server to localhost. You POST or GET a start request naming a profile, the daemon launches its patched Chromium or Firefox build with remote debugging enabled, and the response contains the address of that debugging interface.
From that point you are talking to an ordinary CDP endpoint. Playwright and Puppeteer do not know or care that the browser on the other end has a spoofed canvas hash.
The three steps, in every SDK and every language:
# 1. start the profile through the vendor's local API
resp = requests.post(f"{LOCAL_API}/start", json={"profile_id": PROFILE_ID})
ws = resp.json()["ws"]
# 2. attach
browser = playwright.chromium.connect_over_cdp(ws)
# 3. take the EXISTING context, do not create a new one
context = browser.contexts[0]
page = context.pages[0] if context.pages else context.new_page()
Step three is where most first attempts go wrong. browser.new_context() creates a fresh incognito-equivalent context with no cookies, no local storage and none of the profile's accumulated state. You will have started an antidetect profile and then thrown away the half of it that mattered. Always take contexts[0].
There is a second and less obvious property of attaching rather than launching. Playwright sets navigator.webdriver and passes --enable-automation when it starts a browser itself. connectOverCDP attaches to a process Playwright did not start, so neither is applied. That is a mechanical difference you can verify yourself in about a minute, and it is part of why this pattern survives checks that a stock headless Playwright run fails.
The cleanup half matters as much. Wrap the work in try/finally and call both browser.close() and the vendor's stop endpoint. An orphaned profile process holds a licence slot, keeps a proxy session open, and on some vendors leaves the profile flagged as running so the next start call fails. Long batch jobs that skip this drift into a state where half the licence is consumed by zombies.
What each vendor returns
The shape is constant. The details are not. The AdsPower row, and the plan figures for Kameleo and Multilogin further down, were read from those vendors' own documentation and pricing pages on 27 July 2026. The Octo Browser, GoLogin, Dolphin Anty and Incogniton rows come from each vendor's published developer documentation but were not re-checked on that date. Ports and paths change: read them from the current docs and your local config rather than from this table.
| Tool | Local API surface | What start returns | Notable constraint |
|---|---|---|---|
| Kameleo | REST on port 5050, plus first-party Python, JavaScript and C# SDKs | ws://localhost:5050/playwright/{profileId} for Playwright; separate paths for Puppeteer and Selenium | Two engines. Chroma attaches over CDP; the Firefox-based Junglefox goes through a pw-bridge shim instead |
| Multilogin | Launcher API on port 35000 | GET /api/v1/profile/start?automation=playwright&profileId=… returns a port; build http://127.0.0.1:{port} | automation must be set to selenium, puppeteer or playwright or no port is returned at all |
| AdsPower | REST on port 50325 by default | POST /api/v2/browser-profile/start returns data.ws.puppeteer, data.ws.selenium, data.debug_port and a data.webdriver path to a matching chromedriver | Documented as a paid-account feature; request rate is tied to stored profile count, 2/sec under 200 profiles, 5/sec to 5,000, 10/sec above |
| Octo Browser | Local client API on port 58888 | Pass "debug_port": true and the response carries ws_endpoint | One-time profiles are created, launched and auto-deleted in a single call, which suits stateless collection |
| GoLogin | Local API plus a cloud REST API and SDKs | SDK start() / launch() returns a browserWSEndpoint for Puppeteer | exit() stops the browser and uploads profile state to GoLogin storage, so state leaves your machine by default |
| Dolphin Anty | Desktop app API on port 3001 | Automation launch returns a remote debugging port, with a headless flag | Third-party reviews from mid-2026 place the Local API on paid plans only; confirm against the current plan page |
| Incogniton | Local API fronted by Python and Node SDKs | SDK startPlaywright(); REST /automation/launch/puppeteer/{profile_id} | The browser-automation module is documented as SDK-exclusive, not reachable through raw REST |
Two things to take from that table before anything else.
Do not hardcode the port. AdsPower's API overview, read on 27 July 2026, gives http://local.adspower.net:50325/ as the default address and then tells you to read a local_api file for the authoritative one: under AppData\Local\Adspower on Windows, ~/Library/Application Support/Adspower/local_api on macOS, ~/.config/adspower_global/cwd_global/source/local_api on Linux. Read the port from that file or from your own config at startup. A hardcoded port is the failure that surfaces six months later on one machine in the fleet.
Do not assume Firefox behaves like Chromium. Kameleo is the only vendor here shipping a Firefox-based engine alongside its Chromium one, and Junglefox needs Playwright's bridge rather than a CDP connection. If your target detection meaningfully differs between engines, that is a reason to pick Kameleo. If it does not, it is a second code path you did not need.
Profile creation and fingerprint assignment
The launch call is nearly identical everywhere. Profile creation is where the products actually diverge, and it is the part that determines whether your fleet is reproducible.
Kameleo's model is the most explicit about it. You call searchFingerprints with a device type, browser and OS, get back a list of real fingerprint records, and pass one of their ids into createProfile. The fingerprint is a first-class object you selected, so you can record which one a profile got and recreate it. Kameleo's pricing documentation also notes that SearchFingerprints, CreateProfile and StartProfile all count against your per-minute request budget, which is worth knowing before you write a loop that creates two hundred profiles at once.
GoLogin, AdsPower, Multilogin, Octo Browser and Dolphin Anty all expose profile creation through REST as well, with fingerprint parameters supplied as fields on the create call and unspecified values filled in by the vendor's generator. That is more convenient and less reproducible. If the generator picks your WebGL vendor string and you never record what it picked, a profile you delete is gone rather than rebuildable.
The practical rule: whatever the vendor's create call returns, persist the full profile object in your own store immediately. Not the id. The whole thing.
Incogniton is the odd one here for a different reason. Its browser-automation surface is documented as SDK-exclusive, which is fine if you write Python or Node and awkward if your orchestration lives in Go or Rust and speaks HTTP to everything else. It is wrong for a polyglot platform team for that reason alone, regardless of how the browser itself performs.
Treat profiles as infrastructure, not artefacts
The default workflow in this category is manual. Someone opens the desktop app, clicks new profile, picks a fingerprint from a dropdown, pastes in a proxy, names it something, and the organization now owns an artefact nobody can rebuild. Forty of those and you have a fleet with no source of truth.
The alternative is to split a profile into two things that get managed differently.
The definition is deterministic and belongs in version control. Fingerprint selection or explicit parameters, the proxy binding, engine choice, start flags, headless or not, and whatever tags your vendor supports. Store it as JSON or YAML in the same repo as your scrapers. A reconciler script reads the directory, queries the vendor's list endpoint, creates what is missing and updates what has drifted. The desktop GUI becomes a read-only inspection tool.
The state is cookies, local storage, IndexedDB, service worker caches and the profile's browsing history. This cannot be versioned meaningfully and cannot be regenerated. A session cookie that has survived three weeks of normal use carries trust that a fresh login does not, and no amount of configuration recreates it.
Once you have that split, a profile change becomes reviewable in a pull request and a bad fingerprint edit becomes a revert. Onboarding a new machine turns into a reconcile run instead of an afternoon of clicking. You can also answer the question that always comes up after an incident, which is what changed about this profile and when.
This is also the point where naming matters more than it seems. Encode the purpose, the target and the proxy region into the profile name, because the vendor's API returns names in list responses and you will be filtering on them constantly.
Binding a proxy per profile
A fingerprint that stays constant while the exit IP changes every session is a worse signal than no fingerprint spoofing at all. Real users do not move continents between page loads. This is the single most common way a well-configured antidetect fleet gets itself flagged, and it comes from treating the proxy layer as a shared pool rather than a per-profile attribute.
Every vendor in the table supports per-profile proxy configuration, either on the create call or as a separate proxy endpoint. GoLogin additionally resells its own IPs through an addGologinProxyToProfile helper that takes a country code, which is convenient and locks that binding to their pool. Most teams bring their own from a proxy network instead.
The rules that hold across vendors:
- One profile, one sticky exit IP, for the life of the profile. [Sticky residential proxy sessions](/glossary/sticky-session) or mobile IPs, not a rotating proxy endpoint. Rotation belongs in front of stateless request-level scraping, not behind a persistent identity.
- Match the IP's geography to the profile's timezone, locale and language headers. A profile claiming
Europe/Berlinbehind a São Paulo IP fails a check that costs the detector nothing to run. - Store the binding in your profile definition, not only in the vendor's database. When you migrate vendors, and this category does see teams migrate, the fingerprint is replaceable and the IP mapping is not.
- Treat an unrecoverable proxy change as a profile retirement rather than an edit. If the IP is gone, the identity built on it is compromised anyway.
Choosing which proxy type sits behind a given profile is a separate decision with its own tradeoffs, covered in the proxy type by target guide, and the sourcing question behind residential pools is worth understanding before you sign anything: see residential proxy IP sourcing. If a single vendor's pool is load-bearing for your whole fleet, the multi-proxy-vendor failover guide covers what to build before the outage rather than during it.
Cookies and storage between runs
Persistence is the reason to use this category at all. If you did not need state to survive between runs, plain Playwright with a stealth plugin and a browser infrastructure provider would be cheaper and simpler.
Three failure modes account for most lost state.
The first is new_context(), covered above. It silently gives you a clean browser and the run looks like it worked.
The second is where the state physically lives. GoLogin's SDK, by its own documentation, uploads the profile to GoLogin storage when exit() is called. That is a feature if you run the same profile from several machines and a problem if your policy says session material stays on infrastructure you control. Kameleo and Dolphin Anty both document local-only profile storage as an option. Know which mode you are in before you audit it, not after.
The third is process termination. Chromium flushes cookies and IndexedDB on a clean shutdown. Kill the container and recent state is gone. Every batch runner needs a signal handler that calls the vendor's stop endpoint and waits for it, and job timeouts that are generous enough for that shutdown to finish.
One more thing worth doing: verify persistence explicitly rather than assuming it. A ten-line canary that starts a profile, sets a marker in local storage, stops it, restarts it and asserts the marker is still there will catch a broken configuration in seconds. Run it in CI against a throwaway profile.
Concurrency is a licence line, not a hardware line
Teams size these deployments by RAM and then discover the ceiling is contractual.
Kameleo is the clearest case because it prices the constraint directly. Its pricing page, read on 27 July 2026, sells a Concurrent Browsers Limit as the primary unit: 2 on the free tier, 10 on Startup at €59 per month, 100 on Business at €299 and 1,000 on Enterprise at €1,499, with add-on blocks above each. Its documentation states that every running session counts toward the limit whether a human opened it or a script did, and that stored profile counts are separate. Kameleo also documents Docker support as included at every tier with the same limits, which is unusual in a category where headless server operation is often gated.
Everyone else constrains you indirectly, through stored profile counts, seats and request rates. Multilogin's pricing page, read on 27 July 2026, sells plans by profile count in USD: a free tier at 5 profiles, Pro 10 through Pro 100 at $85 to $320 per year on annual billing, and Business from $685 per year at 300 profiles up to $3,600 at 10,000. The same page puts API access on Pro 10 and above, excludes the free tier from it, and documents rate limits of 100 requests per minute on Pro with custom limits on Business. So on Multilogin your automation ceiling is a request rate attached to a profile-count plan, which is a different thing to forecast than a concurrent-session count. AdsPower likewise ties request rate to stored profile count rather than to a plan name. Octo Browser's documentation notes that rate limits are shared across the whole team and vary by subscription level, which means one badly written script can rate-limit every colleague at once.
Prices in this category move constantly. Treat all of the above as a snapshot of published list prices on the date given and check the vendor's own page before you commit.
Then there is the hardware floor, which is real but usually not what binds first. Each running profile is a full browser process. Budget roughly 300MB to 700MB of RAM per instance depending on the pages you load, plus a CPU core for every three or four active profiles if the targets are JavaScript-heavy. Headless mode reduces that cost, and on Kameleo it is not a gating point: the same plan matrix lists headless browser support and Docker on every tier, including free. Its entry-tier restriction is elsewhere, on emulated mobile profiles, which Free and Startup limit to one at a time.
So the planning question is which of the three ceilings you hit first, and whether raising it means a purchase order or another server.
Which one is wrong for you
Recommending all seven would be useless, so here is what each one disqualifies itself from.
Kameleo is the strongest fit for engineering teams doing collection at scale: three first-class SDKs, an explicit fingerprint-search model, Docker and headless on every tier including free, and concurrency priced as a line item you can forecast. It is wrong for a solo operator or a small budget, because concurrency is exactly where it charges. Two concurrent browsers on the free tier is a validation allowance, not a working configuration, and the next step up is €59 per month for 10.
AdsPower has the most convenient start call in the table. The v2 endpoint hands back a Puppeteer WebSocket, a Selenium debug address and the path to a matching chromedriver in one response, so Puppeteer, Playwright and Selenium code paths all attach from the same call without a second lookup. It is wrong for anyone hoping to evaluate automation for free, since its Local API overview grants API permission to paid accounts. It is also wrong for chatty orchestration over a large profile library, because request rate is tied to stored profile count and the bottom band is 2 per second.
Multilogin has mature, actively maintained automation documentation with per-framework examples, and its launcher API is straightforward. It is wrong for evaluating automation at zero cost, since its pricing page excludes the free tier from API access and starts it at Pro 10. It is also organized around profile counts rather than concurrent automated sessions, so a script fleet is harder to forecast than on Kameleo: you are reasoning about 100 requests per minute against a plan sized in stored profiles.
Octo Browser has the single most useful primitive in this category for collection work: one-time profiles that are created, launched and deleted in one API call. If your work is stateless page fetching rather than long-lived identities, that removes the profile-lifecycle problem entirely. It is wrong for anyone on a subscription level below the one that enables the API, so confirm that on Octo Browser's current plan page rather than on this one, and its team-shared rate limits make it awkward for large mixed teams where one script's retry loop becomes everyone's problem.
GoLogin has the widest set of community examples and an MIT-licensed SDK, which shortens the first day considerably. It is wrong for teams under a data-residency constraint, because the default lifecycle uploads profile state to GoLogin's storage on exit.
Dolphin Anty has a well-established local API on port 3001 and long-standing Python community wrappers. It is wrong as a free-tier evaluation for automation specifically, since multiple mid-2026 reviews describe Local API access as paid-plan only; verify on the current plan page rather than on those reviews.
Incogniton wraps its local API in ergonomic Python and Node SDKs with a startPlaywright() one-liner, which is the fastest path if you already work in those languages. It is wrong for polyglot infrastructure, since its browser-automation layer is documented as SDK-exclusive rather than reachable over plain HTTP.
What this does not solve
An antidetect profile changes what the browser reports about itself. It does not change how the thing driving the browser behaves, and behaviour is where most modern detection lives. A profile with a flawless fingerprint that requests two hundred product pages in ninety seconds with no scrolling, no mouse movement and perfectly uniform intervals will be caught by an anti-bot system that never looks at canvas hashes at all. The reasons scrapers get blocked are mostly not fingerprint reasons; why scrapers get blocked goes through the full list.
It also does not make this the right tool for most collection work. A full browser per identity is the most expensive way to fetch a page, in memory, in licence cost and in engineering time. Reach for it when the target genuinely requires a persistent logged-in identity, or when the anti-bot layer defeats everything cheaper. For everything else, an API that returns rendered HTML is a better trade, and the 2026 web scraping stack post covers where each layer belongs.
If you have already decided you need one, drive it from code. The licence you are paying for includes an API, and the profile grid is the slowest way to use it.
Frequently asked
- Which antidetect browsers expose a local automation API?
- How do I attach Playwright to an antidetect browser profile?
- How many profiles can I run concurrently?
- Should each profile have its own proxy?
- How do I version and back up browser profiles?
Related guides
- Do You Need an Antidetect Browser, or Just a Scraping API?
Jul 27, 2026 · 14 min read
- Browser Agents for Pages Your Crawler Cannot Reach: Logins, Portals, Forms
Jul 27, 2026 · 15 min read
- Buying Web Data: Coverage Audits, Refresh Cadence and Licensing Terms
Jul 27, 2026 · 15 min read
Compare the tools mentioned
Weekly briefing – tool launches, legal shifts, market data.