---
title: Browser API — облачный браузер MegaIndex для автоматизации через CDP
description: Подключение к облачному браузеру MegaIndex через Playwright, Puppeteer и Chrome DevTools Protocol, настройка прокси и покупка browser traffic.
keywords: browser api, cloud browser, megaindex, cdp, websocket, playwright, puppeteer, captcha
---

# Browser API MegaIndex

Browser API — это облачный браузер MegaIndex для автоматизации сайтов, которым нужен настоящий браузер: JavaScript-рендеринг, клики, формы, скролл, геозависимый контент и обработка CAPTCHA.

Ваш скрипт подключается к удалённому браузеру по CDP WebSocket URL и управляет им через Playwright, Puppeteer или другой клиент с поддержкой Chrome DevTools Protocol (CDP). Запускать и обслуживать Chrome на своём сервере не требуется.

Для работы Browser API используются два независимых ресурса:

- **browser traffic** — трафик облачного браузера, который учитывается MegaIndex в GB;
- **custom proxy** — внешний прокси пользователя. MegaIndex не продаёт, не тарифицирует и не учитывает трафик такого прокси.

Новый пользователь получает Default account, Default profile, 1 GB тестового browser traffic и ограниченный бесплатный IPv6 fallback для первоначальной проверки. В текущей реализации fallback также может применяться к другим профилям с `proxyMode: "none"`. Для рабочих подключений используйте собственный прокси.

## Что можно делать через Browser API

- подключаться к удалённому Chrome через CDP;
- использовать Playwright, Puppeteer и другие CDP-клиенты;
- создавать browser accounts и отдельные browser profiles;
- сохранять custom proxy на уровне аккаунта или профиля;
- передавать custom proxy только для конкретного подключения;
- автоматически или вручную решать CAPTCHA через CDP-интерфейс `Captcha`;
- получать историю и статистику расхода browser traffic;
- покупать дополнительный browser traffic за баланс MegaIndex.

## Основные термины

**Browser account** — основная учётная запись облачного браузера. Она содержит browser login, browser password и общие настройки подключения.

**Browser profile** — отдельная браузерная среда внутри browser account. Для параллельных процессов используйте разные профили.

**Default account** — browser account с именем `Default browser account`, автоматически доступный новому пользователю.

**Default profile** — начальный профиль Default account.

**Browser traffic** — трафик, переданный облачным браузером. Он приобретается и учитывается отдельно от трафика пользовательского прокси.

**Custom proxy** — внешний HTTP, HTTPS, SOCKS4 или SOCKS5 прокси пользователя.

**CDP URL / `connectionUri`** — WebSocket URL с данными доступа для подключения к облачному браузеру.

# Быстрый старт

## 1. Проверьте тестовые ресурсы

При первом подключении пользователю доступны:

- Default account;
- Default profile;
- 1 GB тестового browser traffic;
- ограниченный бесплатный IPv6 fallback.

Этого достаточно, чтобы проверить CDP-подключение без предварительной покупки browser traffic и настройки собственного прокси.

IPv6 fallback предназначен только для знакомства с сервисом. Некоторые сайты не поддерживают IPv6, блокируют такой трафик или показывают другой контент. Для рабочего сценария настройте custom proxy.

## 2. Получите готовый CDP URL

Используйте CDP URL Default profile из интерфейса либо запросите `connectionUri` через публичный API.

Не публикуйте `connectionUri`: он содержит данные доступа к браузеру.

## 3. Подключитесь через Playwright

Установите Playwright:

```bash
npm install playwright
```

Сохраните CDP URL в переменную окружения:

```bash
export MEGAINDEX_BROWSER_CDP_URL="PASTE_CDP_URL_HERE"
```

Создайте файл `quick-start.js`:

```javascript
const { chromium } = require('playwright');

async function main() {
  const connectionUri = process.env.MEGAINDEX_BROWSER_CDP_URL;

  if (!connectionUri) {
    throw new Error('MEGAINDEX_BROWSER_CDP_URL is not set');
  }

  const browser = await chromium.connectOverCDP(connectionUri);
  const context = browser.contexts()[0];
  const page = await context.newPage();

  await page.goto('https://example.com', {
    waitUntil: 'networkidle',
    timeout: 60_000,
  });

  console.log(await page.title());
  await page.screenshot({ path: 'browser-api-test.png', fullPage: true });
  await browser.close();
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

Запустите пример:

```bash
node quick-start.js
```

## 4. Подключитесь через Puppeteer

Установите Puppeteer Core:

```bash
npm install puppeteer-core
```

```javascript
const puppeteer = require('puppeteer-core');

