---
title: "AdsPower adapter"
description: "The AdsPower-compatible local API: how to switch it on, which calls it answers, and what an existing AdsPower script has to change."
canonical: "https://scalebrowser.net/docs/adspower"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://scalebrowser.net/llms.txt
> Use this file to discover all available pages before exploring further.

# AdsPower adapter

> The AdsPower-compatible local API: how to switch it on, which calls it answers, and what an existing AdsPower script has to change.


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.

<Warning>

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

</Warning>

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

```toml
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.

<Warning>

**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]`.

</Warning>

Restart the daemon and ask it whether the adapter is up:

```bash
$ 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; everything that writes is a POST with a JSON body.

| Route                    | Method | Takes                           | `data` on success                                  |
| ------------------------ | ------ | ------------------------------- | -------------------------------------------------- |
| `/status`                | GET    | none                            | `{service}`                                        |
| `/api/v1/status`         | GET    | none                            | `{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    | none                            | `{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    | none                            | `{list}` of proxies, never their credentials       |

A profile is a _user_ here, keyed by `user_id`, and that is the same id the [REST API](/docs/rest) 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:

```json
{"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. It means the same thing it means on the [REST API](/docs/rest), 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, because 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/new` reads `name` and `group_id` and 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](/docs/rest), where they are stored encrypted, checked before a launch and injected by the daemon so that no credential ever reaches your script.
- **`webdriver` is always an empty string.** No chromedriver is shipped, so a Selenium script has to bring its own binary and point it at `ws.selenium` (the `host:port` form) as its debugger address. `ws.puppeteer` carries the full WebSocket URL for everything else, the same endpoint [direct CDP](/docs/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. It is also authenticated, which the adapter cannot be.

## Next
- [REST API](/docs/rest): the native surface: profiles, proxies, groups, bulk operations.
- [Direct CDP](/docs/cdp): what to do with the endpoint `browser/start` gave you.
- [Install & run](/docs/install): where the config file lives and how the daemon reads it.
