> For the complete documentation index, see [llms.txt](https://docs.mmoweb.biz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mmoweb.biz/rest-api/master-akkaunt/spisok-master-akkauntov.md).

# Список мастер-аккаунтов

#### Описание

Endpoint возвращает список мастер-аккаунтов проекта, к которому привязан ваш Application Token.

Список отдаётся постранично и отсортирован по `id` по возрастанию, поэтому при последовательном обходе страниц вы получите каждый аккаунт ровно один раз. Набор полей каждого аккаунта совпадает с ответом endpoint'а [Получение мастер-аккаунта](/rest-api/master-akkaunt/poluchenie-master-akkaunta.md).

Endpoint работает в двух режимах:

* **Без параметров `filters` и `search`** — возвращает общий список всех мастер-аккаунтов проекта постранично.
* **С параметрами** — `filters` сужает список по дате регистрации и статусу, `search` находит аккаунты по пачке идентификаторов (`user_ids`, `emails`, `phones`) за один запрос.

Вы можете использовать данный endpoint для:

* Синхронизации базы пользователей с внешними системами (CRM, аналитика, рассылки)
* Периодической выгрузки новых регистраций по дате создания
* Массовой проверки, какие из ваших пользователей зарегистрированы в проекте, и получения их `user_id`
* Получения списка заблокированных или неподтверждённых аккаунтов

> Endpoint возвращает персональные данные пользователей (email, телефон, IP). Выдавайте право `Список мастер аккаунтов (v2)` только тем ключам, которым оно действительно необходимо.

***

#### Endpoint

```
GET https://api.mmoweb.biz/v2/master-account/list
```

**Требуемое разрешение ключа:** `Список мастер аккаунтов (v2)`

***

#### Параметры

Все параметры передаются в строке запроса (query string) и являются необязательными. Без `filters` и `search` возвращается общий список аккаунтов проекта. Параметры делятся на три группы: пагинация, фильтры и поиск.

**Пагинация**

| Параметр   | Тип     | Обязателен | Формат                        | Описание                               |
| ---------- | ------- | ---------- | ----------------------------- | -------------------------------------- |
| `page`     | integer | Нет        | `≥ 1`, по умолчанию `1`       | Номер страницы                         |
| `per_page` | integer | Нет        | `1..1000`, по умолчанию `100` | Количество аккаунтов на одной странице |

**Фильтры `filters[...]`**

Сужают список. Если передано несколько фильтров, аккаунт должен подходить под **все** из них.

| Параметр                | Тип    | Обязателен | Формат                                 | Описание                                                             |
| ----------------------- | ------ | ---------- | -------------------------------------- | -------------------------------------------------------------------- |
| `filters[created_from]` | string | Нет        | `YYYY-MM-DD` или `YYYY-MM-DD HH:MM:SS` | Показать аккаунты, созданные начиная с указанной даты (включительно) |
| `filters[created_to]`   | string | Нет        | `YYYY-MM-DD` или `YYYY-MM-DD HH:MM:SS` | Показать аккаунты, созданные до указанной даты (включительно)        |
| `filters[statuses]`     | string | Нет        | Значения `0`, `1`, `2` через запятую   | Показать аккаунты с любым из перечисленных статусов                  |

> **Статусы аккаунта:** `0` — активный, `1` — email не подтверждён, `2` — заблокирован.

> **Даты без времени.** Значение `YYYY-MM-DD` трактуется как `00:00:00` этого дня. Чтобы включить в диапазон весь день, укажите время явно: `filters[created_to]=2026-01-31 23:59:59`.

**Поиск `search[...]`**

Находит аккаунты по идентификаторам. Аккаунт попадает в ответ, если совпал **хотя бы с одним** значением из любого списка. Если одновременно переданы `filters`, они применяются к найденным аккаунтам.

| Параметр           | Тип    | Обязателен | Формат значений                                           | Описание                        |
| ------------------ | ------ | ---------- | --------------------------------------------------------- | ------------------------------- |
| `search[user_ids]` | string | Нет        | Целые числа `≥ 1` через запятую                           | Идентификаторы мастер-аккаунтов |
| `search[emails]`   | string | Нет        | Валидные email через запятую                              | Email мастер-аккаунтов          |
| `search[phones]`   | string | Нет        | Только цифры, 7–18 символов, первая не `0`, через запятую | Телефоны мастер-аккаунтов       |

> **Формат телефона.** Код страны и номер одной строкой из цифр, без `+`, пробелов, скобок и дефисов: `+7 (900) 123-45-67` передаётся как `79001234567`.

**Правила для `filters` и `search`**

* **Списки — через запятую.** Значения перечисляются в одной строке: `search[user_ids]=15,42,108`. Пробелы вокруг запятых допустимы, повторы учитываются один раз.
* **Массивный синтаксис не поддерживается.** Запрос вида `search[user_ids][]=15` возвращает `VALIDATION_ERROR`.
* **Переданный ключ не может быть пустым.** Запросы вида `search[emails]=` или `filters[statuses]=,` возвращают `VALIDATION_ERROR`. Если условие не нужно, не передавайте ключ вовсе.
* **Только известные ключи.** Внутри `filters` и `search` допускаются только перечисленные выше ключи. Опечатка вроде `search[email]` возвращает `VALIDATION_ERROR`, а не общий список.
* **Лимиты поиска.** Не более **100** значений в каждом списке `search` и не более **300** значений суммарно. Превышение возвращает `VALIDATION_ERROR`.

***

#### Пагинация в ответе

| Поле        | Тип             | Описание                                                                  |
| ----------- | --------------- | ------------------------------------------------------------------------- |
| `page`      | integer         | Текущая страница                                                          |
| `per_page`  | integer         | Размер страницы                                                           |
| `pages`     | integer         | Общее количество страниц с учётом `filters` и `search`                    |
| `next_page` | integer \| null | Номер следующей страницы. `null` означает, что текущая страница последняя |
| `total`     | integer         | Общее количество аккаунтов с учётом `filters` и `search`                  |

Рекомендуемый алгоритм обхода: запросите `page=1`, затем повторяйте запрос с `page=next_page` и теми же `filters`/`search`, пока `next_page` не станет `null`.

***

#### Кодировка

**Запрос**

* Значения параметров передаются в кодировке UTF-8 и должны быть URL-кодированы (percent-encoding).
* Квадратные скобки в именах параметров можно передавать как есть (`filters[statuses]`) или в кодированном виде (`filters%5Bstatuses%5D`).
* Запятая-разделитель допускается как есть или как `%2C`.
* Символ `+` в email обязательно кодируйте как `%2B`. Незакодированный `+` читается как пробел, и такой email не пройдёт проверку.
* Символ `@` допускается как в исходном виде, так и в виде `%40`.

| Исходные данные                              | Значение в query string                      |
| -------------------------------------------- | -------------------------------------------- |
| статусы `0` и `2`                            | `filters[statuses]=0,2`                      |
| email `a@x.com` и `b+promo@x.com`            | `search[emails]=a%40x.com,b%2Bpromo%40x.com` |
| телефоны `+7 (900) 123-45-67`, `79001234568` | `search[phones]=79001234567,79001234568`     |

Стандартные средства формирования query string кодируют значения автоматически: `URLSearchParams` в JavaScript, `http_build_query()` в PHP, `--data-urlencode` в cURL. Список значений при этом соберите в одну строку через запятую заранее.

**Ответ**

Ответ возвращается в формате JSON в кодировке UTF-8 с заголовком `Content-Type: application/json`.

***

#### Примеры

**Общий список без параметров**

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET "https://api.mmoweb.biz/v2/master-account/list" \
  -H "Authorization: Bearer APPLICATION_TOKEN_HERE"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const token = 'APPLICATION_TOKEN_HERE';

fetch('https://api.mmoweb.biz/v2/master-account/list', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$token = 'APPLICATION_TOKEN_HERE';
$url = 'https://api.mmoweb.biz/v2/master-account/list';

$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . $token
  ]
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

echo "HTTP Status: " . $httpCode . "\n";
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
?>
```

{% endtab %}
{% endtabs %}

**Фильтр: заблокированные и неподтверждённые аккаунты за январь 2026**

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET -G "https://api.mmoweb.biz/v2/master-account/list" \
  --data-urlencode "filters[statuses]=1,2" \
  --data-urlencode "filters[created_from]=2026-01-01" \
  --data-urlencode "filters[created_to]=2026-01-31 23:59:59" \
  --data-urlencode "per_page=500" \
  -H "Authorization: Bearer APPLICATION_TOKEN_HERE"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const token = 'APPLICATION_TOKEN_HERE';

const params = new URLSearchParams({
  'filters[statuses]': [1, 2].join(','),
  'filters[created_from]': '2026-01-01',
  'filters[created_to]': '2026-01-31 23:59:59',
  per_page: 500
});

fetch(`https://api.mmoweb.biz/v2/master-account/list?${params}`, {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$token = 'APPLICATION_TOKEN_HERE';

$url = 'https://api.mmoweb.biz/v2/master-account/list?' . http_build_query([
  'filters' => [
    'statuses' => implode(',', [1, 2]),
    'created_from' => '2026-01-01',
    'created_to' => '2026-01-31 23:59:59'
  ],
  'per_page' => 500
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . $token
  ]
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

echo "HTTP Status: " . $httpCode . "\n";
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
?>
```

{% endtab %}
{% endtabs %}

**Поиск: пачка аккаунтов по email и телефонам**

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET -G "https://api.mmoweb.biz/v2/master-account/list" \
  --data-urlencode "search[emails]=first@example.com,second+promo@example.com" \
  --data-urlencode "search[phones]=79001234567,79001234568" \
  -H "Authorization: Bearer APPLICATION_TOKEN_HERE"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const token = 'APPLICATION_TOKEN_HERE';

const emails = ['first@example.com', 'second+promo@example.com'];
const phones = ['+7 (900) 123-45-67', '79001234568'].map(p => p.replace(/\D/g, ''));

const params = new URLSearchParams({
  'search[emails]': emails.join(','),
  'search[phones]': phones.join(',')
});

fetch(`https://api.mmoweb.biz/v2/master-account/list?${params}`, {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => {
  if (!data.success) {
    console.error(data.code, data.not_found);
    return;
  }
  console.log('Найдено:', data.accounts.map(a => a.id));
  if (data.not_found) {
    console.log('Не найдены email:', data.not_found.emails);
    console.log('Не найдены телефоны:', data.not_found.phones);
  }
})
.catch(error => console.error('Error:', error));
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$token = 'APPLICATION_TOKEN_HERE';