async function main() {
  const connectionUri = process.env.MEGAINDEX_BROWSER_CDP_URL;

  if (!connectionUri) {
    throw new Error('MEGAINDEX_BROWSER_CDP_URL is not set');
  }

  const browser = await puppeteer.connect({
    browserWSEndpoint: connectionUri,
  });

  const page = await browser.newPage();
  await page.goto('https://example.com', {
    waitUntil: 'networkidle2',
    timeout: 60_000,
  });

  console.log(await page.title());
  await browser.disconnect();
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

## 5. Перейдите на custom proxy

Для рабочего подключения добавьте собственный прокси на уровне browser account, browser profile или запроса `connection`.

MegaIndex не учитывает proxy traffic. Его стоимость, лимиты, география и доступность определяются провайдером вашего прокси.

# Публичный HTTP API

## Base URL

Текущий тестовый endpoint:

```text
http://89.108.119.8/test-browser-api/browser.php
```

Далее в примерах используется переменная:

```bash
BASE_URL="http://89.108.119.8/test-browser-api/browser.php"
```

## Авторизация

Для запросов используется API key пользователя MegaIndex. Получить или перевыпустить его можно на странице:

```text
/profile/api-key
```

После перевыпуска старый ключ перестаёт работать.

Ключ поддерживается в четырёх форматах.

Query string:

```bash
curl "$BASE_URL?method=accounts&key=YOUR_API_KEY"
```

JSON body:

```bash
curl -X POST "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{"method":"connection","key":"YOUR_API_KEY","accountId":123}'
```

Заголовок `X-API-Key`:

```bash
curl "$BASE_URL?method=accounts" \
  -H "X-API-Key: YOUR_API_KEY"
```

Bearer token:

```bash
curl "$BASE_URL?method=accounts" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Bearer-аутентификация подтверждена фактическим запросом: корректный ключ в заголовке `Authorization` возвращает `200 OK` и обычный ответ выбранного метода.

Не размещайте API key во frontend-коде, публичных репозиториях, скриншотах и логах.

## Формат запросов

Операция передаётся в параметре `method`. Технически также поддерживаются `endpoint` и `action`, но для единообразия используйте `method`.

Для JSON-запросов передавайте:

```http
Content-Type: application/json
```

## Методы

| `method` | HTTP | Назначение |
|---|---|---|
| `prices` | `GET` | Получить стоимость browser traffic с учётом объёма покупки. |
| `accounts` | `GET`, `POST`, `PUT`, `DELETE` | Управлять browser accounts. |
| `profiles` | `GET`, `POST`, `PUT`, `DELETE` | Управлять browser profiles. |
| `connection` | `GET`, `POST` | Получить WebSocket URI для CDP-подключения. |
| `history` | `GET` | Получить историю операций Browser API. |
| `statistics` | `GET` | Получить статистику использования browser traffic. |
| `buy` | `POST` | Купить browser traffic за баланс MegaIndex. |

Алиасы `stats`, `buy-traffic` и `traffic-buy` поддерживаются для совместимости. В новой интеграции используйте основные названия методов.

# Browser traffic и оплата

## Что учитывает MegaIndex

MegaIndex учитывает только browser traffic — сетевой трафик облачного браузера. Он измеряется в GB и расходуется независимо от трафика custom proxy.

Custom proxy принадлежит пользователю. MegaIndex не показывает его остаток, не списывает за него деньги и не контролирует тариф прокси-провайдера.

Практически это означает, что во время рабочего подключения могут одновременно расходоваться:

1. browser traffic в MegaIndex;
2. proxy traffic у внешнего провайдера.

## Получить цены

```bash
curl "$BASE_URL?method=prices&key=YOUR_API_KEY"
```

С явным указанием валюты:

```bash
curl "$BASE_URL?method=prices&currency=usd&key=YOUR_API_KEY"
```

Ответ содержит массив ценовых диапазонов:

```json
[
  {
    "from": 1,
    "to": 9,
    "price": 5,
    "discount": 0,
    "oldPrice": 5,
    "currency": "usd"
  },
  {
    "from": 10,
    "to": 29,
    "price": 4,
    "discount": 20,
    "oldPrice": 5,
    "currency": "usd"
  },
  {
    "from": 10000,
    "to": 0,
    "price": 1.4,
    "discount": 72,
    "oldPrice": 5,
    "currency": "usd"
  }
]
```

Поля диапазона:

| Поле | Тип | Описание |
|---|---|---|
| `from` | integer | Минимальный объём покупки в GB, включительно. |
| `to` | integer | Максимальный объём в GB, включительно. Значение `0` означает отсутствие верхней границы. |
| `price` | number | Цена одного GB после применения объёмной скидки. |
| `oldPrice` | number | Базовая цена одного GB без скидки. |
| `discount` | number | Скидка относительно базовой цены, в процентах. Может быть дробной. |
| `currency` | string | Валюта цены. В подтверждённом ответе используется `usd`. |

При покупке API автоматически выбирает ценовой диапазон по значению `flow`. Например, при покупке от 10 до 29 GB применяется цена `4 USD` за GB, а для покупки от 10000 GB — `1.4 USD` за GB.

## Купить browser traffic

```bash
curl -X POST "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "buy",
    "key": "YOUR_API_KEY",
    "flow": 10,
    "idempotencyKey": "browser-traffic-order-1001"
  }'
