LIVESURFВЕБ ПРОМОУШЕН
RU - Продвигайте сайт/ссылку бесплатно — создайте аккаунт за 30 секунд
- Живой трафик на ваш проект — закажите посещения реальными пользователями
- Установите программу и зарабатывайте кредиты для продвижения без вложений
Авторизуйтесь или зарегистрируйтесь, чтобы получить API-ключ.
Войти или зарегистрироватьсяДата обновления документации: 2026-07-04
REST API LiveSurf для управления аккаунтом, группами, страницами, источниками трафика и получения статистики.
https://api.livesurf.ruAuthorization: <API_KEY>10 запросов в секундуAuthorization: <API_KEY>
Accept: application/json
Content-Type: application/json
curl -sS "https://api.livesurf.ru/user/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json"
curl -sS -X POST "https://api.livesurf.ru/user/manualmode/" \
-H "Authorization: <API_KEY>" \
-H "Accept: application/json" \
-H "Content-Type: application/json"
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"}'
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}'
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.limitsmin_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_backlinksmessengers:
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, который кликает по селектору. Дальше — добавляем блоки и переходы под нужный сценарий.
actionsbehavior.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-селекторы.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 — удалить элемент из DOMvalue.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-enter |
Enter |
k-tab |
Tab |
k-space |
Space |
k-backspace |
Backspace |
k-delete |
Delete |
k-up k-down k-left k-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" |
int 1..1000 |
Увеличить целевую переменную на N. |
"decr" |
int 1..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. Все примеры — варианты реальных сценариев из тестового набора редактора.
{
"triggers": {"afterload": [{"enter": ["*"], "action": "wait1"}]},
"blocks": [
{"id": "wait1", "action": "wait", "value": [5000, 5000]}
]
}
{
"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"}}
]
}
{
"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"}}
]
}
{
"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"]}}
]
}
{
"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"}}}
]
}
{
"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"}}
]
}
{
"triggers": {"afterload": [{"enter": ["*"], "action": "s1"}]},
"blocks": [
{"id": "s1", "action": "moveToDown", "next": ["s2"]},
{"id": "s2", "action": "moveToUp"}
]
}
{
"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"}}}
]
}
{
"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"]}}
]
}
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"}}}
]
}
{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).
«Повторять 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": {}}
]
}
«Скроллить вниз с паузами, пока на странице не появится кнопка .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"}}}
]
}
{
"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"}}
]
}
«Кликаем по баннеру и считаем клики сразу по всем вкладкам визита; как только суммарно перевалило за 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": {}}
]
}
«Прокручиваем страницу частями — на 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": {}}
]
}
«В нескольких местах сценария может выскочить модалка (логина/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"}}}
]
}
«Перед заполнением формы клиент спрашивает у вашего сервера имя автора и текст отзыва (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"}}}
]
}
«Клиент читает цены двух товаров со страницы, делает скриншот и отправляет всё на ваш сервер (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"]}}
]
}
Узлы 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
Везде два «кармана»:
Request-Meta, значение = base64( JSON(meta) ). Заголовок есть и в POST, и в GET.{ "payload": { "имя": "<base64-значения>" } }: у POST это тело запроса, у GET — тело ответа вашего сервера.Про base64 (важно для новичка):
42 уедет строкой "42" → "NDI=". Сервер после декода получит строку.Поля меты:
| Поле | Тип | Смысл |
|---|---|---|
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 — просто разовый идентификатор запроса.
Настройка в сценарии: 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).42 → "42" → "NDI=").2xx (можно с пустым телом).Настройка в сценарии: URL + маппер «поле ответа → переменная» (0–20 строк). По проводу уходит:
GET /ваш-передатчик HTTP/1.1
Request-Meta: eyJ0cyI6MTcxODYwMDAwMCwiZG9tYWluIjoi...
GET, без тела и без query-параметров; URL берётся буквально (подстановка {переменная} в URL не выполняется).Ваш сервер должен ответить:
200 OK
{ "payload": { "token": "YWJjMTIz", "qty": "MTA=" } }
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\""
]
}
}
GET /limits/.user.limits) с порогами max_* дляpricing.showtime_price ({secs: base_price}),pricing.modifiers (надбавки/скидки в процентах от базы;final = base × Π(1 + modifier.value) по всемenabled=true).Authorization: <token>. 401/403 =429; на массовых операциях разбивайте поток во времени.400 с полем errors. Конкретныеerrors.<field_name> или errors.non_field_errors —Ошибка в тексте
Страница:
Оригинал текста:
Ваш вариант:
Комментарий (не обязательно):