$emails = ['first@example.com', 'second+promo@example.com'];
$phones = array_map(fn($p) => preg_replace('/\D/', '', $p), ['+7 (900) 123-45-67', '79001234568']);

$url = 'https://api.mmoweb.biz/v2/master-account/list?' . http_build_query([
  'search' => [
    'emails' => implode(',', $emails),
    'phones' => implode(',', $phones)
  ]
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . $token
  ]
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);

if (!$data['success']) {
  echo "Ошибка: {$data['code']}\n";
  exit;
}

echo "Найдено: " . implode(', ', array_column($data['accounts'], 'id')) . "\n";

if (isset($data['not_found'])) {
  echo "Не найдены email: " . implode(', ', $data['not_found']['emails']) . "\n";
  echo "Не найдены телефоны: " . implode(', ', $data['not_found']['phones']) . "\n";
}
?>
```

{% endtab %}
{% endtabs %}

**Полный обход списка по страницам**

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const token = 'APPLICATION_TOKEN_HERE';

async function fetchAllAccounts(filters = {}) {
  const accounts = [];
  let page = 1;

  while (page !== null) {
    const params = new URLSearchParams({ page, per_page: 1000 });
    for (const [key, value] of Object.entries(filters)) {
      params.set(`filters[${key}]`, value);
    }

    const response = await fetch(
      `https://api.mmoweb.biz/v2/master-account/list?${params}`,
      { headers: { 'Authorization': `Bearer ${token}` } }
    );
    const data = await response.json();

    if (!data.success) {
      if (data.code === 'ACCOUNTS_NOT_FOUND') break;
      throw new Error(data.code);
    }

    accounts.push(...data.accounts);
    page = data.next_page;
  }

  return accounts;
}