```

Параметры:

| Поле | Тип | Обязательное | Описание |
|---|---|---:|---|
| `flow` | integer | да | Покупаемое количество GB. Текущий допустимый диапазон: от 1 до 100000. |
| `idempotencyKey` | string | да | Уникальный ключ операции, защищающий от повторного списания. |

Для повторной отправки того же заказа используйте тот же `idempotencyKey`. Для новой покупки создайте новый ключ.

Пример ответа:

```json
{
  "status": "OK",
  "flow": 10,
  "financeId": 23082035,
  "price": {
    "amount": 50,
    "pricePerGb": 5,
    "oldPrice": 5,
    "discount": 0,
    "currency": "usd"
  },
  "balance": {
    "before": 100,
    "after": 50,
    "currency": "usd"
  },
  "traffic": {
    "totalKb": 10485760,
    "usedKb": 0,
    "availableKb": 10485760,
    "totalGb": 10,
    "usedGb": 0,
    "availableGb": 10
  }
}
```

В успешном ответе:

- `flow` — купленный объём в GB;
- `financeId` — ID финансовой операции MegaIndex;
- `price.amount` — полная стоимость покупки;
- `price.pricePerGb` — применённая цена одного GB;
- `balance.before` и `balance.after` — баланс до и после списания;
- `traffic` — обновлённый общий, использованный и доступный browser traffic в KB и GB.

Подтверждённое соотношение: `1 GB = 1048576 KB`.

Если запрос с тем же `idempotencyKey` уже был обработан, деньги повторно не списываются:

```json
{
  "status": "OK",
  "duplicate": true,
  "flow": 10,
  "financeId": 23082035,
  "price": {
    "amount": 50,
    "pricePerGb": 5,
    "oldPrice": 5,
    "discount": 0,
    "currency": "usd"
  },
  "balance": {
    "current": 50,
    "currency": "usd"
  }
}
```

В повторном ответе:

- `duplicate: true` подтверждает, что операция уже была обработана;
- `financeId` совпадает с ID первоначальной покупки;
- повторного списания и начисления browser traffic не происходит;
- вместо `balance.before`/`balance.after` возвращается `balance.current`;
- блок `traffic` отсутствует.

Если начисление browser traffic не завершилось, списание с баланса MegaIndex откатывается.

# Browser accounts

## Получить список аккаунтов

```bash
curl "$BASE_URL?method=accounts&key=YOUR_API_KEY"
```

Сокращённый пример ответа:

```json
{
  "status": "OK",
  "project": {
    "id": 2,
    "code": "megaindex",
    "name": "MegaIndex"
  },
  "count": 1,
  "maxAccounts": 10,
  "unlimitedAccounts": false,
  "data": [
    {
      "id": 123,
      "login": "REDACTED_BROWSER_LOGIN",
      "password": "REDACTED_BROWSER_PASSWORD",
      "name": "Default browser account",
      "status": 1,
      "country": "en",
      "defaultProfileId": 456,
      "proxyMode": "none",
      "customProxy": null,
      "profile": {
        "id": 456,
        "accountId": 123,
        "profileId": "p0123456789abcdef0123456789abcdef",
        "isDefault": true,
        "name": "Default profile",
        "proxyMode": "inherit",
        "usedKb": 1075,
        "usedGb": 0.001,
        "requestCount": 8,
        "status": 1
      },
      "profilesCount": 1,
      "usedKb": 1075,
      "usedGb": 0.001,
      "requestCount": 8,
      "maxProfiles": 1000,
      "unlimitedProfiles": false,
      "connection": {
        "host": "browser.example.com:9222",
        "username": "REDACTED_USERNAME",
        "password": "REDACTED_BROWSER_PASSWORD",
        "profileId": "p0123456789abcdef0123456789abcdef"
      },
      "connectionUri": "REDACTED_CONNECTION_URI",
      "createdAt": "2026-07-23 17:53:21",
      "updatedAt": "2026-07-23 17:53:21"
    }
  ]
}
```

Важные поля:

- `count` — текущее количество browser accounts;
- `maxAccounts` — максимальное количество accounts, по умолчанию 10;
- `unlimitedAccounts` — отключён ли количественный лимит;
- `defaultProfileId` — числовой внутренний ID записи Default profile;
- `profile.profileId` — строковый ID профиля, используемый при получении `connectionUri` и в CDP URL;
- `profilesCount` и `maxProfiles` — текущее и максимальное количество профилей account;
- `usedKb`, `usedGb` и `requestCount` — суммарная статистика account;
- `profile.isDefault: true` — признак Default profile;
- `profile.proxyMode: "inherit"` — профиль наследует прокси-настройки account.

Не путайте числовой `profile.id` и строковый `profile.profileId`. Для `connection` используйте строковый `profileId`.

Ответ также содержит служебные объекты `project`, `serviceUser`, `proxySettings` и подробный объект `connection`. Их не нужно изменять на стороне клиента.

## Создать аккаунт

Browser account можно создать без сохранённого прокси. В этом случае custom proxy нужно сохранить позднее либо передать при получении `connectionUri`. Ограниченный IPv6 fallback доступен только для первоначального теста.

```bash
curl -X POST "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "accounts",
    "key": "YOUR_API_KEY",
    "name": "Main browser account"
  }'
```

Успешный ответ содержит `status`, `project` и созданный объект `account`. Вместе с account автоматически создаётся Default profile:

- `account.proxyMode` имеет значение `none`;
- `account.customProxy` имеет значение `null`;
- `profile.isDefault` имеет значение `true`;
- `profile.name` имеет значение `Default profile`;
- `profile.proxyMode` имеет значение `inherit`;
- `account.defaultProfileId` совпадает с числовым `profile.id`;
- статистика нового account/profile начинается с нулевых значений;
- `profile.lastUsedAt` до первого использования является пустой строкой.

При стандартном лимите можно создать до 10 accounts. Каждый account может содержать до 1000 profiles. Фактические ограничения возвращаются в `maxAccounts`, `unlimitedAccounts`, `maxProfiles` и `unlimitedProfiles`.

Поле `name` задаёт отображаемое имя browser account и возвращается в `account.name`.

## Изменить, обновить пароль и удалить аккаунт

Метод `accounts` поддерживает `PUT` и `DELETE`.

Удаление account:

```bash
curl -X DELETE "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "accounts",
    "key": "YOUR_API_KEY",
    "id": 123
  }'
