Docs · Interfaces
AdsPower adapter
A local API that answers in AdsPower's request and response shape, so an existing script keeps working. It is off until you switch it on.
Off by default, and the port is the reason. Detector pages guess which anti-detect software a machine has installed by probing localhost ports, and 50325 is AdsPower's. A daemon that answers there is a daemon that has announced itself before your first page load. The engine defends against those probes on its own; keeping the port shut is the second layer, and it costs nothing while you are not using it. Switch it on to migrate, and consider switching it off again once you have.
Switch it on
One setting, in the daemon's TOML config, naming the address to bind. The commented line in the shipped example config is the value you want:
adspower_bind = "127.0.0.1:50325"
The environment variable is SCALEBROWSER_ADSPOWER_BIND and it overrides
the file. It takes an address, or one of the words off,
none, disabled — or an empty value — to turn the adapter
off again. The config key itself only parses an address, so the env variable is the
way to disable it without editing the file.
Loopback only — a network address stops the daemon from starting.
The adapter is unauthenticated by design, because that is what makes an
unmodified AdsPower script work: there is no token to add. Binding it to
0.0.0.0 or a LAN address would put an unauthenticated control API on
the network, so the daemon refuses at startup with an error naming the address
instead of coming up. Use 127.0.0.1 or [::1].
Restart the daemon and ask it whether the adapter is up:
$ curl http://127.0.0.1:50325/status
{"code":0,"msg":"success","data":{"service":"scalebrowser-adspower-adapter"}}
No token, no header. If the connection is refused, the adapter is off — check that
the daemon really loaded the config file you edited. The daemon also writes the
adapter's address into <data_dir>/runtime.json when it is enabled,
which is the quickest way to see what it actually bound.
The calls it answers
Fourteen routes. The methods are AdsPower's: reads are GET with query
parameters — including browser/start, which surprises people
the first time — and everything that writes is a POST with a JSON body.
| Route | Method | Takes | data on success |
|---|---|---|---|
/status | GET | — | {service} |
/api/v1/status | GET | — | {service} |
/api/v1/browser/start | GET | user_id, headless | {ws:{selenium,puppeteer}, debug_port, webdriver} |
/api/v1/browser/stop | GET | user_id | null |
/api/v1/browser/active | GET | user_id | {status} — Active or Inactive |
/api/v1/user/list | GET | group_id, page, page_size | {list, page, page_size} |
/api/v1/user/new | POST | name, group_id | {id} — the new profile id |
/api/v1/user/update | POST | user_id, name, group_id | null |
/api/v1/user/delete | POST | user_ids — an array | null |
/api/v1/group/list | GET | — | {list} of {group_id, group_name} |
/api/v1/group/create | POST | group_name | {group_id} |
/api/v1/group/update | POST | group_id, group_name | null |
/api/v1/group/delete | POST | group_id | null |
/api/v1/proxy/list | GET | — | {list} of proxies, never their credentials |
A profile is a user here, keyed by user_id — that is the same
id the REST API calls a profile id, so the two surfaces address
the same objects and you can mix them. Parameters the adapter does not know are
ignored rather than rejected, so extra AdsPower arguments on a call do no harm; they
also do nothing.
A listed user carries user_id, name,
group_id, created_time and an always-empty
domain_name. page_size defaults to 100 and is capped at
1000; page starts at 1.
Every answer is HTTP 200
Including the failures — that is AdsPower's convention and the adapter keeps it, so a
client that only checks the status code will think everything worked. The
code field is what carries the truth:
{"code": 0, "msg": "success", "data": { … }} // success
{"code": 4003, "msg": "capacity exceeded: …"} // a coded failure
{"code": -1, "msg": "user_id is required"} // a malformed request
0 is success. A four-digit code is the daemon's own — 4001
not found, 4002 a geo or proxy mismatch, 4003 capacity,
4004 no engine installed, 4005 a failed pre-launch check,
4006 already running — and it means the same thing it means on the
REST API, where the same codes come with the matching HTTP status.
-1 is a request the adapter could not make sense of.
An absent parameter means a visible window
This is deliberate, and it is the opposite of what the native API does. On
browser/start the rule is AdsPower's:
| Request | This adapter | Native /v1 |
|---|---|---|
no headless parameter | visible window | headless |
headless=0 | visible window | visible window |
headless=1 | headless | headless |
The native surface assumes an unattended start, because that is what an agent or an SDK does. A migrated AdsPower script assumes the opposite — it opened windows and a person watched them — and an adapter that quietly changed that would leave its user staring at a screen where nothing happens. So the two defaults differ on purpose, and the difference is pinned by a test.
What your script has to change
Usually just the base URL, and if you bind 127.0.0.1:50325 and your
script already points there, not even that. The route shapes, the query parameters,
the {code, msg, data} envelope and the visible-by-default rule are all
the ones it already expects. Four behaviours do differ, and each one can break a
script silently rather than loudly:
-
A fingerprint in the request body is ignored.
user/newreadsnameandgroup_idand nothing else. The persona is generated here, as one coherent set — that is the product, and a client-supplied mixture of values is exactly what makes a profile detectable. Sending the payload is harmless; expecting it to take effect is not. - A proxy in the request body is ignored too. Assign proxies over the REST API, where they are stored encrypted, checked before a launch and injected by the daemon so that no credential ever reaches your script.
-
webdriveris always an empty string. No chromedriver is shipped, so a Selenium script has to bring its own binary and point it atws.selenium(thehost:portform) as its debugger address.ws.puppeteercarries the full WebSocket URL for everything else — it is the same endpoint direct CDP describes. - The lists are thinner. Users come back with five fields and proxies without credentials. A script that reads a field AdsPower had and this does not will find it missing rather than empty.
Once the script runs, the adapter has done its job. Everything it can do, the native API does with more of it — per-launch visibility, bulk operations, proxy checks, session export — and it is authenticated, which the adapter cannot be.
Next
- REST API — the native surface: profiles, proxies, groups, bulk operations.
- Direct CDP — what to do with the endpoint
browser/startgave you. - Install & run — where the config file lives and how the daemon reads it.