Документация API livesurf.ru
Авторизуйтесь или зарегистрируйтесь, чтобы получить API-ключ.
Войти или зарегистрироватьсяLiveSurf Client API
Дата обновления документации: 2026-07-04
Что это
REST API LiveSurf для управления аккаунтом, группами, страницами, источниками трафика и получения статистики.
- Base URL:
https://api.livesurf.ru - Формат: JSON
- Авторизация: заголовок
Authorization: <API_KEY> - Rate limit: не более
10запросов в секунду
Быстрый старт
Обязательные заголовки
Authorization: <API_KEY>
Accept: application/json
Content-Type: application/json
Пример GET
curl -sS "https://api.livesurf.ru/user/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json"
Пример POST
curl -sS -X POST "https://api.livesurf.ru/user/manualmode/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Пример PATCH
curl -sS -X PATCH "https://api.livesurf.ru/group/12345/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"name":"Updated group"}'
Пример PUT
curl -sS -X PUT "https://api.livesurf.ru/group/12345/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"name":"Full group update", "hour_limit": 100, "day_limit": 5000}'
Карта API
Справочники и лимиты
GET /categories/— список возможных категорийGET /countries/— список возможных странGET /languages/— список доступных языковGET /limits/— лимиты текущего аккаунта и справочные лимиты по типам аккаунтов
Источники
GET /sources/search/— список поисковых системGET /sources/ad/— список рекламных площадокGET /sources/messengers/— список мессенджеровGET /sources/social/— список социальных сетейGET /sources/neural/— список источников нейросетейGET /sources/recommender/— список источников рекомендательных систем
Пользователь
GET /user/— получение информации о пользователеPOST /user/automode/— включение режима АРК (автоматическая рекламная кампания)POST /user/manualmode/— включение ручного режима работы
Группы
GET /group/all/— информация о добавленных группахGET /group/{group_id}/— информация о конкретной группеPATCH /group/{group_id}/— частичное изменение настроек группыPUT /group/{group_id}/— полное обновление настроек группыDELETE /group/{group_id}/— удаление группыPOST /group/create/— создание новой группыPOST /group/{group_id}/clone/— клонирование группыPOST /group/{group_id}/add_credits/— зачисление кредитов посещений на счёт группы в ручном режиме работыPOST /group/{group_id}/refund_credits/— возврат всех кредитов с проекта на основной баланс в ручном режимеPOST /group/{group_id}/reorder/— изменение порядка страниц в группе
Страницы
GET /page/{page_id}/— информация о конкретной страницеPATCH /page/{page_id}/— частичное изменение настроек страницыPUT /page/{page_id}/— полное обновление настроек страницыDELETE /page/{page_id}/— удаление страницыPOST /page/create/— создание новой страницыPOST /page/{page_id}/clone/— клонирование страницыPOST /page/{page_id}/up/— перемещение страницы на позицию вверхPOST /page/{page_id}/down/— перемещение страницы на позицию внизPOST /page/{page_id}/start/— запуск страницы в работуPOST /page/{page_id}/stop/— остановка работы страницыGET /pages-compiled-stats/— статистика показа страницы
Стандарт ошибок
Чаще всего API возвращает ошибки в формате:
{
"errors": {
"field": ["message"]
}
}
или:
{
"errors": {
"detail": "message"
}
}
Лимиты и стоимость
GET /limits/
Назначение: вернуть лимиты и стоимость (pricing).
Параметры: нет.
Ответ содержит 4 блока:
general.timezone— таймзона, в которой работает API.limits— справочные лимиты по типам аккаунтов: Минимум (account_type_id: 0,min), Стандарт (1,pro), Премиум (2,vip).user.limits— фактические лимиты текущего пользователя с учетом индивидуальных расширений.pricing— стоимость посещения по showtime + модификаторы.
Важно: для валидации UI и запросов используйте user.limits.
Ключевые поля user.limits
min_showtime/max_showtime— диапазон каждого значенияpage.showtime(fromиtoвалидируются отдельно).min_daylimit/max_daylimit— диапазонgroup.day_limit; приmin_daylimit = 0разрешенday_limit = 0.min_hourlimit/max_hourlimit— диапазонgroup.hour_limit; приmin_hourlimit = 0разрешенhour_limit = 0.min_imp/max_imp— диапазон каждого значенияgroup.interval; приmin_imp = 0возможно отключение.max_keywords— максимум элементовsources.keywords.settings.list.max_backlinks— максимум элементовsources.backlinks.settings.list.max_selectors— максимум элементовbehavior.settings.clicks.listв режимеclicks.max_active_pages— число слотов одновременного запуска страниц.max_alternate_urls— число дополнительных URL; общий максимум URL на страницу:1 + max_alternate_urls.max_total_pages— максимум страниц на аккаунт.
Пример ответа:
{
"general": {
"timezone": "Europe/Moscow"
},
"limits": [
{
"account_type_id": 0,
"account_type": "min",
"min_showtime": 15,
"max_showtime": 45,
"min_daylimit": 0,
"max_daylimit": 200,
"min_hourlimit": 0,
"max_hourlimit": 50,
"min_imp": 0,
"max_imp": 10800,
"max_keywords": 50,
"max_backlinks": 50,
"max_selectors": 2,
"max_active_pages": 1,
"max_alternate_urls": 0,
"max_total_pages": 100
},
{
"account_type_id": 1,
"account_type": "pro",
"min_showtime": 15,
"max_showtime": 300,
"min_daylimit": 0,
"max_daylimit": 1000,
"min_hourlimit": 0,
"max_hourlimit": 200,
"min_imp": 0,
"max_imp": 10800,
"max_keywords": 100,
"max_backlinks": 100,
"max_selectors": 5,
"max_active_pages": 12,
"max_alternate_urls": 5,
"max_total_pages": 200
},
{
"account_type_id": 2,
"account_type": "vip",
"min_showtime": 15,
"max_showtime": 900,
"min_daylimit": 0,
"max_daylimit": 2000,
"min_hourlimit": 0,
"max_hourlimit": 501,
"min_imp": 0,
"max_imp": 10800,
"max_keywords": 300,
"max_backlinks": 300,
"max_selectors": 10,
"max_active_pages": 24,
"max_alternate_urls": 10,
"max_total_pages": 104
}
],
"pricing": {
"showtime_price": {
"15": 0.5,
"900": 499
},
"modifiers": [
{
"key": "geotargeting",
"type": "percentage",
"value": "0.30",
"description": "Геотаргетинг включён: +30%",
"enabled": true
},
{
"key": "low_pf",
"type": "percentage",
"value": "-0.70",
"description": "Трафик с низкими поведенческими факторами: -70%",
"enabled": true
}
]
},
"user": {
"limits": {
"account_type_id": 2,
"account_type": "vip",
"min_showtime": 15,
"max_showtime": 900,
"min_daylimit": 0,
"max_daylimit": 2000,
"min_hourlimit": 0,
"max_hourlimit": 501,
"min_imp": 0,
"max_imp": 10800,
"max_keywords": 300,
"max_backlinks": 300,
"max_selectors": 10,
"max_active_pages": 24,
"max_alternate_urls": 10,
"max_total_pages": 104
}
}
}
Справочники
GET /categories/
Назначение: категории для group.category.
Параметры: нет.
Пример ответа:
[
{
"id": 1,
"name": "Интернет, Компьютеры",
"parent": 0,
"active": true
}
]
GET /countries/
Назначение: страны для group.geo.
Параметры: нет.
Пример ответа:
[
{
"id": 1,
"country": "RU",
"region": "",
"city": "",
"name": "Россия"
}
]
GET /languages/
Назначение: языки для group.language.
Параметры: нет.
Пример ответа:
[
{
"id": 2,
"name": "Russian",
"translate_name": "Русский"
}
]
Источники
GET /sources/search/
Назначение: список поисковых систем для sources.keywords.settings.search_engines.
Параметры: нет.
Поля элемента ответа:
id— ID источника (используется как ключ вsources.keywords.settings.search_engines).name— название источника.default— вес по умолчанию (строка с числом).payload— объект с данными источника:iso(код страны/региона, например "RU", "BY", "KZ", "global"),str_id(строковый идентификатор, например "mail_ru", "ya_by", "rambler_ru").enable— доступность источника.
Пример ответа:
[
{
"id": 11,
"name": "Mail.RU",
"default": "0.8",
"payload": {
"iso": "RU",
"str_id": "mail_ru"
},
"enable": true
}
]
GET /sources/ad/
Назначение: список рекламных площадок для sources.adsystems.settings.
Параметры: нет.
Поля элемента ответа: name, default, payload, enable. В payload: iso (код страны/региона, например "RU", "global"), fullName (отображаемое название, например "Facebook", "Google AdSense", "Kavanga").
Пример ответа:
[
{
"name": "facebook",
"default": true,
"payload": {
"iso": "global",
"fullName": "Facebook"
},
"enable": true
}
]
GET /sources/messengers/
Назначение: список мессенджеров для sources.messengers.settings.
Параметры: нет.
Поля элемента ответа: name, default, payload, enable. В payload: iso (код страны/региона, например "global", "UA"), fullName (отображаемое название, например "Slack", "Viber", "WhatsApp").
Пример ответа:
[
{
"name": "telegram",
"default": true,
"payload": {
"iso": "global",
"fullName": "Telegram"
},
"enable": true
}
]
GET /sources/social/
Назначение: список соцсетей для sources.socialanalytics.settings.
Параметры: нет.
Поля элемента ответа: name, default, payload, enable. В payload: iso (код страны/региона, например "global", "RU", "CN"), fullName (отображаемое название, например "Instagram", "Pinterest", "VK", "Weibo", "Douyin", "Reddit").
Пример ответа:
[
{
"name": "pinterest",
"default": false,
"payload": {
"iso": "global",
"fullName": "Pinterest"
},
"enable": true
}
]
GET /sources/neural/
Назначение: список источников нейросетей для sources.neurals.settings.
Параметры: нет.
Поля элемента ответа: name, default, payload, enable. В payload: iso (код страны/региона, например "US"), fullName (отображаемое название, например "Poe", "Microsoft Edge Services", "Exa", "QuillBot", "Reka AI").
Пример ответа:
[
{
"name": "poe",
"default": false,
"payload": {
"iso": "US",
"fullName": "Poe"
},
"enable": true
}
]
GET /sources/recommender/
Назначение: список источников рекомендательных систем для sources.recommenders.settings.
Параметры: нет.
Поля элемента ответа: name, default, payload, enable. В payload: iso (код страны/региона, например "global", "RU", "CN"), fullName (отображаемое название, например "Opera Personal News", "Мир тесен", "Toutiao", "Дзен").
Пример ответа:
[
{
"name": "opera_personal_news",
"default": false,
"payload": {
"iso": "global",
"fullName": "Opera Personal News"
},
"enable": true
}
]
Пользователь
GET /user/
Назначение: получить параметры пользователя и текущий режим работы.
Параметры: нет.
Ключевые поля ответа:
credits— текущий баланс кредитов.workmode— режим работы (0ручной,1автоматический).type— ID типа аккаунта (справочно; для валидации лимитов использоватьGET /limits/ -> user.limits).experience— опыт пользователя.token— токен пользователя.is_active— активность аккаунта.
Пример ответа:
{
"credits": "170491620.33332",
"workmode": 0,
"type": 2,
"experience": 72009,
"token": "...",
"is_active": true
}
POST /user/automode/
Назначение: включить автоматический режим (АРК).
Параметры: нет.
Пример ответа:
{"status": 1}
POST /user/manualmode/
Назначение: включить ручной режим.
Параметры: нет.
Пример ответа:
{"status": 1}
Группы
GET /group/all/
Назначение: список всех групп аккаунта с полной конфигурацией и вложенными страницами.
Параметры: нет.
Пример ответа:
[
{
"id": 123,
"name": "My group",
"hour_limit": 50,
"day_limit": 1000,
"interval": [30, 180],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"autocalc_visits": {
"enabled": false,
"lower_at_night": false,
"lower_at_week": false
},
"use_profiles": true,
"retention": true,
"description": "",
"timezone": "Europe/Moscow",
"category": 1,
"language": 2,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"schedules": [],
"low_pf": {"enabled": false, "ratio": 30},
"sources": {
"keywords": {"value": 50, "enabled": true, "settings": {"list": ["пример фразы"], "search_engines": {"1": 1}}},
"adsystems": {"value": 20, "enabled": true, "settings": ["B2BContext"]},
"backlinks": {"value": 10, "enabled": true, "settings": {"list": ["https://example.com"]}},
"messengers": {"value": 5, "enabled": true, "settings": ["telegram"]},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {"value": 5, "enabled": true, "settings": ["pinterest"]},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
},
"pages": [
{
"id": 999,
"state": 1,
"position": 0,
"url": ["https://example.com/"],
"showtime": [15, 30],
"break_chain": 0,
"adult": false,
"group_id": 123,
"behavior": {"mode": "disabled", "settings": {"reading_up": false, "clicks": {"list": []}}}
}
],
"credits": 0
}
]
GET /group/{group_id}/
Назначение: полная конфигурация конкретной группы.
Параметры URL:
group_id— ID группы.
Примечание: список group_id можно получить через GET /group/all/.
Пример ответа:
{
"id": 123,
"name": "My group",
"hour_limit": 50,
"day_limit": 1000,
"interval": [30, 180],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"stopping_hours": [1, 2, 3],
"autocalc_visits": {
"enabled": false,
"lower_at_night": false,
"lower_at_week": false
},
"use_profiles": true,
"retention": true,
"description": "",
"timezone": "Europe/Moscow",
"category": 1,
"language": 2,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"schedules": [],
"low_pf": {"enabled": false, "ratio": 30},
"sources": {
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["пример фразы"],
"search_engines": {"1": 1}
}
},
"adsystems": {"value": 20, "enabled": true, "settings": ["B2BContext"]},
"backlinks": {"value": 10, "enabled": true, "settings": {"list": ["https://example.com"]}},
"messengers": {"value": 5, "enabled": true, "settings": ["telegram"]},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {"value": 5, "enabled": true, "settings": ["pinterest"]},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
},
"pages": [
{
"id": 999,
"state": 1,
"position": 0,
"url": ["https://example.com/"],
"showtime": [15, 30],
"break_chain": 0,
"adult": false,
"group_id": 123,
"behavior": {
"mode": "neural",
"settings": {"reading_up": true, "clicks": {"list": ["a", ".link"]}}
}
}
],
"credits": 0
}
PATCH /group/{group_id}/
Назначение: частичное обновление группы. Обновляются только переданные поля, остальные поля группы остаются без изменений. Валидация применяется только к переданным полям.
Параметры URL:
group_id— ID группы.
Тело запроса: любой поднабор полей ниже.
Основные поля:
name(string) — название группы, до 255 символов.hour_limit(int) — часовой лимит в диапазонеmin_hourlimit..max_hourlimitиз/limits/; еслиmin_hourlimit = 0, допускается0.day_limit(int) — суточный лимит в диапазонеmin_daylimit..max_daylimit; еслиmin_daylimit = 0, допускается0.interval(array[int,int]) — интервал между показами[from, to], каждое значение ограниченоmin_imp..max_imp.uniq_ip(int) — уникальность IP в часах (0выключено, максимум168).moby_ratio(int) — доля mobile трафика0..100.geo(array[int]) — список стран (GET /countries/).autocalc_visits(object) — авторасчет лимитов:enabled(bool)lower_at_night(bool) — снижать посещения в ночное время до 10% относительно часового пояса группыlower_at_week(bool)
use_profiles(bool) — профили посетителей с активной поисковой историей, для улучшения поведенческих факторов.retention(bool) — удержание.description(string) — описание до 100 символов.timezone(string) — TZID, напримерEurope/Moscow.stopping_hours(array[int]) — часы недели,1..168.category(int) — ID категории (GET /categories/).language(int) — ID языка (GET /languages/).bookmarks(array[int,int]) — диапазон переходов из закладок.autolimit(array[int,int]) — диапазон автоизменения суточного лимита (-500..500).low_pf(object) — трафик с низкими ПФ:enabled(bool)ratio(int)
schedules(array) — расписание (VIP): элементы вида[state, "dd.mm.yyyy HH:MM"]; state:0— пауза,1— возобновить показ. Пример:[[1, "07.02.2026 14:00"], [0, "08.02.2026 10:00"]].- Таймзона: дата интерпретируется в таймзоне группы (поле
timezone). Еслиtimezoneпередан в том же запросе — используется он, иначе берётся уже сохранённый у группы, иначеEurope/Moscow. Ответ API форматирует дату в той же таймзоне группы. - Гранулярность — час: минуты входных данных округляются вниз до начала часа (
14:37сохранится и сработает как14:00).
- Таймзона: дата интерпретируется в таймзоне группы (поле
sources(object) — настройки источников посещений.
Структура sources:
keywords:value(int),enabled(bool)settings.list(array[string]) — максимумmax_keywordssettings.search_engines(map[string,float]) — ключи: id изGET /sources/search/; значения: float от 0 до 1 (соотношение использования поисковой системы)
adsystems:value(int),enabled(bool)settings(array[string]) — значения изGET /sources/ad/
backlinks:value(int),enabled(bool)settings.list(array[string]) — максимумmax_backlinks
messengers:value(int),enabled(bool)settings(array[string]) — изGET /sources/messengers/
clickunders:value(int),enabled(bool)emailanalytics:value(int),enabled(bool)socialanalytics:value(int),enabled(bool)settings(array[string]) — изGET /sources/social/
neurals:value(int),enabled(bool)settings(array[string]) — изGET /sources/neural/
recommenders:value(int),enabled(bool)settings(array[string]) — изGET /sources/recommender/
qrcodes:value(int),enabled(bool)
Примеры структур по источникам
keywords (переходы из поисковых систем):
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["пример фразы", "другая фраза"],
"search_engines": {"1": 0.7, "2": 0.3}
}
}
adsystems (переходы из рекламных систем):
"adsystems": {
"value": 20,
"enabled": true,
"settings": ["B2BContext", "adfox"]
}
backlinks (переходы по прямым ссылкам):
"backlinks": {
"value": 10,
"enabled": true,
"settings": {
"list": ["https://example.com", "https://example.com/page"]
}
}
messengers (переходы из мессенджеров):
"messengers": {
"value": 5,
"enabled": true,
"settings": ["telegram", "whatsapp"]
}
clickunders (переходы с кликандеров):
"clickunders": {
"value": 5,
"enabled": true
}
emailanalytics (переходы с почтовых рассылок):
"emailanalytics": {
"value": 5,
"enabled": true
}
socialanalytics (переходы из социальных сетей):
"socialanalytics": {
"value": 5,
"enabled": true,
"settings": ["pinterest", "instagram"]
}
neurals (переходы из нейросетей):
"neurals": {
"value": 5,
"enabled": true,
"settings": ["chatgpt", "poe"]
}
recommenders (переходы из рекомендательных систем):
"recommenders": {
"value": 5,
"enabled": true,
"settings": ["dzen", "opera_personal_news"]
}
qrcodes (переходы через QR-коды):
"qrcodes": {
"value": 5,
"enabled": true
}
Пример запроса:
curl -sS -X PATCH "https://api.livesurf.ru/group/123/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{
"hour_limit": 100,
"day_limit": 10000,
"interval": [60, 300],
"autocalc_visits": {"enabled": true, "lower_at_night": true, "lower_at_week": false},
"sources": {
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["Поисковая фраза"],
"search_engines": {"1": 1}
}
},
"adsystems": {
"value": 50,
"enabled": true,
"settings": ["B2BContext"]
}
}
}'
Пример ответа: структура как у GET /group/{group_id}/ (все поля группы).
Пример полной конфигурации (тело для PATCH или фрагмент для PUT):
{
"name": "My group",
"hour_limit": 100,
"day_limit": 10000,
"interval": [60, 300],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"timezone": "Europe/Moscow",
"stopping_hours": [1, 2, 4, 5, 7, 9],
"autocalc_visits": {
"enabled": true,
"lower_at_night": true,
"lower_at_week": false
},
"use_profiles": true,
"retention": true,
"description": "",
"category": 1,
"language": 1,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"low_pf": {"enabled": false, "ratio": 30},
"schedules": [
[1, "07.02.2026 14:17"],
[0, "08.02.2026 10:00"]
],
"sources": {
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["Поисковая фраза"],
"search_engines": {"1": 1}
}
},
"adsystems": {
"value": 20,
"enabled": true,
"settings": ["B2BContext"]
},
"backlinks": {
"value": 10,
"enabled": true,
"settings": {"list": ["https://example.com"]}
},
"messengers": {
"value": 5,
"enabled": true,
"settings": ["telegram"]
},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {
"value": 5,
"enabled": true,
"settings": ["pinterest"]
},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
}
}
PUT /group/{group_id}/
Назначение: полное обновление группы. В отличие от PATCH, метод PUT заменяет все настройки группы. Поля, не указанные в теле запроса, будут сброшены в значения по умолчанию. Валидация применяется ко всем полям.
Параметры URL:
group_id— ID группы.
Тело запроса: те же поля, что и в PATCH /group/{group_id}/.
Пример запроса:
curl -sS -X PUT "https://api.livesurf.ru/group/123/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{
"name": "My group",
"hour_limit": 100,
"day_limit": 10000,
"interval": [60, 300],
"uniq_ip": 0,
"moby_ratio": 0,
"geo": [1],
"timezone": "Europe/Moscow",
"autocalc_visits": {"enabled": false, "lower_at_night": false, "lower_at_week": false},
"sources": {
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["Поисковая фраза"],
"search_engines": {"1": 1}
}
}
}
}'
Пример ответа: структура как у GET /group/{group_id}/ (все поля группы).
DELETE /group/{group_id}/
Назначение: удалить группу и все вложенные страницы.
Параметры URL:
group_id— ID группы.
Пример ответа:
{"status": 1}
POST /group/{group_id}/clone/
Назначение: клонировать группу вместе со всеми её страницами.
Параметры URL:
group_id— ID группы.
Тело запроса:
name(string, опционально) — имя новой группы.
Пример ответа: полная структура как у GET /group/{group_id}/. Пример:
{
"id": 456,
"name": "My group copy 2",
"hour_limit": 50,
"day_limit": 1000,
"interval": [30, 180],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"stopping_hours": [],
"autocalc_visits": {"enabled": false, "lower_at_night": false, "lower_at_week": false},
"use_profiles": true,
"retention": true,
"description": "",
"timezone": "Europe/Moscow",
"category": 1,
"language": 2,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"schedules": [],
"low_pf": {"enabled": false, "ratio": 30},
"sources": {
"keywords": {"value": 50, "enabled": true, "settings": {"list": ["пример фразы"], "search_engines": {"1": 1}}},
"adsystems": {"value": 20, "enabled": true, "settings": ["B2BContext"]},
"backlinks": {"value": 10, "enabled": true, "settings": {"list": ["https://example.com"]}},
"messengers": {"value": 5, "enabled": true, "settings": ["telegram"]},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {"value": 5, "enabled": true, "settings": ["pinterest"]},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
},
"pages": [
{"id": 1001, "state": 0, "position": 0, "url": ["https://example.com/"], "showtime": [15, 30], "break_chain": 0, "adult": false, "group_id": 456, "behavior": {"mode": "disabled", "settings": {"reading_up": false, "clicks": {"list": []}}}},
{"id": 1002, "state": 0, "position": 1, "url": ["https://example.com/page2"], "showtime": [15, 30], "break_chain": 0, "adult": false, "group_id": 456, "behavior": {"mode": "disabled", "settings": {"reading_up": false, "clicks": {"list": []}}}}
],
"credits": 0
}
POST /group/{group_id}/add_credits/
Назначение: зачислить кредиты на проект в ручном режиме.
Параметры URL:
group_id— ID группы.
Тело запроса:
credits(int) — количество зачисляемых кредитов. Допускаются только положительные значения (больше 0). Метод доступен только в ручном режиме работы.
Пример запроса:
curl -sS -X POST "https://api.livesurf.ru/group/123/add_credits/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"credits": 100}'
Пример ответа:
{"status": 1}
POST /group/{group_id}/refund_credits/
Назначение: вернуть все кредиты проекта на основной баланс аккаунта (ручной режим).
Параметры URL:
group_id— ID группы.
Параметры тела: нет.
Пример ответа:
{"status": 1}
POST /group/{group_id}/reorder/
Назначение: изменить порядок страниц в группе. Порядок задаётся списком page_ids (первый элемент — position 0, второй — 1 и т.д.). После успеха GET /group/{group_id}/ и GET /group/all/ возвращают страницы в новом порядке. При неверном списке (не массив, пустой, есть ID не из группы, неполный или с дублями) возвращается 400 с полем errors.page_ids.
Параметры URL:
group_id— ID группы.
Тело запроса:
page_ids(array of int) — полный список ID страниц группы без дублей, в нужном порядке. Должен содержать ровно все страницы группы.
Пример запроса:
curl -sS -X POST "https://api.livesurf.ru/group/12345/reorder/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"page_ids": [102, 101, 103]}'
Пример ответа:
{"id": 12345}
POST /group/create/
Назначение: создать группу с вложенными страницами.
Поля тела запроса:
- Поддерживаются те же поля конфигурации группы, что и в
PATCH /group/{group_id}/. - Дополнительно:
pages(array) — список создаваемых страниц.- Для каждого элемента
pagesиспользуются поля изPOST /page/create/.
Важные ограничения:
sources.keywords.settings.list<=max_keywords.sources.backlinks.settings.list<=max_backlinks.- Общее число страниц аккаунта <=
max_total_pages.
Пример запроса (полная конфигурация с несколькими страницами и всеми источниками):
{
"name": "Группа 27",
"hour_limit": 50,
"day_limit": 10000,
"interval": [60, 300],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"timezone": "Europe/Moscow",
"autocalc_visits": {
"enabled": false,
"lower_at_night": false,
"lower_at_week": false
},
"use_profiles": true,
"retention": true,
"category": 1,
"language": 1,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"schedules": [
[1, "07.02.2026 14:17"],
[0, "08.02.2026 10:00"]
],
"pages": [
{
"url": ["https://example.com/", "https://example.com/page2"],
"showtime": [15, 30],
"state": 0,
"break_chain": 10
},
{
"url": ["https://example.com/page3", "https://example.com/page4"],
"showtime": [15, 30],
"state": 0,
"break_chain": 0
}
],
"sources": {
"keywords": {
"value": 50,
"enabled": true,
"settings": {
"list": ["ключевая фраза 1", "ключевая фраза 2"],
"search_engines": {"1": 1, "2": 0.5}
}
},
"adsystems": {
"value": 20,
"enabled": true,
"settings": ["B2BContext"]
},
"backlinks": {
"value": 10,
"enabled": true,
"settings": {"list": ["https://example.com"]}
},
"messengers": {
"value": 5,
"enabled": true,
"settings": ["telegram"]
},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {
"value": 5,
"enabled": true,
"settings": ["pinterest"]
},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
}
}
Пример ответа: полная структура как у GET /group/{group_id}/. Пример:
{
"id": 789,
"name": "Группа 27",
"hour_limit": 50,
"day_limit": 10000,
"interval": [60, 300],
"uniq_ip": 0,
"moby_ratio": 50,
"geo": [1, 2],
"stopping_hours": [],
"autocalc_visits": {"enabled": false, "lower_at_night": false, "lower_at_week": false},
"use_profiles": true,
"retention": true,
"description": "",
"timezone": "Europe/Moscow",
"category": 1,
"language": 1,
"bookmarks": [10, 40],
"autolimit": [-10, 10],
"schedules": [],
"low_pf": {"enabled": false, "ratio": 30},
"sources": {
"keywords": {"value": 50, "enabled": true, "settings": {"list": ["ключевая фраза 1"], "search_engines": {"1": 1}}},
"adsystems": {"value": 20, "enabled": true, "settings": ["B2BContext"]},
"backlinks": {"value": 10, "enabled": true, "settings": {"list": ["https://example.com"]}},
"messengers": {"value": 5, "enabled": true, "settings": ["telegram"]},
"clickunders": {"value": 5, "enabled": true},
"emailanalytics": {"value": 5, "enabled": true},
"socialanalytics": {"value": 5, "enabled": true, "settings": ["pinterest"]},
"neurals": {"value": 0, "enabled": false, "settings": []},
"recommenders": {"value": 0, "enabled": false, "settings": []},
"qrcodes": {"value": 0, "enabled": false}
},
"pages": [
{"id": 1001, "state": 0, "position": 0, "url": ["https://example.com/", "https://example.com/page2"], "showtime": [15, 30], "break_chain": 10, "adult": false, "group_id": 789, "behavior": {"mode": "disabled", "settings": {"reading_up": false, "clicks": {"list": []}}}}
],
"credits": 0
}
Пример ошибки валидации (400):
{
"errors": {
"pages": ["Ensure this field has at least 1 elements."]
}
}
Страницы
GET /page/{page_id}/
Назначение: получить конфигурацию страницы.
Параметры URL:
page_id— ID страницы.
Примечание: ID страниц можно получить через GET /group/{group_id}/.
Пример ответа:
{
"id": 1086337,
"state": 1,
"position": 3,
"url": ["https://example.com/"],
"showtime": [15, 45],
"break_chain": 10,
"adult": false,
"group_id": 123,
"behavior": {
"mode": "neural",
"settings": {
"reading_up": true,
"clicks": {
"list": ["a", ".css-class", "#css-id"]
}
}
}
}
PATCH /page/{page_id}/
Назначение: частично обновить страницу. Обновляются только переданные поля.
Параметры URL:
page_id— ID страницы.
Основные поля тела:
state(int):0пауза,1работа.break_chain(int): вероятность прерывания цепочки (0–100).url(array[string]): список URL на одном домене, максимум1 + max_alternate_urls.showtime(array[int,int]):[from, to], каждое значение отдельно ограниченоmin_showtime..max_showtime.behavior(object): настройки поведения.
behavior.mode:
disabled— поведение отключено.clicks— клик по заданным селекторам.fixation— фиксация посетителя кликом в конце.neural— поведение генерируется нейросетью.manual— ручная настройка поведения через JSON-сценарий действий.
behavior.settings:
reading_up(bool) — дочитывание: добавляет поведение доскролла до нижней части страницы в любом режиме, кроме режима ручной настройки поведения.clicks.list(array[string]) — CSS селекторы для режимаclicks, максимумmax_selectors. Клик выполняется по одному из селекторов списка, выбранному случайным образом в начале посещения.
behavior.manual (только для mode = "manual"):
actions(object) — JSON-сценарий действий на странице. Структуру и справочник всех действий см. в разделе Поведение в режимеmanualниже.
Пример запроса:
curl -sS -X PATCH "https://api.livesurf.ru/page/1086337/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{
"url": ["https://example.com/page_1.html", "https://example.com/page_2.html"],
"break_chain": 10,
"showtime": [30, 45],
"behavior": {
"mode": "neural",
"settings": {
"reading_up": true,
"clicks": {"list": ["a", "span", "#awesome_button"]}
}
},
"state": 1
}'
Пример ответа:
{"id": 1086337}
PUT /page/{page_id}/
Назначение: полное обновление страницы. В отличие от PATCH, метод PUT заменяет все настройки страницы. Поля, не указанные в теле запроса, будут сброшены в значения по умолчанию. Валидация применяется ко всем полям.
Параметры URL:
page_id— ID страницы.
Тело запроса: те же поля, что и в PATCH /page/{page_id}/.
Пример ответа:
{"id": 1086337}
DELETE /page/{page_id}/
Назначение: удалить страницу. Если в группе остаётся 0 страниц, группа тоже удаляется.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"status": 1}
POST /page/create/
Назначение: создать новую страницу.
Поля тела:
group_id(int) — ID группы.state(int) —0/1.break_chain(int) — вероятность прерывания (0–100).url(array[string]) — до1 + max_alternate_urls.showtime(array[int,int]) — каждое значение вmin_showtime..max_showtime.behavior(object) — настройки поведения.
Пример запроса:
{
"group_id": 123,
"state": 0,
"break_chain": 10,
"url": ["https://example.com/", "https://example.com/page2"],
"showtime": [15, 30],
"behavior": {
"mode": "disabled",
"settings": {
"reading_up": true,
"clicks": {
"list": ["a"]
}
}
}
}
Пример ответа:
{"id": 1}
Пример ошибки валидации (400):
{
"errors": {
"group_id": "Group with group_id=0 does not exist"
}
}
POST /page/{page_id}/clone/
Назначение: клонировать страницу в рамках группы и поместить новую страницу в конец списка страниц.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"id": 1087000}
POST /page/{page_id}/up/
Назначение: поднять страницу на 1 позицию вверх в списке.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"status": 1}
POST /page/{page_id}/down/
Назначение: опустить страницу на 1 позицию вниз в списке.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"status": 1}
POST /page/{page_id}/start/
Назначение: запустить страницу.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"status": 1}
POST /page/{page_id}/stop/
Назначение: остановить страницу.
Параметры URL:
page_id— ID страницы.
Пример ответа:
{"status": 1}
Поведение в режиме manual
Режим manual (behavior.mode = "manual") — это сценарий поведения, который браузер пошагово выполняет на странице. Сценарий описывается как граф из блоков: один блок — одно действие (подождать, кликнуть, прочитать текст, проверить условие). Блоки связаны переходами и запускаются триггерами.
Раздел длинный. Чтобы быстрее начать — см. Минимальный пример и затем Готовые рецепты.
Как это работает
Пример сценария «дождаться кнопку .cta и кликнуть» в виде блок-схемы:
flowchart TD
A([Триггер afterload]) --> B["wait — пауза 1–2 сек"]
B -->|next| C{"Кнопка .cta видна?"}
C -->|success| D["clickTo — клик по .cta"]
C -->|failure| E["moveToDown — проскроллить вниз"]
E -->|next| C
D --> F([Конец цепочки])
Сценарий запускается триггером (afterload — после загрузки страницы, beforeendtask — перед завершением задания) и идёт от блока к блоку по полю next. У condition вместо next — два именованных выхода success и failure.
Кроме триггеров, цепочку могут запускать ещё два вида точек входа — они работают так же (указывают стартовый блок), но реагируют не на загрузку страницы, а на другие сигналы:
flowchart LR
T([Триггер<br/>afterload / beforeendtask]) --> B1["блок-старт"]
E([Событие<br/>events ← emitEvent]) --> B2["блок-старт"]
W([Наблюдатель<br/>watches: переменная пересекла порог]) --> B3["блок-старт"]
B1 -.-> N["… цепочка блоков по next …"]
B2 -.-> N
B3 -.-> N
triggers— запуск по жизненному циклу страницы (есть и в прошлом API).events— запуск по пользовательскому событию, которое испускает блокemitEvent(см.events). Удобно, когда один обработчик нужен из нескольких мест графа.watches— запуск, когда значение переменной пересекает порог (см.watches).
Все три — необязательны, и граф может быть незавершённым: точка входа без подключённого блока (action: null) или блок без next сохраняются нормально. Бэкенд проверяет лишь корректность самих значений, а не достижимость каждого узла.
Минимальный пример
Подождать пару секунд после загрузки и кликнуть по баннеру cookies:
{
"behavior": {
"mode": "manual",
"manual": {
"actions": {
"triggers": {
"afterload": [{"enter": ["*"], "action": "wait"}]
},
"blocks": [
{"id": "wait", "action": "wait", "value": [1500, 3000], "next": ["click"]},
{"id": "click", "action": "clickTo",
"value": {"selector": [".cookies-accept"],
"modifiers": {"button": "left", "count": 1, "keys": [], "hold": false}}}
]
}
}
}
}
Здесь видно всё ключевое: триггер afterload запускает блок wait, тот передаёт управление по next в clickTo, который кликает по селектору. Дальше — добавляем блоки и переходы под нужный сценарий.
Структура actions
behavior.manual.actions — объект с секциями:
{
"variables": { /* переменные сценария */ },
"sharedVariables": { /* переменные, общие между вкладками задания */ },
"triggers": { /* точки входа по загрузке: afterload, beforeendtask */ },
"events": [ /* точки входа по пользовательскому событию */ ],
"watches": [ /* точки входа по изменению переменной */ ],
"blocks": [ /* массив блоков-действий */ ]
}
| Поле | Обязательное | Что это |
|---|---|---|
variables |
нет | Именованные хранилища значений сценария. |
sharedVariables |
нет | То же, но значение общее для всех вкладок визита задания. |
triggers |
нет | Точки входа по жизненному циклу страницы: afterload, beforeendtask. |
events |
нет | Точки входа по пользовательскому событию (его испускает блок emitEvent). |
watches |
нет | Точки входа по изменению переменной (значение пересекло порог). |
blocks |
да | Сами действия. Должен быть хотя бы один блок. |
Глоссарий
| Термин | Что это |
|---|---|
| Блок | Один шаг сценария: wait, clickTo, condition и т.п. У каждого свой id. |
Действие (action) |
Тип блока. Строка из справочника ниже. |
| Триггер | Точка входа по загрузке страницы: какое событие жизненного цикла запускает какой блок. |
next |
Куда идти после блока (массив id блоков-получателей, обычно один). |
success / failure |
Именованные выходы у condition вместо next. |
| Переменная | Именованное хранилище значений, доступное через подстановки. |
Общая переменная (sharedVariables) |
Переменная, значение которой общее для всех вкладок/заданий одного визита. |
Событие / emitEvent |
Пользовательский сигнал; блок emitEvent его испускает, приёмник в events — ловит и запускает цепочку. |
Наблюдатель (watches) |
Точка входа, срабатывающая когда значение переменной пересекает заданный порог. |
| Системная переменная | Значение, которое подставляет исполнитель (domain, tab_id, time …); сохранять её в графе нельзя, ссылаться — можно. |
variables — переменные
Плоский объект { "<имя>": <начальное_значение> }. До 20 переменных.
- Имя — юникод-буквы, цифры,
_и-; длина 3–15 символов (regex^[\p{L}\d_-]+$). Подходятcounter,my_var,kebab-x,счётчик. - Значение — число (
int) или строка. Тип переменной выводится по JSON-типу начального значения. Для строки charset не ограничен (общий лимит — 25 KiB на весьactions). - Имя не должно совпадать с системной переменной.
"variables": {"counter": 0, "phrase": "hello", "счётчик": 0}
sharedVariables — общие переменные
Та же форма и те же правила, что у variables. Отличие — значение общее для всех вкладок одного визита задания: то, что одна вкладка записала, видят остальные. Удобно для счётчиков и лимитов на весь визит.
"sharedVariables": {"totalDone": 0}
- Одно и то же имя не может одновременно встречаться в
variablesиsharedVariables— это ошибка сохранения.
Имя переменной уникально в объединении
variables∪sharedVariables: одно и то же имя в двух секциях сразу — ошибка.
В чём разница на практике: обычная переменная у каждой вкладки своя, общая — одна на весь визит.
flowchart TB
subgraph V["variables — у каждой вкладки свой экземпляр"]
A["Вкладка 1<br/>counter = 3"]
B["Вкладка 2<br/>counter = 1"]
end
subgraph S["sharedVariables — одно значение на весь визит"]
C["Вкладка 1"] --> H[("totalDone = 4")]
D["Вкладка 2"] --> H
end
Системные переменные
Создаются исполнителем в рантайме. Сохранять их в графе нельзя — ни в variables, ни в sharedVariables (редактор вырезает их при экспорте). Попытка сохранить граф с таким именем — ошибка. А вот ссылаться на них из узлов можно: через подстановку ({tab_id}, livesurf.ru), как источник wait/scrollPercent, как наблюдаемую в watches.
| Имя | Тип | Смысл |
|---|---|---|
time |
int | секунд с момента готовности страницы (DOM ready) |
lsc_timer |
int | внутренний таймер исполнителя |
domain |
string | домен текущей страницы |
device |
string | desktop / mobile |
tab_id |
string | уникальный id вкладки |
viewport_w |
int | ширина экрана устройства |
viewport_h |
int | высота экрана устройства |
:last-element |
string | CSS-путь последнего элемента взаимодействия (см. getElement) |
triggers — точки входа
Триггер — это вход в граф. Он отвечает на вопросы «когда» (afterload / beforeendtask) и «куда» (с какого id стартовать).
"triggers": {
"afterload": [{"enter": ["*"], "action": "start"}],
"beforeendtask": [{"enter": ["*"], "action": "wrapup"}]
}
| Триггер | Когда срабатывает |
|---|---|
afterload |
После полной загрузки URL страницы. |
beforeendtask |
Перед завершением задания посещения. |
Поля точки входа:
enter(array[string]) — для каких URL применяется триггер. Непустой массив; каждый паттерн — до 255 символов.["*"]— любой URL текущей страницы; либо массив конкретных URL — триггер сработает только на них (страница может содержать несколько URL: основной + альтернативные).action(string | null) —idблока, с которого стартует цепочка.null— триггер пока не подключён (граф можно сохранить незавершённым); заданныйidобязан существовать средиblocks.id(string, optional) — id карточки в редакторе; на исполнение не влияет.
events — события
Точка входа, которая срабатывает по пользовательскому событию. Блок emitEvent испускает событие по имени, а приёмник в events ловит его и запускает свою цепочку.
Приёмник у каждого имени события один, а вот эмиттеров — сколько угодно: один и тот же сигнал можно слать из разных мест графа, а обрабатывать в одном месте. Это и есть «единый обработчик». Плюс у приёмника есть режим (mode): запускать обработчик только раз за задание, не давать ему накладываться сам на себя и т.п. — сколько бы раз сигнал ни прилетел.
flowchart LR
A["emitEvent<br/>event: modal"] -->|сигнал| EV(("событие<br/>modal"))
B["другой emitEvent<br/>event: modal"] -->|сигнал| EV
EV -->|"приёмник events (один)"| R["action → close"]
R --> H["… закрыть модалку …"]
"events": [
{"id": "ev1", "event": "modal", "mode": "every", "action": "closeModal"}
]
| Поле | Тип | Правило |
|---|---|---|
event |
string | имя события; непустое, до 30 символов, charset ^[\p{L}\d_-]+$. |
mode |
enum | как часто реагировать (см. ниже). По умолчанию every. |
action |
string | null | id блока-старта (или null, если приёмник пока не подключён). |
id |
string, optional | id карточки в редакторе. |
Режимы mode:
| Значение | Смысл |
|---|---|
every |
реагировать на каждое событие (по умолчанию). |
once_tab |
один раз за вкладку. |
once_task |
один раз за задание. |
one_at_a_time |
не запускать новую цепочку, пока выполняется предыдущая. |
in_turn |
обрабатывать события по очереди. |
Одно событие — один приёмник. Двух элементов
eventsс одинаковымeventбыть не может. Эмиттеров (emitEvent) с этим именем — наоборот, может быть сколько угодно. Хотите из приёмника несколько действий — ведите цепочку черезnextот стартового блока.
watches — наблюдатели за переменными
Точка входа, которая срабатывает, когда значение переменной пересекает порог. Удобно для лимитов и реакций «как только насчитали N — сделать X».
"watches": [
{"id": "w1", "variable": "counter", "sign": "<", "value": 5, "mode": "every", "action": "retry"}
]
| Поле | Тип | Правило |
|---|---|---|
variable |
string | имя наблюдаемой переменной (пользовательской или системной). |
sign |
enum | <, > или =. |
value |
number | string | порог. Число — в диапазоне −1 000 000…1 000 000; строка — до 100 символов. |
mode |
enum | every (по умолчанию) или once_tab — только эти два. |
action |
string | null | id блока-старта (или null). |
id |
string, optional | id карточки в редакторе. |
Переменная может быть ещё не объявлена в
variables— граф можно сохранить незавершённым. Поэтомуvalueпроверяется мягко (число в диапазоне или строка ≤100), без привязки к типу переменной.
Одна переменная — один наблюдатель. Двух элементов
watchesна одну и ту жеvariableбыть не может.
Наблюдатель срабатывает в момент, когда условие становится истинным, и прерывает текущую цепочку вкладки, запуская свою:
flowchart LR
S["где-то в графе:<br/>clicksTotal стало 51"] --> V{"watches:<br/>clicksTotal > 50 ?"}
V -->|стало истинным| W["прервать и запустить<br/>action → stop"]
V -->|ещё нет| X["вкладка работает дальше"]
Если наблюдают за общей 🌐-переменной, сработать может цепочка другой вкладки — той, что меняла значение, и той, что следит, может быть разной. За локальной — вкладка реагирует на собственное изменение.
blocks — блоки действий
{
"id": "<уникальный_id>",
"action": "<имя_действия>",
"value": <данные_действия>,
"next": ["<id_следующего>"]
}
id— уникальный в рамках сценария,[a-zA-Z0-9]{1,15}.action— название действия из справочника ниже.value— параметры. ДляmoveToDown,moveToUp,random,endотсутствует (или{}). Для остальных — обязательно.next—id-получатели по обычному выходу. 0..10 элементов (пустой массив = конец цепочки). Уconditionполяnextнет — естьsuccess/failureвнутриvalue.
Подстановки и :last-element
В строках CSS-селекторов и текстовых значениях ряда действий доступны подстановки:
livesurf.ru— текущий домен страницы.{<имя_переменной>}— значение переменной сценария.
Пример: «положить значение переменной baz в input, чей id определяется значением переменной bar»:
{"id": "step", "action": "setAttribute",
"value": {"selector": ["#{bar}"], "attr": "value", "variable": "baz"}}
Псевдо-селектор :last-element — последний элемент, найденный действием getElement. Удобно когда селектор сложный или одноразовый: нашли один раз, потом ссылаемся как на :last-element.
Лимиты (сводно)
| Где | Лимит |
|---|---|
Размер всего actions (UTF-8) |
25 KiB |
id блока |
[a-zA-Z0-9], до 15 символов |
Имя переменной (variables / sharedVariables) |
[\p{L}\d_-], 3..15 символов |
| Количество переменных | до 20 в каждой секции |
Имя события (events / emitEvent) |
[\p{L}\d_-], 1..30 символов |
triggers.enter — один паттерн |
до 255 символов |
watches.value — число / строка |
−1 000 000..1 000 000 / до 100 символов |
httpGet/httpPost url |
валидный http/https, до 2048 символов |
httpGet.map / httpPost.send |
до 20 / 1..20 элементов |
scrollPercent.percent |
0..100 |
next.length |
0..10 |
Количество селекторов в selector |
до 100 |
| Длина одного CSS-селектора | 1..500 символов |
Длина имени атрибута (attr) |
до 25 символов |
Справочник действий
Действия сгруппированы по назначению. По умолчанию массив selector принимает от 1 до 100 элементов; конкретные нюансы (мин-количество, опциональность) отмечены в каждом действии.
Поиск вглубь. Если в верхнем документе страницы совпадений по селектору нет, поиск автоматически продолжается внутри closed shadow root и фреймов (в т.ч. кросс-доменных) — там часто прячут чекбоксы капч. Комбинаторы (потомок) и > границы shadow/iframe не пересекают: поиск идёт внутри каждого дерева отдельно (например, iframe a не найдёт a внутри элемента <iframe>).
allowInvisible — работа с невидимыми элементами (опционально; поддержка движка в процессе внедрения). Поле value.allowInvisible (boolean, по умолчанию false) у действий, работающих с ОДНИМ элементом: clickTo, moveTo, scrollTo, focus, getElement и condition в режиме selector. Управляет проверкой видимости целевого элемента:
| Состояние элемента | false (по умолчанию) |
true |
|---|---|---|
| Видим, не перекрыт | берётся | берётся |
opacity:0 / visibility:hidden, но есть геометрия и не перекрыт |
пропуск | берётся |
display:none (нет геометрии) |
пропуск | пропуск |
| Перекрыт сверху (оверлей) | пропуск | пропуск |
true снимает только проверку «видно глазу»; проверка «клик долетит» (элемент имеет геометрию и не перекрыт сверху) остаётся всегда. Ставьте true, когда целевой элемент намеренно скрыт стилями, но это точка клика (кастомные чекбоксы/капчи: реальный <input> под opacity:0). На обычных страницах оставляйте по умолчанию, иначе можно кликнуть скрытый дубль-шаблон вместо видимого элемента.
Поток управления
wait — пауза
Длительность (мс) задаётся одной из форм value:
value |
Что значит |
|---|---|
[min, max] |
случайная пауза из диапазона. Каждое значение 0..180000, min ≤ max. |
[ms] |
фиксированная пауза. |
ms (число) |
фиксированная пауза. |
{"variable": "<имя>"} |
пауза берётся из переменной. |
{"id": "p", "action": "wait", "value": [1000, 3000], "next": ["nxt"]}
{"id": "p2", "action": "wait", "value": {"variable": "dwell"}, "next": ["nxt"]}
condition — ветвление
Имеет два именованных выхода success / failure внутри value. Поле next у блока не задаётся.
value.success(array[string], 1..10) —idблоков по ветви «истина».value.failure(array[string], 1..10) —idблоков по ветви «ложь».
Плюс один из трёх режимов проверки:
По наличию элемента — есть ключ selector:
"value": {"selector": [".cta"], "success": ["click"], "failure": ["fallback"]}
Истина, если найден хотя бы один из селекторов.
По переменной — есть ключи variable, sign, value:
"value": {"variable": "tries", "sign": "<", "value": 5,
"success": ["retry"], "failure": ["giveUp"]}
sign:<,>,=. Для строк допустим только=.value:intили строка (до 100 символов).
По состоянию курсора — есть ключ selected:
"value": {"selected": ["#btn"], "success": ["ok"], "failure": ["bad"]}
Истина, если курсор сейчас наведён на один из элементов (использовать после moveTo).
shuffle — случайные движения
value.duration(array[int, int]) —[min_sec, max_sec], секунды. Каждое1..30,min ≤ max.value.modifiers(array[string], optional) —["click"]чтобы разрешить случайные клики во время shuffle; пустой массив / отсутствие поля — только движения курсора.
{"id": "shuf", "action": "shuffle",
"value": {"duration": [3, 5], "modifiers": ["click"]}}
random — случайное ветвление
Без value. Передаёт управление случайному блоку из next.
{"id": "branch", "action": "random", "next": ["a", "b", "c"]}
end — конец цепочки
value:{}.nextне задаётся.
Завершает поток выполнения.
Курсор и клики
moveTo — навестись на элемент
value.selector(array[string], до 10) — CSS-селекторы. Выбирается случайный из найденных.value.drag(bool, optional) —trueимитирует драг (drag-and-drop междуclickTo(hold:true)иclickRelease; актуально и для тач-устройств).
clickTo — клик по элементу
value.selector(array[string], 1..10) — CSS-селекторы.value.modifiers(object, required):button:"left"|"middle"|"right".count(int, 1..10) — количество кликов.keys(array of"shift"|"ctrl"|"alt") — клавиши-модификаторы во время клика.hold(bool) — удерживать кнопку до парногоclickRelease.
{"id": "c", "action": "clickTo",
"value": {"selector": [".cta"],
"modifiers": {"button": "left", "count": 1, "keys": [], "hold": false}}}
click — клик в текущей позиции курсора
Как clickTo, но без selector — кликает там, где сейчас курсор. modifiers те же.
clickRelease — отпустить удерживаемую кнопку
value.button:"left"|"middle"|"right". Парный кclickToсhold: true.
Прокрутка и фокус
scrollTo — проскроллить к элементу
value.selector(array[string], до 10) — CSS-селекторы.
scrollPercent — проскроллить на процент страницы
Прокрутка к доле высоты страницы (0 — самый верх, 100 — низ).
value.percent— число0..100, либо объект{"variable": "<имя>"}(процент берётся из переменной).
{"id": "s1", "action": "scrollPercent", "value": {"percent": 50}, "next": ["nxt"]}
{"id": "s2", "action": "scrollPercent", "value": {"percent": {"variable": "depth"}}, "next": ["nxt"]}
moveToDown — проскроллить страницу вниз
Без value.
moveToUp — проскроллить страницу вверх
Без value.
focus — установить фокус
value.selector(array[string], до 10) — CSS-селекторы.
Поиск и работа с DOM
findText — поиск текста / элемента / атрибута
Дискриминатор value.type определяет режим.
type: "text" — поиск подстроки на странице:
value.pattern(string, 1..100) — что искать.value.mode:"Text"(видимый текст) или"HTML"(HTML-разметка).value.variable(string) — куда положить результат.
{"action": "findText",
"value": {"type": "text", "pattern": "Test Passed",
"mode": "Text", "variable": "found"}}
type: "element" — поиск элемента, результат в переменную. Поле attr опционально.
type: "attribute" — поиск элемента и чтение его атрибута в переменную (attr обязателен):
{"action": "findText",
"value": {"type": "attribute", "selector": ["#uniqText"],
"attr": "value", "variable": "saved"}}
getElement — найти и закешировать элемент
value.selector(array[string], до 10) — CSS-селекторы.
После выполнения найденный элемент доступен в последующих действиях через псевдо-селектор :last-element (см. Рецепт 10).
removeElement — удалить элемент из DOM
value.selector(array[string], min 1) — удаляются все найденные.
setAttribute — установить атрибут на элементе
value.selector(array[string], min 1) — CSS-селекторы.value.attr(string, до 25 символов) — имя атрибута.- Источник значения — одно из (взаимно исключающие):
value.value(string, до 100 символов) — литерал;value.variable(string) — значение из переменной.
setAttrToVar — сохранить значение атрибута в переменную
value.selector(array[string], min 1) — если найдено несколько элементов, значения склеиваются через запятую.value.attr(string, опц., до 25 символов) — какое свойство читать; по умолчаниюinnerText.value.variable(string) — куда сохранить.
removeAttribute — удалить атрибут с элемента
value.selector(array[string], min 1) — CSS-селекторы.value.attr(string, до 25 символов) — имя атрибута.
takeImageToVar — снимок страницы в переменную
Делает скриншот текущей страницы и сохраняет его в переменную как строку base64. Дальше её можно, например, отправить на свой сервер через httpPost.
value.variable(string) — имя переменной, куда положить снимок.
{"id": "shot", "action": "takeImageToVar", "value": {"variable": "snapshot"}, "next": ["send"]}
Ввод текста
type — печать текста в активном элементе
-
value.strings(array[string], 1..100) — список вариантов, выбирается случайный. Каждая строка — до 255 символов. Допускаются спец-токены:Токен Клавиша k-enterEnter k-tabTab k-spaceSpace k-backspaceBackspace k-deleteDelete k-upk-downk-leftk-rightстрелки -
value.modifiers.count(int, 1..100, optional) — повторить ввод выбранной строки N раз.
{"action": "type", "value": {"strings": ["k-enter"]}}
Переменные
setVar — изменить значение переменной
-
value.variable(string) — имя цели. -
value.set.mode— режим:Режим set.valueСмысл "val"intили строка до 100 символовПрисвоить литерал. "var"имя другой переменной (string) Присвоить значение этой переменной. "incr"int1..1000Увеличить целевую переменную на N. "decr"int1..1000Уменьшить целевую переменную на N.
{"action": "setVar",
"value": {"variable": "counter", "set": {"mode": "incr", "value": 1}}}
События
emitEvent — испустить событие
Шлёт пользовательское событие; его ловит приёмник в секции events.
value.event(string) — имя события: непустое, до 30 символов, charset^[\p{L}\d_-]+$. Одно и то же имя можно эмитить из нескольких блоковemitEvent.
{"id": "e", "action": "emitEvent", "value": {"event": "modal"}, "next": ["nxt"]}
Сеть
httpGet — GET-запрос и разбор ответа
Делает GET-запрос и раскладывает поля ответа по переменным сценария.
Формат запроса/ответа и готовые примеры серверной части — в разделе Свой сервер для httpGet и httpPost.
value.url(string) — валидный URL, толькоhttp/https, до 2048 символов.value.map(array, 0..20) — список соответствий{ "from": "<поле_ответа>", "to": "<переменная>" }:from— непустая строка до 255 символов;to— имя переменной (charset^[\p{L}\d_-]+$).
{"id": "g", "action": "httpGet",
"value": {"url": "https://api.example.com/task",
"map": [{"from": "query", "to": "q"}, {"from": "maxPrice", "to": "limit"}]},
"next": ["nxt"]}
httpPost — POST-запрос с переменными
Отправляет значения переменных POST-запросом.
Формат тела/заголовков и готовые примеры серверной части — в разделе Свой сервер для httpGet и httpPost.
value.url(string) — валидный URL, толькоhttp/https, до 2048 символов.value.send(array[string], 1..20) — имена переменных, которые надо отправить.
{"id": "p", "action": "httpPost",
"value": {"url": "https://api.example.com/report", "send": ["orderId", "status"]},
"next": ["nxt"]}
Готовые рецепты
Самодостаточные фрагменты actions, скопировать → положить в behavior.manual.actions. Все примеры — варианты реальных сценариев из тестового набора редактора.
Рецепт 1: подождать 5 секунд
{
"triggers": {"afterload": [{"enter": ["*"], "action": "wait1"}]},
"blocks": [
{"id": "wait1", "action": "wait", "value": [5000, 5000]}
]
}
Рецепт 2: найти текст и вставить в input
{
"triggers": {"afterload": [{"enter": ["*"], "action": "find"}]},
"blocks": [
{"id": "find", "action": "findText",
"value": {"type": "text", "pattern": "Test Passed",
"mode": "Text", "variable": "found"},
"next": ["paste"]},
{"id": "paste", "action": "setAttribute",
"value": {"selector": ["#textarea", "#textInput"],
"attr": "value", "variable": "found"}}
]
}
Рецепт 3: прочитать атрибут одного элемента и вставить в другой
{
"triggers": {"afterload": [{"enter": ["*"], "action": "read"}]},
"blocks": [
{"id": "read", "action": "findText",
"value": {"type": "attribute", "selector": ["#uniqText"],
"attr": "value", "variable": "found"},
"next": ["paste"]},
{"id": "paste", "action": "setAttribute",
"value": {"selector": ["#textarea"], "attr": "value", "variable": "found"}}
]
}
Рецепт 4: ввод текста и спец-клавиш по цепочке
{
"triggers": {"afterload": [{"enter": ["*"], "action": "t1"}]},
"blocks": [
{"id": "t1", "action": "type", "value": {"strings": ["HelloWorld"]}, "next": ["t2"]},
{"id": "t2", "action": "type", "value": {"strings": ["k-enter"]}, "next": ["t3"]},
{"id": "t3", "action": "type", "value": {"strings": ["k-tab"]}}
]
}
Рецепт 5: серия кликов по разным элементам
{
"triggers": {"afterload": [{"enter": ["*"], "action": "c1"}]},
"blocks": [
{"id": "c1", "action": "clickTo",
"value": {"selector": ["#uid1"], "modifiers": {"count": 1, "button": "left"}},
"next": ["c2"]},
{"id": "c2", "action": "clickTo",
"value": {"selector": ["#uid2"], "modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 6: drag-and-drop
{
"triggers": {"afterload": [{"enter": ["*"], "action": "grab"}]},
"blocks": [
{"id": "grab", "action": "clickTo",
"value": {"selector": ["#topLeftSquare"],
"modifiers": {"count": 1, "button": "left", "hold": true}},
"next": ["drag"]},
{"id": "drag", "action": "moveTo",
"value": {"drag": true, "selector": ["#bottomRightSquare"]},
"next": ["drop"]},
{"id": "drop", "action": "clickRelease", "value": {"button": "left"}}
]
}
Рецепт 7: проскроллить вниз и обратно вверх
{
"triggers": {"afterload": [{"enter": ["*"], "action": "s1"}]},
"blocks": [
{"id": "s1", "action": "moveToDown", "next": ["s2"]},
{"id": "s2", "action": "moveToUp"}
]
}
Рецепт 8: проскроллить точно к кнопке и кликнуть
{
"triggers": {"afterload": [{"enter": ["*"], "action": "scroll"}]},
"blocks": [
{"id": "scroll", "action": "scrollTo",
"value": {"selector": ["#finishBtn"]}, "next": ["click"]},
{"id": "click", "action": "clickTo",
"value": {"selector": ["#finishBtn"],
"modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 9: фокус по нескольким полям подряд
{
"triggers": {"afterload": [{"enter": ["*"], "action": "f1"}]},
"blocks": [
{"id": "f1", "action": "focus", "value": {"selector": ["#inputText"]}, "next": ["f2"]},
{"id": "f2", "action": "focus", "value": {"selector": ["#aLink"]}, "next": ["f3"]},
{"id": "f3", "action": "focus", "value": {"selector": ["#buttOn"]}}
]
}
Рецепт 10: getElement + :last-element
Когда селектор страшный или одноразовый — нашли один раз через getElement, дальше ссылаемся как :last-element:
{
"triggers": {"afterload": [{"enter": ["*"], "action": "find"}]},
"blocks": [
{"id": "find", "action": "getElement",
"value": {"selector": ["#page1Btn"]}, "next": ["clk"]},
{"id": "clk", "action": "clickTo",
"value": {"selector": [":last-element"],
"modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 11: переменные и подстановка {name} в селекторе
{
"variables": {"foo": 3, "bar": "baz"},
"triggers": {"afterload": [{"enter": ["*"], "action": "step1"}]},
"blocks": [
{"id": "step1", "action": "setVar",
"value": {"variable": "baz", "set": {"mode": "var", "value": "foo"}},
"next": ["step2"]},
{"id": "step2", "action": "setAttribute",
"value": {"selector": ["#{bar}"], "attr": "value", "variable": "baz"}}
]
}
Сначала кладём в baz значение foo (= 3). Затем подставляем в селектор #{bar} значение переменной bar (= "baz") — получаем #baz — и пишем туда значение переменной baz (= 3).
Рецепт 12: цикл со счётчиком и числовое сравнение
«Повторять 5 раз с паузой по 500 мс, потом завершиться»:
{
"variables": {"tries": 0},
"triggers": {"afterload": [{"enter": ["*"], "action": "inc"}]},
"blocks": [
{"id": "inc", "action": "setVar",
"value": {"variable": "tries", "set": {"mode": "incr", "value": 1}},
"next": ["chk"]},
{"id": "chk", "action": "condition",
"value": {"variable": "tries", "sign": "<", "value": 5,
"success": ["pause"], "failure": ["stop"]}},
{"id": "pause", "action": "wait", "value": [500, 500], "next": ["inc"]},
{"id": "stop", "action": "end", "value": {}}
]
}
Рецепт 13: «дождаться кнопку и кликнуть» (poll-pattern)
«Скроллить вниз с паузами, пока на странице не появится кнопка .cta-button, потом кликнуть»:
{
"triggers": {"afterload": [{"enter": ["*"], "action": "check"}]},
"blocks": [
{"id": "check", "action": "condition",
"value": {"selector": [".cta-button"],
"success": ["click"], "failure": ["scroll"]}},
{"id": "scroll", "action": "moveToDown", "next": ["pause"]},
{"id": "pause", "action": "wait", "value": [300, 700], "next": ["check"]},
{"id": "click", "action": "clickTo",
"value": {"selector": [".cta-button"],
"modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 14: обмен значений между двумя input через переменную
{
"triggers": {"afterload": [{"enter": ["*"], "action": "read"}]},
"blocks": [
{"id": "read", "action": "setAttrToVar",
"value": {"selector": ["#inputText1"], "attr": "value", "variable": "temp"},
"next": ["clear"]},
{"id": "clear", "action": "setAttribute",
"value": {"selector": ["#inputText1"], "attr": "value", "value": ""},
"next": ["paste"]},
{"id": "paste", "action": "setAttribute",
"value": {"selector": ["#inputText2"], "attr": "value", "variable": "temp"}}
]
}
Рецепт 15: общий лимит кликов на всю кампанию
«Кликаем по баннеру и считаем клики сразу по всем вкладкам визита; как только суммарно перевалило за 50 — останавливаемся».
Счётчик лежит в sharedVariables — поэтому его видят все вкладки одновременно (в обычных variables каждая вкладка считала бы отдельно). Порог ловит watches: как только clicksTotal станет больше 50, он сам запустит блок остановки — не нужно проверять условие после каждого клика вручную.
{
"sharedVariables": {"clicksTotal": 0},
"triggers": {"afterload": [{"enter": ["*"], "action": "clickAd"}]},
"watches": [
{"id": "wt", "variable": "clicksTotal", "sign": ">", "value": 50, "action": "stop"}
],
"blocks": [
{"id": "clickAd", "action": "clickTo",
"value": {"selector": [".banner"], "modifiers": {"count": 1, "button": "left"}},
"next": ["count"]},
{"id": "count", "action": "setVar",
"value": {"variable": "clicksTotal", "set": {"mode": "incr", "value": 1}}, "next": []},
{"id": "stop", "action": "end", "value": {}}
]
}
Рецепт 16: естественное дочитывание лонгрида
«Прокручиваем страницу частями — на 25, 50, 75 и 100 % высоты — с паузами между ними, как живой читатель; в конце, если на странице есть кнопка подписки, кликаем по ней».
scrollPercent прокручивает на заданную долю высоты страницы (0 — верх, 100 — низ).
{
"triggers": {"afterload": [{"enter": ["*"], "action": "s25"}]},
"blocks": [
{"id": "s25", "action": "scrollPercent", "value": {"percent": 25}, "next": ["p1"]},
{"id": "p1", "action": "wait", "value": [1500, 3000], "next": ["s50"]},
{"id": "s50", "action": "scrollPercent", "value": {"percent": 50}, "next": ["p2"]},
{"id": "p2", "action": "wait", "value": [1500, 3000], "next": ["s75"]},
{"id": "s75", "action": "scrollPercent", "value": {"percent": 75}, "next": ["p3"]},
{"id": "p3", "action": "wait", "value": [1500, 3000], "next": ["s100"]},
{"id": "s100", "action": "scrollPercent", "value": {"percent": 100}, "next": ["check"]},
{"id": "check", "action": "condition",
"value": {"selector": [".subscribe-btn"], "success": ["sub"], "failure": ["done"]}},
{"id": "sub", "action": "clickTo",
"value": {"selector": [".subscribe-btn"], "modifiers": {"count": 1, "button": "left"}}},
{"id": "done", "action": "end", "value": {}}
]
}
Рецепт 17: единый обработчик всплывашек через событие
«В нескольких местах сценария может выскочить модалка (логина/cookie). Чтобы не дублировать логику закрытия, каждое такое место шлёт сигнал emitEvent{event: modal}, а единственный приёмник events её закрывает».
Эмиттеров одного события может быть сколько угодно, приёмник — один. Режим mode: every — реагировать на каждый сигнал.
{
"triggers": {"afterload": [{"enter": ["*"], "action": "openA"}]},
"events": [{"id": "ev", "event": "modal", "mode": "every", "action": "close"}],
"blocks": [
{"id": "openA", "action": "clickTo",
"value": {"selector": ["#menu"], "modifiers": {"count": 1, "button": "left"}}, "next": ["sigA"]},
{"id": "sigA", "action": "emitEvent", "value": {"event": "modal"}, "next": ["openB"]},
{"id": "openB", "action": "clickTo",
"value": {"selector": ["#profile"], "modifiers": {"count": 1, "button": "left"}}, "next": ["sigB"]},
{"id": "sigB", "action": "emitEvent", "value": {"event": "modal"}, "next": []},
{"id": "close", "action": "clickTo",
"value": {"selector": [".modal-close", ".cookie-accept"], "modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 18: подставить в форму данные из вашего API
«Перед заполнением формы клиент спрашивает у вашего сервера имя автора и текст отзыва (httpGet), раскладывает ответ по переменным и вписывает их в поля».
Маппер from→to кладёт поле ответа author в переменную author, а поле text — в review. Серверную часть см. в Свой сервер для httpGet и httpPost.
{
"variables": {"author": "", "review": ""},
"triggers": {"afterload": [{"enter": ["*"], "action": "fetch"}]},
"blocks": [
{"id": "fetch", "action": "httpGet",
"value": {"url": "https://my-server.example.com/next-review",
"map": [{"from": "author", "to": "author"}, {"from": "text", "to": "review"}]},
"next": ["fillName"]},
{"id": "fillName", "action": "setAttribute",
"value": {"selector": ["#author"], "attr": "value", "variable": "author"}, "next": ["fillText"]},
{"id": "fillText", "action": "setAttribute",
"value": {"selector": ["#review"], "attr": "value", "variable": "review"}, "next": ["submit"]},
{"id": "submit", "action": "clickTo",
"value": {"selector": ["#submit"], "modifiers": {"count": 1, "button": "left"}}}
]
}
Рецепт 19: собрать цены и скриншот, отправить на сервер
«Клиент читает цены двух товаров со страницы, делает скриншот и отправляет всё на ваш сервер (httpPost)».
takeImageToVar кладёт скриншот в переменную, httpPost отправляет выбранные переменные. Серверную часть см. в Свой сервер для httpGet и httpPost.
{
"variables": {"priceA": "", "priceB": ""},
"triggers": {"afterload": [{"enter": ["*"], "action": "readA"}]},
"blocks": [
{"id": "readA", "action": "setAttrToVar",
"value": {"selector": ["#product1 .price"], "attr": "textContent", "variable": "priceA"}, "next": ["readB"]},
{"id": "readB", "action": "setAttrToVar",
"value": {"selector": ["#product2 .price"], "attr": "textContent", "variable": "priceB"}, "next": ["snap"]},
{"id": "snap", "action": "takeImageToVar", "value": {"variable": "snapshot"}, "next": ["send"]},
{"id": "send", "action": "httpPost",
"value": {"url": "https://my-server.example.com/collect", "send": ["priceA", "priceB", "snapshot"]}}
]
}
Свой сервер для httpGet и httpPost
Узлы httpPost («Отправить по ссылке») и httpGet («Получить по ссылке») связывают сценарий с вашим HTTP-эндпоинтом:
httpPost— клиент шлёт выбранные переменные на ваш URL;httpGet— клиент запрашивает ваш URL и раскладывает поля ответа по переменным.
Запрос идёт через сессию вкладки — с тем же прокси, User-Agent и cookies, что и обычный серфинг этой вкладки. Формат фиксированный и зеркальный: эти узлы рассчитаны на ваш сервер, а не на произвольный сторонний API.
sequenceDiagram
participant C as Клиент (вкладка)
participant S as Ваш сервер
Note over C,S: httpPost — «Отправить по ссылке»
C->>S: POST url + заголовок Request-Meta,<br/>тело { "payload": { имя: base64(значение) } }
S-->>C: 2xx (тело игнорируется)
Note over C,S: httpGet — «Получить по ссылке»
C->>S: GET url + заголовок Request-Meta
S-->>C: 200 { "payload": { поле: base64(значение) } }
Note over C: поле → переменная по мапперу from→to
Общий формат (одинаков для обоих узлов)
Везде два «кармана»:
- Мета — в HTTP-заголовке
Request-Meta, значение =base64( JSON(meta) ). Заголовок есть и в POST, и в GET. - Данные — в «конверте»
{ "payload": { "имя": "<base64-значения>" } }: у POST это тело запроса, у GET — тело ответа вашего сервера.
Про base64 (важно для новичка):
- Значения данных всегда в base64 (это base64 от UTF-8-байт строки). Тип не сохраняется: число
42уедет строкой"42"→"NDI=". Сервер после декода получит строку. - Мета кодируется в base64 целиком как одна JSON-строка; поля внутри меты — обычные, по отдельности не кодируются.
Поля меты:
| Поле | Тип | Смысл |
|---|---|---|
ts |
number | время запроса, unix-секунды |
domain |
string | домен текущей страницы |
url |
string | полный URL текущей страницы |
tab_id |
string | id вкладки |
task_id |
number | id задания |
device |
string | desktop / mobile |
referrer |
string | document.referrer (может быть пустым) |
nonce |
string | случайный UUID, новый на каждый запрос |
Токена или подписи в мете нет; nonce — просто разовый идентификатор запроса.
httpPost — клиент → ваш сервер
Настройка в сценарии: URL + список переменных (1–20). По проводу уходит:
POST /ваш-приёмник HTTP/1.1
Content-Type: application/json
Request-Meta: eyJ0cyI6MTcxODYwMDAwMCwiZG9tYWluIjoi...
{ "payload": { "login": "dXNlcjE=", "counter": "NDI=" } }
- Метод
POST,Content-Type: application/json. - Тело —
{ "payload": { имя: base64(значение) } }(не плоский объект и не form-urlencoded). - Все значения — строки в base64 (
42→"42"→"NDI="). - Мета — только в заголовке, в теле её нет.
- Ответ игнорируется: клиент смотрит лишь код состояния. Достаточно вернуть любой
2xx(можно с пустым телом).
httpGet — клиент → ваш сервер → клиент
Настройка в сценарии: URL + маппер «поле ответа → переменная» (0–20 строк). По проводу уходит:
GET /ваш-передатчик HTTP/1.1
Request-Meta: eyJ0cyI6MTcxODYwMDAwMCwiZG9tYWluIjoi...
- Метод
GET, без тела и без query-параметров; URL берётся буквально (подстановка{переменная}в URL не выполняется). - Мета — в заголовке.
Ваш сервер должен ответить:
200 OK
{ "payload": { "token": "YWJjMTIz", "qty": "MTA=" } }
- Тело — валидный JSON (если не распарсится — клиент ничего не запишет).
- Данные — в
payload, значения — base64. - Маппер: для каждой строки
{ "from": "<поле>", "to": "<переменная>" }клиент берётpayload[from], декодирует base64 и кладёт в переменнуюto. Адресуется только ключ верхнего уровня вpayload(без точечных путей и JSONPath). Поля нет → строка пропускается, переменная сохраняет прежнее значение. Массивы в ответе не поддерживаются.
Ограничения
| Что | Значение |
|---|---|
| Протокол URL | только http/https, длина ≤ 2048 |
Подстановки {переменная} в URL |
не выполняются |
Размер ответа (httpGet) |
до 2 МБ (сверх — чтение обрывается, переменные не пишутся) |
| Таймаут | 20 с |
| Пауза между вызовами | обязательная 5 с после каждого вызова (в т.ч. при ошибке) |
| Откуда идёт запрос | через прокси профиля вкладки (IP/UA/cookies как у обычного серфинга) |
Минимальные примеры сервера
Ниже — по одному приёмнику (для httpPost) и передатчику (для httpGet) на Node.js, PHP и Python. Логика везде одинакова: мету при желании читаем из заголовка Request-Meta, а данные кладём/берём из payload, не забывая про base64.
Node.js — приёмник (httpPost):
const express = require('express');
const app = express();
app.use(express.json());
app.post('/collect', (req, res) => {
// мета — по желанию (декодируем заголовок)
const metaRaw = req.get('Request-Meta');
const meta = metaRaw ? JSON.parse(Buffer.from(metaRaw, 'base64').toString('utf8')) : {};
const payload = (req.body && req.body.payload) || {};
const data = {};
for (const [name, b64] of Object.entries(payload)) {
data[name] = Buffer.from(b64, 'base64').toString('utf8'); // декод значения
}
console.log('tab', meta.tab_id, data); // data = { login: 'user1', counter: '42' }
res.sendStatus(200); // тело не важно — достаточно 2xx
});
app.listen(3000);
Node.js — передатчик (httpGet):
app.get('/next-review', (req, res) => {
const out = { author: 'Иван', text: 'Отличный товар!' };
const payload = {};
for (const [field, value] of Object.entries(out)) {
payload[field] = Buffer.from(String(value), 'utf8').toString('base64'); // каждое значение — base64
}
res.json({ payload }); // { "payload": { "author": "<base64>", "text": "<base64>" } }
});
PHP — приёмник (httpPost):
<?php
$meta = json_decode(base64_decode($_SERVER['HTTP_REQUEST_META'] ?? ''), true) ?: [];
$body = json_decode(file_get_contents('php://input'), true) ?: [];
$data = [];
foreach (($body['payload'] ?? []) as $name => $b64) {
$data[$name] = base64_decode($b64); // декод значения
}
// $data = ['login' => 'user1', 'counter' => '42'];
error_log('tab ' . ($meta['tab_id'] ?? '?') . ' ' . json_encode($data));
http_response_code(200); // достаточно 2xx
PHP — передатчик (httpGet):
<?php
$out = ['token' => 'abc123', 'qty' => 10];
$payload = [];
foreach ($out as $field => $value) {
$payload[$field] = base64_encode((string) $value); // каждое значение — base64
}
header('Content-Type: application/json');
echo json_encode(['payload' => $payload]);
Python (Flask) — приёмник (httpPost):
import base64, json
from flask import Flask, request
app = Flask(__name__)
@app.post('/collect')
def collect():
raw = request.headers.get('Request-Meta', '')
meta = json.loads(base64.b64decode(raw)) if raw else {}
payload = (request.get_json(silent=True) or {}).get('payload', {})
data = {name: base64.b64decode(b64).decode('utf-8') for name, b64 in payload.items()}
# data = {'login': 'user1', 'counter': '42'}
print('tab', meta.get('tab_id'), data)
return '', 200 # достаточно 2xx
Python (Flask) — передатчик (httpGet):
@app.get('/next-review')
def next_review():
out = {'author': 'Иван', 'text': 'Отличный товар!'}
payload = {field: base64.b64encode(str(value).encode('utf-8')).decode('ascii')
for field, value in out.items()}
return {'payload': payload} # Flask отдаст это как JSON
Статистика
GET /pages-compiled-stats/
Назначение: статистика показов по странице или группе за дату/период. Максимальный диапазон дат — 7 дней.
Параметры query:
page_id(int) илиgroup_id(int)- и один из вариантов периода:
date(YYYY-MM-DD)date_from+date_to(YYYY-MM-DD)
Пример запроса:
curl -sS "https://api.livesurf.ru/pages-compiled-stats/?group_id=123&date=2026-02-19" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json"
Пример ответа:
[
{
"group_id": 123,
"page_id": 1086337,
"visits": 42,
"credits": 98,
"date": "19.02.2026",
"date_update": "19.02.2026 15:57:24"
}
]
Пример ошибки (400):
{
"errors": {
"non_field_errors": [
"required parameter \"date\" or parameters \"date_from\" and \"date_to\""
]
}
}
Практика использования
- Прежде чем строить UI и валидировать формы — запросите
GET /limits/.
Оттуда придёт: тариф текущего юзера (user.limits) с порогамиmax_*для
self-валидации, таблицаpricing.showtime_price({secs: base_price}),
и список активныхpricing.modifiers(надбавки/скидки в процентах от базы;
применяются мультипликативно:final = base × Π(1 + modifier.value)по всем
enabled=true). - Авторизация — заголовок
Authorization: <token>.401/403=
«нет токена» / «токен валиден, но операция не разрешена для тарифа». - Рейт-лимит клиентского API — 10 rps на токен. При превышении вернётся
429; на массовых операциях разбивайте поток во времени. - Ошибки валидации возвращаются как
400с полемerrors. Конкретные
сообщения лежат вerrors.<field_name>илиerrors.non_field_errors—
на них и завязывайте показ ошибок пользователю.
LIVEsurf
RU
IT