```

Успешный ответ:

```json
{
  "status": "OK",
  "project": {
    "id": 2,
    "code": "megaindex",
    "name": "MegaIndex"
  }
}
```

# Browser profiles

## Получить список профилей

```bash
curl "$BASE_URL?method=profiles&accountId=123&page=1&limit=50&key=YOUR_API_KEY"
```

Параметры:

| Параметр | Тип | Обязательный | Описание |
|---|---|---:|---|
| `accountId` | integer | да | Числовой ID browser account. |
| `page` | integer | нет | Номер страницы, начиная с 1. |
| `limit` | integer | нет | Максимальное количество профилей на странице. |

Сокращённый пример ответа:

```json
{
  "status": "OK",
  "project": {
    "id": 2,
    "code": "megaindex",
    "name": "MegaIndex"
  },
  "maxProfiles": 1000,
  "unlimitedProfiles": false,
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "pages": 1
  },
  "data": [
    {
      "id": 456,
      "accountId": 123,
      "profileId": "p0123456789abcdef0123456789abcdef",
      "isDefault": true,
      "name": "Default profile",
      "zone": "scraping_browser",
      "country": "en",
      "proxyMode": "none",
      "customProxy": null,
      "profileData": {
        "comment": ""
      },
      "usedKb": 4677,
      "usedGb": 0.0045,
      "requestCount": 26,
      "status": 1,
      "source": "frontend_auto",
      "connectionUri": "REDACTED_CONNECTION_URI",
      "createdAt": "2026-08-05 10:34:49",
      "updatedAt": "2026-08-05 14:48:02",
      "lastUsedAt": "2026-08-05 14:48:02",
      "deletedAt": ""
    }
  ]
}
```

Объект `pagination` содержит текущую страницу, установленный лимит, общее количество профилей и количество страниц.

Признак `isDefault: true` означает, что профиль является Default profile своего account. Это не определяет режим прокси: Default profile может возвращаться как с `proxyMode: "inherit"`, так и с `proxyMode: "none"`, в зависимости от настроек и способа создания account/profile.

Поля `connection` и `connectionUri` содержат данные подключения. Не публикуйте и не записывайте их в открытые логи.

## Создать профиль

```bash
curl -X POST "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "profiles",
    "key": "YOUR_API_KEY",
    "accountId": 123,
    "name": "Google SERP checks"
  }'
```

Для параллельных CDP-подключений используйте разные профили.

Поле `name` задаёт отображаемое имя профиля и возвращается в `profile.name`. Строковое поле `profileId` применяется во внешнем API и CDP URL, а числовое `id` является внутренним ID записи.

Успешное создание пользовательского профиля возвращает `status`, `project` и объект `profile`. Новый профиль:

- получает сгенерированные сервером числовой `id` и строковый `profileId`;
- имеет `isDefault: false`;
- по умолчанию использует `proxyMode: "inherit"`;
- начинает с нулевых `usedKb`, `usedGb` и `requestCount`;
- имеет `source: "frontend"`;
- до первого подключения имеет пустой `lastUsedAt`.

Для сравнения, автоматически созданный Default profile имеет `isDefault: true` и `source: "frontend_auto"`.

Стандартный лимит — 1000 profiles на один account. Значения `maxProfiles` и `unlimitedProfiles` возвращаются как в объекте account, так и на верхнем уровне ответа списка профилей.

# Custom proxy

## Обязательность прокси

Browser account и profile можно создать без сохранённого прокси. Для рабочего подключения к браузеру custom proxy обязателен.

Исключение — ограниченный бесплатный IPv6 fallback для первоначального тестирования. В подтверждённом ответе он применился к Default profile обычного пользовательского account с `proxyMode: "none"`, а не только к системному Default account. Профиль с `proxyMode: "inherit"` также остаётся без сохранённого прокси, если родительский account имеет режим `none`.

При доступном fallback запрос `connection` без `customProxy` возвращает `200 OK` и готовый `connectionUri`. В `connection.username` при этом отсутствует сегмент `-proxy-`, а верхнеуровневое поле `customProxy` равно `null`.

Не используйте `proxySettings.exists` для определения наличия custom proxy. В подтверждённом ответе у профиля это поле было `true`, хотя `proxyMode` был `none`, `customProxy` был `null`, а подключение сформировалось без сегмента `-proxy-`.

## Формат `customProxy`

```json
{
  "type": "http",
  "host": "proxy.example.com",
  "port": 8000,
  "login": "proxy_user",
  "password": "proxy_password"
}
```

| Поле | Тип | Обязательное | Описание |
|---|---|---:|---|
| `type` | string | да | `http`, `https`, `socks4` или `socks5`. |
| `host` | string | да | Host или IP-адрес прокси. |
| `port` | integer | да | Порт прокси. |
| `login` | string | нет | Логин, если прокси требует авторизацию. |
| `password` | string | нет | Пароль, если прокси требует авторизацию. |

Фактическая страна выхода определяется самим custom proxy.

## Приоритет прокси

Предполагаемый порядок выбора:

1. custom proxy, переданный для конкретного `connectionUri`;
2. custom proxy, сохранённый у browser profile;
3. custom proxy, сохранённый у browser account;
4. ограниченный IPv6 fallback, если он в данный момент доступен и после применения наследования у профиля нет сохранённого прокси.

# Получение `connectionUri`

## Подключение с сохранённым прокси

```bash
curl "$BASE_URL?method=connection&accountId=123&key=YOUR_API_KEY"
```

Для конкретного профиля:

```bash
curl "$BASE_URL?method=connection&accountId=123&profileId=PROFILE_ID&key=YOUR_API_KEY"
```

## Подключение с прокси для текущей сессии

```bash
curl -X POST "$BASE_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "connection",
    "key": "YOUR_API_KEY",
    "accountId": 123,
    "customProxy": {
      "type": "http",
      "host": "proxy.example.com",
      "port": 8000,
      "login": "proxy_user",
      "password": "proxy_password"
    }
  }'