fetchAllAccounts({ statuses: '0', created_from: '2026-01-01' })
  .then(accounts => console.log(`Получено аккаунтов: ${accounts.length}`))
  .catch(error => console.error('Error:', error));
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$token = 'APPLICATION_TOKEN_HERE';

function fetchPage(string $token, int $page, array $filters): array
{
  $url = 'https://api.mmoweb.biz/v2/master-account/list?' . http_build_query([
    'page' => $page,
    'per_page' => 1000,
    'filters' => $filters
  ]);

  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token]
  ]);
  $response = curl_exec($ch);
  curl_close($ch);

  return json_decode($response, true);
}

$filters = ['statuses' => '0', 'created_from' => '2026-01-01'];
$accounts = [];
$page = 1;

while ($page !== null) {
  $data = fetchPage($token, $page, $filters);

  if (!$data['success']) {
    if ($data['code'] === 'ACCOUNTS_NOT_FOUND') {
      break;
    }
    throw new RuntimeException($data['code']);
  }

  $accounts = array_merge($accounts, $data['accounts']);
  $page = $data['next_page'];
}

echo "Получено аккаунтов: " . count($accounts) . "\n";
?>
```

{% endtab %}
{% endtabs %}

***

#### Ответы

**Успешный ответ (200 OK)**

```json
{
  "success": true,
  "page": 1,
  "per_page": 100,
  "pages": 1,
  "next_page": null,
  "total": 1,
  "accounts": [
    {
      "id": 1,
      "email": "first@example.com",
      "phone": "79001234567",
      "shield": 0,
      "affiliate": 0,
      "select_lid": 1,
      "select_sid": 3,
      "project_id": 5,
      "status": 0,
      "desc_status": "",
      "created_at": "2026-01-01 10:00:00",
      "last_login": "2026-02-01 12:30:00",
      "last_ip": "203.0.113.10",
      "email_valid": 1,
      "lang": "ru",
      "iso": "RU",
      "telegram": ""
    }
  ],
  "not_found": {
    "user_ids": [],
    "emails": ["second+promo@example.com"],
    "phones": ["79001234568"]
  }
}
```

> В примере выше запрос искал два email и два телефона. Найден один аккаунт, поэтому `total: 1`, а второй email и второй телефон перечислены в `not_found`. Если бы все значения нашлись, блока `not_found` в ответе не было бы. Подробнее — в разделе «Важные особенности».

**Поля аккаунта:**

| Поле          | Тип            | Описание                                                                         |
| ------------- | -------------- | -------------------------------------------------------------------------------- |
| `id`          | integer        | Идентификатор мастер-аккаунта                                                    |
| `email`       | string \| null | Email                                                                            |
| `phone`       | string \| null | Телефон. `"0"` означает, что телефон не указан                                   |
| `shield`      | integer        | Включена ли дополнительная защита аккаунта (`1` — да, `0` — нет)                 |
| `affiliate`   | integer        | Идентификатор пригласившего аккаунта, `0` — реферал не установлен                |
| `select_lid`  | integer        | Идентификатор выбранного логин-сервера                                           |
| `select_sid`  | integer        | Идентификатор выбранного игрового сервера                                        |
| `project_id`  | integer        | Идентификатор проекта                                                            |
| `status`      | integer        | Статус аккаунта (`0` — активный, `1` — email не подтверждён, `2` — заблокирован) |
| `desc_status` | string \| null | Сообщение, которое видит пользователь при блокировке                             |
| `created_at`  | string         | Дата регистрации в формате `YYYY-MM-DD HH:MM:SS`                                 |
| `last_login`  | string \| null | Дата последнего входа в формате `YYYY-MM-DD HH:MM:SS`                            |
| `last_ip`     | string \| null | IP-адрес последнего входа                                                        |
| `email_valid` | integer        | Подтверждён ли email (`1` — да, `0` — нет)                                       |
| `lang`        | string \| null | Язык интерфейса пользователя                                                     |
| `iso`         | string \| null | ISO-код страны                                                                   |
| `telegram`    | string \| null | Telegram пользователя                                                            |

**Ошибка структуры параметров (400 Bad Request)**

```json
{
  "success": false,
  "code": "VALIDATION_ERROR",
  "message": {
    "search.email": "Unknown key. Allowed keys: user_ids, emails, phones."
  }
}
```

> **Когда встречается:** неизвестный ключ внутри `filters` или `search`, массивный синтаксис `[]` вместо строки через запятую, ключ передан с пустым значением (`search[emails]=`), либо `filters`/`search` переданы без ключа (`filters=abc`)

**Превышен лимит поиска (400 Bad Request)**

```json
{
  "success": false,
  "code": "VALIDATION_ERROR",
  "message": {
    "search.emails": "Maximum 100 values allowed.",
    "search": "Maximum 300 values allowed in total."
  }
}
```

**Неверный формат значения (400 Bad Request)**

```json
{
  "success": false,
  "code": "VALIDATION_ERROR",
  "message": {
    "search.emails.1": "The search.emails field must contain a valid email address.",
    "filters.statuses.0": "The filters.statuses field must be one of: 0,1,2."
  }
}
```

> **Когда встречается:** `page` или `per_page` вне допустимого диапазона, дата не распознана, статус не из списка `0,1,2`, среди `user_ids` есть не целое число, среди `emails` — невалидный адрес, среди `phones` — значение с символами кроме цифр, ведущим `0` или неверной длиной. Для списков ключ ошибки содержит позицию проблемного значения, начиная с `0`: в примере выше это второй email и первый статус

**Некорректный диапазон дат (400 Bad Request)**

```json
{
  "success": false,
  "code": "INVALID_DATE_RANGE"
}
```

> **Когда встречается:** `filters[created_from]` позже `filters[created_to]`

**Страница за пределами списка (400 Bad Request)**

```json
{
  "success": false,
  "code": "PAGE_OUT_OF_RANGE",
  "pages": 7,
  "total": 67
}
```

> **Когда встречается:** запрошенный `page` больше количества страниц. В ответе возвращаются актуальные `pages` и `total`, чтобы вы могли скорректировать запрос

**Аккаунты не найдены (404 Not Found)**

```json
{
  "success": false,
  "code": "ACCOUNTS_NOT_FOUND",
  "not_found": {
    "user_ids": [15],
    "emails": [],
    "phones": []
  }
}
```

> **Когда встречается:** ни один аккаунт проекта не подходит под `filters` и `search`. Успешный ответ никогда не содержит пустой список. Если передан `search`, в ответе есть блок `not_found` со всеми переданными идентификаторами

**Недостаточно прав (403 Forbidden)**

```json
{
  "success": false,
  "code": "ACCESS_DENIED"
}
```

> **Когда встречается:** у Application Token не включено разрешение `Список мастер аккаунтов (v2)`

***

#### Коды ошибок

| Код                  | HTTP Status | Описание                                                        | Что делать                                                                                                           |
| -------------------- | ----------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `VALIDATION_ERROR`   | 400         | Неверная структура, превышен лимит или неверный формат значения | Сверьте имена ключей, лимиты 100/300 и форматы значений с разделом «Параметры»                                       |
| `INVALID_DATE_RANGE` | 400         | Начало диапазона дат позже его конца                            | Поменяйте `filters[created_from]` и `filters[created_to]` местами или исправьте значения                             |
| `PAGE_OUT_OF_RANGE`  | 400         | Запрошенная страница больше `pages`                             | Используйте `pages` из ответа; при обходе списка ориентируйтесь на `next_page`                                       |
| `ACCOUNTS_NOT_FOUND` | 404         | Под указанные условия не подходит ни один аккаунт               | Это нормальный результат для пустой выборки; при поиске используйте `not_found`, чтобы узнать, что именно не найдено |
| `ACCESS_DENIED`      | 403         | У токена нет разрешения на этот endpoint                        | Включите разрешение `Список мастер аккаунтов (v2)` для ключа в панели администратора                                 |

Ошибки аутентификации (`EMPTY_TOKEN`, `INVALID_TOKEN`, `TOKEN_NOT_FOUND`, `TOKEN_DISABLED`) общие для всех endpoint'ов API v2 и описаны во введении.

***

#### Важные особенности

**Без параметров — общий список**

Если не переданы ни `filters`, ни `search`, endpoint возвращает всех мастер-аккаунтов проекта постранично. Это основной режим для полной выгрузки.

**Фильтры сужают, поиск объединяет**

Условия внутри `filters` объединяются через И: аккаунт должен подходить под каждое. Значения внутри `search` объединяются через ИЛИ: достаточно совпадения по любому `user_id`, email или телефону. Если переданы обе группы, в ответ попадают аккаунты, которые совпали с `search` и одновременно подходят под `filters`. Например, `search[emails]=a@x.com&filters[statuses]=2` вернёт аккаунт `a@x.com`, только если он заблокирован; иначе ответ будет `ACCOUNTS_NOT_FOUND`, а email окажется в `not_found`.

**Пустой результат — это ошибка, а не успех**

Если под условия не попал ни один аккаунт, endpoint возвращает `ACCOUNTS_NOT_FOUND` (404), а не `success: true` с пустым массивом. При обходе списка обрабатывайте этот код как штатное завершение.

**`not_found` появляется только при пропусках**

Блок `not_found` есть в ответе, только если передан `search` и хотя бы одно его значение не нашлось. Если найдены все идентификаторы, блока нет, поэтому проверяйте его наличие перед чтением. Внутри блока всегда три списка: `user_ids`, `emails`, `phones`; списки без пропусков пустые.

Значение попадает в `not_found`, если по нему не найден ни один аккаунт, подходящий под `filters`. Блок рассчитывается по всей выборке, не зависит от `page` и `per_page` и одинаков на каждой странице.

**Стабильный порядок**

Аккаунты всегда отсортированы по `id` по возрастанию. Новые регистрации получают больший `id`, поэтому они появляются на последних страницах и не сдвигают уже прочитанные.

**Сравнение email и телефонов**

Регистр букв в email не учитывается. Алиасы вида `user+promo@example.com` и `user@example.com` считаются разными адресами. Телефон должен совпадать полностью, включая код страны.

***

#### Рекомендации

**Безопасность:**

* Храните Application Token на сервере и никогда не передавайте его в браузер или мобильное приложение
* Выдавайте разрешение `Список мастер аккаунтов (v2)` отдельному ключу, который используется только для выгрузки
* Используйте HTTPS для всех запросов

**Производительность:**

* Для полной выгрузки используйте `per_page=1000`, чтобы сократить количество запросов
* Для регулярной синхронизации запрашивайте только новые аккаунты через `filters[created_from]` с даты последней выгрузки
* Для проверки пачки пользователей используйте `search` вместо отдельных запросов к Получению мастер-аккаунта: до 300 идентификаторов за один запрос

**Надёжность:**

* Реализуйте retry logic с экспоненциальной задержкой при ошибках 5xx и сетевых сбоях
* Обрабатывайте `ACCOUNTS_NOT_FOUND` как пустую выборку, а не как сбой
* Ориентируйтесь на `next_page` вместо самостоятельного расчёта номера следующей страницы
* Передавайте одни и те же `filters` и `search` на каждой странице обхода

**Интеграция:**

* Собирайте списки для `search` в строку через запятую средствами языка (`join`, `implode`) и формируйте query string стандартными функциями кодирования
* Убедитесь, что токен приложения имеет разрешение `Список мастер аккаунтов (v2)`
