Введение в API
API PWA Hub нужен для ваших скриптов: выгрузить статистику и конверсии, наклонировать PWA под новую кампанию, повесить домен, включить или выключить приложение - без ручной работы в панели.
Через API доступно то же, что вы делаете в панели, и с теми же правами: скрипт видит и меняет ровно те PWA, сплиты и прочие ресурсы, что и вы - team_lead ресурсы своих команд, customer свои, и те и другие - открытые им через группы доступа. Настройки, пользователи, аккаунты интеграций, пароль и сам ключ через API недоступны - только из панели.
Коллекция для Postman
Все методы API готовы к запуску без кода: скачайте коллекцию, импортируйте её в Postman и задайте переменные baseUrl (адрес вашей панели с /api) и apiKey.
Получение ключа
- Администратор включает вам Manage API Token (см. раздел «Пользователи»). Это нужно и самим администраторам: без переключателя ключ не выдаётся и не работает.
- Откройте Profile и в карточке API Key нажмите Generate Key (см. раздел «Профиль»).
- Скопируйте ключ и сохраните его в настройках скрипта.
Ключ - это пароль
С ключом можно делать всё, что можете вы сами. Не публикуйте его в репозиториях, чатах и коде, который выполняется в браузере. Если ключ утёк - перевыпустите его в профиле, старый перестанет работать сразу.
API работает, только если он входит в вашу лицензию, и только пока лицензия активна.
Запрос
Адрес API - домен вашей панели с /api на конце, например https://panel.example.com/api. Ключ передаётся в заголовке Authorization, тело запроса - JSON:
curl https://panel.example.com/api/user/profile \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer ВАШ_КЛЮЧ', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->get('user/profile');
$user = json_decode($response->getBody(), true)['data']['user'];
echo $user['email'] . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer ВАШ_КЛЮЧ", "Accept": "application/json"}
response = requests.get(f"{API}/user/profile", headers=HEADERS, timeout=30)
user = response.json()["data"]["user"]
print(user["email"])// Node.js 18+, без зависимостей; сохраните как script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer ВАШ_КЛЮЧ', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/user/profile`, { headers, signal: AbortSignal.timeout(30000) });
const { data } = await response.json();
console.log(data.user.email);Этот запрос - удобная проверка ключа: он возвращает пользователя, которому ключ принадлежит, и его команды.
Идентификаторы
У всех объектов короткие строковые идентификаторы вида WZAX - и в ответах, и в запросах (team_id, cloudflare_id и т.д.). Берите их из ответов API или из адресной строки панели.
Ответ
Данные всегда лежат в поле data:
{
"data": {
"pwa": { "id": "WZAX", "name": "Promo EN", "active": true }
}
}Списки приходят постранично: в data.meta - номер страницы и общее количество, рядом - сами записи под именем ресурса.
{
"data": {
"meta": { "current_page": 1, "per_page": 100, "last_page": 3, "total": 245 },
"pwas": [ { "id": "WZAX", "name": "Promo EN" } ]
}
}Страница задаётся параметром page, её размер - per_page (по умолчанию 100). Чтобы забрать всё, идите по страницам, пока current_page не дойдёт до last_page.
Даты в ответах - в формате 05 Oct 2026 14:03, по UTC.
Ошибки
Ошибка - это HTTP-код и JSON с data: null и текстом в message:
{
"data": null,
"message": "The name field is required. (and 2 more errors)",
"errors": {
"name": ["The name field is required."],
"cloudflare_id": ["The cloudflare id field is required."]
}
}| Код | Когда | Что делать |
|---|---|---|
401 | Unauthenticated - не передан заголовок Authorization; Invalid API key - ключ неверный, перевыпущен или у вас выключили Manage API Token; API key access is not enabled - API нет в лицензии; Account is not approved - аккаунт не подтверждён | Проверьте ключ в профиле, обратитесь к администратору |
403 | Not available with an API key - этот метод доступен только из панели; This action is unauthorized. - нет прав на объект; или лицензия неактивна (тогда в ответе есть поле error) | Проверьте, что объект в вашей команде |
404 | Not found - объекта с таким идентификатором нет | Проверьте идентификатор |
422 | Ошибка в данных - в errors перечислены поля и что с ними не так | Исправьте поля из errors |
429 | Too many requests. Try again in a minute. или Too many invalid API key attempts. Try again in a minute. - превышен лимит, см. «Лимиты» | Подождите минуту и повторите |
Обработка ошибок
Проверяйте код ответа, прежде чем читать data. При ошибке выведите message, а для 422 - ещё и поля из errors: там сказано, что именно исправить. Пример ниже нарочно создаёт PWA без названия и печатает, что ответил сервер.
curl -X POST https://panel.example.com/api/pwas \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{}' \
-w "\nHTTP %{http_code}\n"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer ВАШ_КЛЮЧ', 'Accept' => 'application/json'],
'timeout' => 30,
'http_errors' => false, // не бросать исключение на 4xx/5xx, разбираем ответ сами
]);
$response = $api->post('pwas', ['json' => []]);
$body = json_decode($response->getBody(), true);
if ($response->getStatusCode() >= 400) {
echo "Ошибка {$response->getStatusCode()}: {$body['message']}" . PHP_EOL;
foreach ($body['errors'] ?? [] as $field => $messages) {
echo " {$field}: " . implode(' ', $messages) . PHP_EOL;
}
exit(1);
}
echo "Создано PWA {$body['data']['pwa']['id']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer ВАШ_КЛЮЧ", "Accept": "application/json"}
response = requests.post(f"{API}/pwas", headers=HEADERS, json={}, timeout=30)
body = response.json()
if not response.ok:
print(f"Ошибка {response.status_code}: {body['message']}")
for field, messages in body.get("errors", {}).items():
print(f" {field}: {' '.join(messages)}")
raise SystemExit(1)
print("Создано PWA", body["data"]["pwa"]["id"])// Node.js 18+, без зависимостей; сохраните как script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer ВАШ_КЛЮЧ', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwas`, {
method: 'POST',
headers,
body: JSON.stringify({}),
signal: AbortSignal.timeout(30000),
});
const body = await response.json();
if (!response.ok) {
console.log(`Ошибка ${response.status}: ${body.message}`);
for (const [field, messages] of Object.entries(body.errors ?? {})) {
console.log(` ${field}: ${messages.join(' ')}`);
}
process.exit(1);
}
console.log('Создано PWA', body.data.pwa.id);Вывод:
Ошибка 422: The name field is required. (and 2 more errors)
name: The name field is required.
team_id: The team id field is required.
cloudflare_id: The cloudflare id field is required.Не повторяйте 4xx в цикле
Ошибки 4xx, кроме 429, сами не пройдут: повтор того же запроса даст тот же ответ. Повторять имеет смысл только 429 (через минуту) и ошибки сети или 5xx.
Лимиты
- 300 запросов в минуту на пользователя - на все методы вместе.
- 10 отправок пушей в минуту на пользователя (
POST/pwa_pushes/{id}/send) - каждая отправка - это настоящая рассылка. - 10 отказов
401по ключу в минуту с одного IP - неверный ключ, неподтверждённый аккаунт или API не в лицензии. Дальше любой запрос с ключом с этого IP получает429до конца минуты, отсчитанной от первого отказа, - даже с правильным ключом. Если скрипт поймал401- остановите его, а не повторяйте запрос.
Что доступно
| Раздел | Что можно |
|---|---|
| PWA | список, просмотр, создание, изменение, включение и выключение, клонирование, архивирование |
| Сплиты | список, просмотр, создание, изменение, включение и выключение, клонирование, архивирование |
| Потоки | список, просмотр, добавление PWA в сплит, изменение веса, удаление из сплита |
| Домены | список, добавление к PWA или сплиту, включение и пересоздание DNS, удаление |
| Пуши | список, просмотр, создание, изменение, предпросмотр, аудитория, отправка, клонирование, архивирование |
| Лендинги | список |
| Пиксели и теги | список, просмотр, добавление, изменение, архивирование |
| Статистика | дашборд, воронка, статистика по саб-меткам |
| Конверсии | список, просмотр, повторная отправка в Facebook |
| Экспорт | конверсии и визиты в CSV за период |
| Справочники | команды, аккаунты Cloudflare, аккаунты фильтрации и их потоки |