```

Пример ответа:

```json
{
  "status": "OK",
  "connectionUri": "REDACTED_CONNECTION_URI",
  "connection": {
    "scheme": "ws",
    "host": "cb.2captcha.com:9222",
    "username": "REDACTED_CONNECTION_USERNAME",
    "password": "REDACTED_BROWSER_PASSWORD"
  },
  "account": {
    "id": 123,
    "name": "Main browser account",
    "proxyMode": "none"
  },
  "profile": {
    "id": 456,
    "accountId": 123,
    "profileId": "p0123456789abcdef0123456789abcdef",
    "isDefault": true,
    "name": "Default profile",
    "proxyMode": "none"
  },
  "customProxy": {
    "type": "http",
    "host": "proxy.example.com",
    "port": 8000,
    "login": "REDACTED_PROXY_LOGIN",
    "password": "REDACTED_PROXY_PASSWORD"
  }
}
```

Получение URI само по себе не запускает браузерную сессию. Сессия начинается после подключения CDP-клиента.

Переданный для подключения custom proxy возвращается в верхнеуровневом поле `customProxy`. В подтверждённом ответе он не был сохранён в account или profile: оба объекта сохранили `proxyMode: "none"` и `customProxy: null`. Таким образом, прокси из запроса применяется к сформированному подключению, но не становится сохранённой настройкой account/profile.

В `connection.username` присутствует сегмент `-proxy-{encodedProxy}`. Значение `encodedProxy` является Base64URL-представлением полного URL прокси, включая credentials. Base64URL — это обратимое кодирование, а не шифрование.

Считайте секретными и не логируйте:

- весь `connectionUri`;
- `connection.username`;
- `connection.password`;
- browser login и password внутри вложенного `account`;
- весь верхнеуровневый объект `customProxy`, если в нём есть credentials.

Ответ `connection` содержит полные объекты выбранных `account` и `profile`. Их ID совпадают с `accountId` и `profileId`, переданными в запросе.

## Подключение без custom proxy

Если у account/profile установлен `proxyMode: "none"` и доступен тестовый IPv6 fallback, запрос без `customProxy` завершается успешно:

```json
{
  "status": "OK",
  "connectionUri": "REDACTED_CONNECTION_URI",
  "connection": {
    "scheme": "ws",
    "host": "cb.2captcha.com:9222",
    "username": "REDACTED_CONNECTION_USERNAME_WITHOUT_PROXY_SEGMENT",
    "password": "REDACTED_BROWSER_PASSWORD"
  },
  "account": {
    "id": 123,
    "proxyMode": "none",
    "customProxy": null
  },
  "profile": {
    "accountId": 123,
    "profileId": "p0123456789abcdef0123456789abcdef",
    "isDefault": true,
    "proxyMode": "none",
    "customProxy": null
  },
  "customProxy": null
}
```

Такое подключение использует ограниченный IPv6 fallback. Отсутствие `-proxy-` в username подтверждает, что custom proxy в URI не встроен.

Не используйте fallback как постоянную рабочую конфигурацию: его доступность ограничена и может быть отключена. Для рабочих сценариев передавайте custom proxy.

# CDP-интерфейс `Captcha`

CDP-функциональность и решение CAPTCHA в MegaIndex совпадают с Browser API 2Captcha. После подключения к облачному браузеру можно использовать стандартные возможности Playwright/Puppeteer и дополнительный CDP-домен `Captcha`.

Решение CAPTCHA входит в Browser API и отдельно не оплачивается. MegaIndex списывает только browser traffic, израсходованный облачным браузером.

## Назначение

CDP-домен `Captcha` позволяет управлять решением CAPTCHA на текущей вкладке облачного браузера.

Доступны два режима:

1. Автоматическое решение после загрузки страницы.
2. Ручной запуск через команду `Captcha.solve`.

События CDP позволяют отслеживать обнаружение CAPTCHA, отправку запроса в сервис, успешное решение и ошибку.

## Подключение к CDP-сессии

### Playwright

```javascript
import { chromium } from 'playwright';

const browser = await chromium.connectOverCDP(connectionUri);
const context = browser.contexts()[0];
const page = await context.newPage();
const session = await context.newCDPSession(page);
```

### Puppeteer

```javascript
import puppeteer from 'puppeteer-core';

const browser = await puppeteer.connect({
  browserWSEndpoint: connectionUri
});

const page = await browser.newPage();
const session = await page.target().createCDPSession();
```

## Типы данных CDP

### `CaptchaOptions`

Один элемент массива `options`. Обычно передаётся один объект:

```json
{
  "type": "*",
  "submitForm": false,
  "selector": ".captcha-container",
  "detectSelector": ".captcha-container",
  "responseSelector": "textarea[name=\"g-recaptcha-response\"]"
}
```

| Поле | Тип | Описание |
|---|---|---|
| `type` | string | Тип CAPTCHA. Передайте `*` для автоматического определения. |
| `submitForm` | boolean | Отправить форму после получения токена. |
| `submitSelector` | string | CSS-селектор кнопки отправки формы. |
| `selector` | string | CSS-селектор контейнера CAPTCHA. |
| `detectSelector` | string | CSS-селектор для обнаружения CAPTCHA. |
| `responseSelector` | string | CSS-селектор поля, куда нужно записать токен. |
| `sitekeyAttributes` | string[] | Атрибуты, из которых можно получить `sitekey`. |
| `actionAttributes` | string[] | Атрибуты, из которых можно получить `action` для reCAPTCHA v3. |

### `SolveResult`

Ответ команды `Captcha.solve` возвращается в поле `result`:

```json
{
  "result": {
    "status": "solveFinished",
    "token": "03AGdBq26..."
  }
}
```

| Поле | Тип | Описание |
|---|---|---|
| `status` | string | `solveFinished`, `solveFailed`, `notDetected` или `invalid`. |
| `token` | string | Токен при успешном решении. |
| `errorMessage` | string | Текст ошибки при неуспешном решении. |

## Команды CDP

### `Captcha.setAutoSolve`

Включает или отключает автоматическое решение CAPTCHA на вкладке:

```javascript
await session.send('Captcha.setAutoSolve', {
  autoSolve: true,
  options: [
    {
      type: '*'
    }
  ]
});
```

| Параметр | Тип | Обязательный | Описание |
|---|---|---:|---|
| `autoSolve` | boolean | да | `true` — решать автоматически; `false` — использовать только `Captcha.solve`. |
| `options` | `CaptchaOptions[]` | нет | Настройки поиска и решения CAPTCHA. |

При успехе команда не возвращает тело.

Возможная CDP-ошибка:

```text
No active frame
```

### `Captcha.solve`

Запускает явное решение CAPTCHA на текущей вкладке:

```javascript
const response = await session.send('Captcha.solve', {
  detectTimeout: 15000,
  options: [
    {
      type: '*'
    }
  ]
});

