Документация 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_keywords
    • settings.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 — это ошибка сохранения.

Имя переменной уникально в объединении variablessharedVariables: одно и то же имя в двух секциях сразу — ошибка.

В чём разница на практике: обычная переменная у каждой вкладки своя, общая — одна на весь визит.

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 отсутствует (или {}). Для остальных — обязательно.
  • nextid-получатели по обычному выходу. 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-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. Все примеры — варианты реальных сценариев из тестового набора редактора.

Рецепт 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

Общий формат (одинаков для обоих узлов)

Везде два «кармана»:

  1. Мета — в HTTP-заголовке Request-Meta, значение = base64( JSON(meta) ). Заголовок есть и в POST, и в GET.
  2. Данные — в «конверте» { "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
    на них и завязывайте показ ошибок пользователю.