console.log(response.result.status);
console.log(response.result.token);
```

| Параметр | Тип | Обязательный | Описание |
|---|---|---:|---|
| `detectTimeout` | integer | нет | Таймаут обнаружения CAPTCHA в миллисекундах. Внутренний лимит ответа примерно равен `detectTimeout + 10s`; без параметра используется около `60s`. |
| `options` | `CaptchaOptions[]` | нет | Параметры поиска и решения CAPTCHA. |

Успешное решение:

```json
{
  "result": {
    "status": "solveFinished",
    "token": "03AGdBq26..."
  }
}
```

CAPTCHA не найдена:

```json
{
  "result": {
    "status": "notDetected"
  }
}
```

Возможные CDP-ошибки без `SolveResult`:

```text
No active frame
Captcha.solve timed out waiting for extension response
```

## События CDP

Подписка в Playwright:

```javascript
session.on('Captcha.detected', () => {
  console.log('CAPTCHA detected');
});

session.on('Captcha.waitForSolve', () => {
  console.log('CAPTCHA sent to solver');
});

session.on('Captcha.solveFinished', () => {
  console.log('CAPTCHA solved');
});

session.on('Captcha.solveFailed', () => {
  console.log('CAPTCHA solve failed');
});
```

| Событие | Описание |
|---|---|
| `Captcha.detected` | CAPTCHA обнаружена на странице. |
| `Captcha.waitForSolve` | Запрос отправлен в сервис, идёт ожидание ответа. |
| `Captcha.solveFinished` | CAPTCHA успешно решена. |
| `Captcha.solveFailed` | Решение завершилось ошибкой. |

Цепочка событий в автоматическом режиме:

```text
Captcha.detected → Captcha.waitForSolve → Captcha.solveFinished | Captcha.solveFailed
```

События предназначены для отслеживания прогресса. Токен возвращается только в ответе команды `Captcha.solve`.

## Рекомендуемые сценарии

### Автоматическое решение CAPTCHA

Используйте автоматический режим, если браузер должен сам решать CAPTCHA после загрузки страницы:

```javascript
await session.send('Captcha.setAutoSolve', {
  autoSolve: true,
  options: [{ type: '*' }]
});

const solved = new Promise((resolve, reject) => {
  session.once('Captcha.solveFinished', resolve);
  session.once('Captcha.solveFailed', reject);
});

await page.goto('https://example.com');
await solved;
```

Рекомендуемый порядок:

```text
Captcha.setAutoSolve({ autoSolve: true, options })
  → навигация на страницу с CAPTCHA
  → ожидание Captcha.solveFinished или Captcha.solveFailed
```

В автоматическом режиме токен забирать и подставлять самостоятельно не нужно. Он записывается на странице и, если задано, форма отправляется автоматически через `responseSelector`, `submitForm` и `submitSelector`. События сообщают о готовности, но не содержат токен.

### Ручное решение CAPTCHA

Используйте ручной запуск, чтобы контролировать момент начала решения и получить токен:

```javascript
await session.send('Captcha.setAutoSolve', {
  autoSolve: false,
  options: [{ type: '*' }]
});

await page.goto('https://example.com');
await page.waitForTimeout(5000);

const { result } = await session.send('Captcha.solve', {
  detectTimeout: 15000,
  options: [{ type: '*' }]
});

if (result.status === 'solveFinished') {
  console.log('Token:', result.token);
} else {
  console.log('Captcha solve status:', result.status, result.errorMessage);
}
```

Рекомендуемый порядок:

```text
Captcha.setAutoSolve({ autoSolve: false, options })
  → навигация
  → пауза 3–5 секунд для регистрации виджета
  → Captcha.solve({ detectTimeout, options })
  → проверка result.status
```

Не вызывайте `Captcha.solve` из обработчика `Captcha.detected`. Для ручного режима достаточно одного вызова после короткой паузы.

## Таймауты CDP

| Таймаут | Рекомендация |
|---|---|
| Внутренний таймаут браузера | `detectTimeout + 10s` или около `60s`, если `detectTimeout` не передан. |
| Таймаут клиента | Не меньше `detectTimeout + 120–180s`, чтобы учесть время ожидания решения. |
| Максимальная продолжительность браузерной сессии | 30 минут. Выбирайте таймауты так, чтобы решение не вышло за пределы сессии. |

Если CDP-клиент имеет собственный таймаут на `session.send`, увеличьте его для `Captcha.solve`, иначе клиент может прекратить ожидание раньше ответа сервиса.

## Полный пример: MegaIndex Browser API + Playwright + авто-решение

```javascript
import { chromium } from 'playwright';

const API_URL = 'http://89.108.119.8/test-browser-api/browser.php';
const API_KEY = process.env.MEGAINDEX_API_KEY;

async function createConnectionUri() {
  const response = await fetch(API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      method: 'connection',
      key: API_KEY,
      accountId: 123,
      profileId: 'p0123456789abcdef0123456789abcdef',
      customProxy: {
        type: 'http',
        host: 'proxy.example.com',
        port: 8080,
        login: 'proxyuser',
        password: 'proxypass'
      }
    })
  });

  const data = await response.json();

  if (data.status !== 'OK') {
    throw new Error(`${data.errorCode}: ${data.error}`);
  }

  return data.connectionUri;
}

const connectionUri = await createConnectionUri();
const browser = await chromium.connectOverCDP(connectionUri);
const context = browser.contexts()[0];
const page = await context.newPage();
const session = await context.newCDPSession(page);

session.on('Captcha.detected', () => console.log('CAPTCHA detected'));
session.on('Captcha.waitForSolve', () => console.log('Waiting for MegaIndex'));
session.on('Captcha.solveFinished', () => console.log('CAPTCHA solved'));
session.on('Captcha.solveFailed', () => console.log('CAPTCHA solve failed'));

await session.send('Captcha.setAutoSolve', {
  autoSolve: true,
  options: [{ type: '*' }]
});

await page.goto('https://example.com');
```

Не храните API key и proxy credentials непосредственно в исходном коде рабочего приложения. Используйте переменные окружения или хранилище секретов.

## Полный пример: ручной `Captcha.solve`

```javascript
import { chromium } from 'playwright';

const connectionUri = process.env.MEGAINDEX_BROWSER_CDP_URL;
const browser = await chromium.connectOverCDP(connectionUri);
const context = browser.contexts()[0];
const page = await context.newPage();
const session = await context.newCDPSession(page);

await session.send('Captcha.setAutoSolve', {
  autoSolve: false,
  options: [{ type: '*' }]
});

await page.goto('https://example.com');
await page.waitForTimeout(5000);

const { result } = await session.send('Captcha.solve', {
  detectTimeout: 15000,
  options: [{ type: '*' }]
});

switch (result.status) {
  case 'solveFinished':
    console.log('CAPTCHA token:', result.token);
    break;
  case 'solveFailed':
    console.log('CAPTCHA solve failed:', result.errorMessage);
    break;
  case 'notDetected':
    console.log('CAPTCHA was not detected on the page');
    break;
  case 'invalid':
    console.log('Invalid solve request:', result.errorMessage);
    break;
  default:
    console.log('Unknown CAPTCHA status:', result.status);
}
```

## Рекомендации по интеграции

1. Получайте `connectionUri` через `method=connection`, а не собирайте WebSocket URL вручную.
2. Для рабочих сценариев всегда передавайте custom proxy через запрос подключения или сохранённые настройки account/profile.
3. Для стабильной изоляции используйте отдельные profiles под разные сценарии.
4. Если нужен только факт успешного прохождения CAPTCHA, используйте автоматический режим и события.
5. Если нужен токен, используйте ручной `Captcha.solve`.
6. Увеличивайте таймаут CDP-клиента для ручного решения.
7. Проверяйте `result.status`, а не только наличие ответа от команды.
8. Для диагностики логируйте события, но не логируйте токены, `connectionUri`, `connection.username`, browser password и proxy credentials.

Отдельного баланса или тарифа для решения CAPTCHA нет: оплачивается только browser traffic.

# История и статистика

## История

```bash
curl "$BASE_URL?method=history&page=1&limit=50&key=YOUR_API_KEY"
```

Метод возвращает историю покупок browser traffic:

```json
{
  "status": "OK",
  "project": {
    "id": 2,
    "code": "megaindex",
    "name": "MegaIndex"
  },
  "data": [
    {
      "id": 10,
      "flow": 1,
      "trafficKb": 1048576,
      "price": 5,
      "amount": 5,
      "valute": "usd",
      "status": 1,
      "createdAt": "2026-08-05 16:20:19"
    }
  ]
}
```

| Поле | Тип | Описание |
|---|---|---|
| `id` | integer | ID операции покупки. |
| `flow` | integer | Купленный объём browser traffic в GB. |
| `trafficKb` | integer | Тот же объём в KB. Один GB соответствует 1048576 KB. |
| `price` | number | Цена одного GB. |
| `amount` | number | Полная сумма операции: `flow × price`. |
| `valute` | string | Валюта операции. Название поля в API — именно `valute`. |
| `status` | integer | Числовой статус операции. |
| `createdAt` | string | Дата и время создания операции в формате `YYYY-MM-DD HH:mm:ss`. |

Операции в подтверждённом ответе расположены от новых к старым.

## Статистика browser traffic

```bash
curl "$BASE_URL?method=statistics&key=YOUR_API_KEY"
```

Сокращённый пример ответа:

```json
{
  "status": "OK",
  "project": {
    "id": 2,
    "code": "megaindex",
    "name": "MegaIndex"
  },
  "period": {
    "dateFrom": "2026-07-06",
    "dateTo": "2026-08-05"
  },
  "traffic": {
    "totalKb": 3145728,
    "usedKb": 0,
    "availableKb": 3145728,
    "totalGb": 3,
    "usedGb": 0,
    "availableGb": 3
  },
  "usage": {
    "trafficKb": 5752,
    "trafficGb": 0.0055,
    "requestCount": 34
  },
  "accounts": [
    {
      "id": 123,
      "login": "REDACTED_BROWSER_LOGIN",
      "name": "Default browser account",
      "status": 1,
      "trafficKb": 1075,
      "trafficGb": 0.001,
      "requestCount": 8,
      "lastUsedAt": "2026-08-05 10:00:00"
    }
  ],
  "profileUsage": {
    "trafficKb": 5752,
    "trafficGb": 0.0055,
    "requestCount": 34
  },
  "profiles": [
    {
      "id": 456,
      "accountId": 123,
      "profileId": "p0123456789abcdef0123456789abcdef",
      "name": "Default profile",
      "trafficKb": 1075,
      "trafficGb": 0.001,
      "requestCount": 8,
      "totalUsedKb": 1075,
      "totalUsedGb": 0.001,
      "totalRequestCount": 8,
      "periodLastUsedAt": "2026-08-05 10:00:00",
      "lastUsedAt": "2026-08-05 10:39:02"
    }
  ]
}
```

Основные блоки:

- `period` — период, за который рассчитаны показатели использования;
- `traffic` — общий приобретённый, использованный и доступный объём browser traffic;
- `usage` — суммарный расход и количество запросов за выбранный период;
- `accounts` — расход за период с группировкой по browser account;
- `profileUsage` — суммарный расход профилей за период;
- `profiles` — периодические и накопленные показатели отдельных профилей.

В объектах `profiles` поля `trafficKb`, `trafficGb` и `requestCount` относятся к выбранному периоду, а `totalUsedKb`, `totalUsedGb` и `totalRequestCount` содержат накопленные значения.

В подтверждённом ответе без явных дат API выбрал период с 6 июля по 5 августа 2026 года.

# Ошибки

Все ошибки возвращаются в JSON:

```json
{
  "errorId": 1,
  "errorCode": "ERROR_WRONG_USER_KEY",
  "error": "API key is invalid."
}
```

| `errorCode` | HTTP | Значение |
|---|---:|---|
| `ERROR_KEY_DOES_NOT_EXIST` | 401 | API key не передан. |
| `ERROR_WRONG_USER_KEY` | 401 | API key не найден в MegaIndex. |
| `ERROR_ACCOUNT_SUSPENDED` | 403 | Аккаунт отключён. |
| `ERROR_METHOD` | 405 | Неподдерживаемый HTTP method. |
| `ERROR_NO_SUCH_METHOD` | 404 | Неизвестный API method. |
| `ERROR_FLOW_AMOUNT` | 400 | Некорректное количество GB. |
| `ERROR_IDEMPOTENCY_KEY` | 400 | Некорректный idempotency key. |
| `ERROR_INSUFFICIENT_FUNDS` | 400 | Недостаточно средств на балансе MegaIndex. |
| `ERROR_GB_PRICE` | 400 | Не удалось определить цену browser traffic. |
| `ERROR_CONNECTION_URI` | 404 | Нет данных для подключения к браузеру. |
| `ERROR_BROWSER_BACKEND_UNAVAILABLE` | 502 | Browser backend временно недоступен. |
| `ERROR_BROWSER_BACKEND_BAD_RESPONSE` | 502 | Browser backend вернул некорректный ответ. |

Пример неверного API key:

```json
{
  "errorId": 1,
  "errorCode": "ERROR_WRONG_USER_KEY",
  "error": "API key is invalid."
}
```

HTTP status: `401 Unauthorized`.

Пример неизвестного значения `method`:

```json
{
  "errorId": 1,
  "errorCode": "ERROR_NO_SUCH_METHOD",
  "error": "Method is not supported."
}
```

HTTP status: `404 Not Found`.

# Ограничения и безопасность

- Для каждого параллельного процесса используйте отдельный browser profile.
- Не передавайте API key, `connectionUri`, browser password или proxy password третьим лицам.
- Не публикуйте `connection.username`: встроенный в него Base64URL-сегмент custom proxy может содержать обратимо закодированные proxy login и password.
- Учитывайте, что ответ `connection` возвращает custom proxy вместе с credentials; не записывайте полный JSON-ответ в открытые логи.
- Не сохраняйте секреты во frontend-коде и публичных логах.
- При повторе покупки используйте тот же `idempotencyKey` только для того же заказа.
- Учитывайте отдельно остаток browser traffic MegaIndex и лимит proxy traffic у своего провайдера.

Максимальная продолжительность браузерной сессии — 30 минут.

# Короткий FAQ

## Что оплачивается в MegaIndex?

MegaIndex учитывает и продаёт browser traffic в GB. Дополнительный трафик приобретается с баланса MegaIndex.

## Входит ли proxy traffic в browser traffic?

Нет. Это независимые ресурсы. MegaIndex не учитывает и не оплачивает трафик вашего custom proxy.

## Можно ли создать browser account без прокси?

Да. Прокси можно добавить позднее на уровне account или profile либо передать при получении `connectionUri`.

## Можно ли подключиться без собственного прокси?

Для первоначального теста профиля с `proxyMode: "none"` может быть доступен ограниченный IPv6 fallback. Для рабочих подключений требуется custom proxy.

## Сколько тестового browser traffic предоставляется?

Новый пользователь получает 1 GB тестового browser traffic.

## Можно ли повторить запрос покупки после сетевой ошибки?

Да. Для того же заказа повторно передайте тот же `idempotencyKey`, чтобы исключить двойное списание.

## Поддерживается ли автоматическое решение CAPTCHA?

Да. MegaIndex поддерживает тот же CDP-интерфейс `Captcha`, что и Browser API 2Captcha.

## Нужно ли отдельно оплачивать решение CAPTCHA?

Нет. Решение CAPTCHA входит в Browser API. MegaIndex учитывает и списывает только browser traffic.
