API — Профили, пресеты и роутинг

Просматривайте и импортируйте идентичности, управляйте пресетами и правилами доменного роутинга, а также регистрируйте шлюзы, через которые проходит трафик ваших портов.

Профили

GET/api/v1/profilesТребуется авторизация

Возвращает список сохранённых идентичностей с опциональной постраничной навигацией и фильтром по браузеру.

ПараметрТипОбязательныйОписание
limitint (query)НетРазмер страницы: по умолчанию 100, максимум 1000. Большее значение НЕ отклоняется — оно молча урезается до 1000; фактический размер страницы возвращается в поле limit ответа. Чтобы выкачать базу целиком, листайте offset'ом, сверяясь с total.
offsetint (query)НетСмещение страницы (по умолчанию 0).
browserstring (query)НетФильтр по браузеру, например "chrome".
Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" "http://127.0.0.1:8891/api/v1/profiles?browser=chrome&limit=50"
Ответ
{
  "profiles": [
    { "id": 8412, "name": "chrome_152_windows", "browser": "chrome",
      "version": "152", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) …",
      "created_at": "2026-08-20T12:00:00Z" }
  ],
  "total": 143900,
  "limit": 50,
  "offset": 0
}

total — сколько профилей отвечает фильтру ЦЕЛИКОМ, а не сколько вернулось на этой странице. 400 «browser must be one of: chrome, firefox, safari, edge» на неизвестном браузере.

GET/api/v1/countsТребуется авторизация

Возвращает разбивку идентичностей по сочетанию браузер+версия+ОС. Группировки по семейству браузера ручка не делает.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8891/api/v1/counts
Ответ
{
  "chrome_152+windows": 41000,
  "chrome_152+macos": 22000,
  "firefox_152+windows": 17400,
  "safari_18+ios": 9800
}

Ключ имеет вид "браузер_версия+ос": браузер — в НИЖНЕМ регистре (chrome, firefox, safari, edge), затем подчёркивание, версия, знак плюса и ОС (windows, macos, linux, android, ios). Если у профиля не записана ОС, ключ выглядит как "chrome_152+" — с пустым хвостом после плюса. Ключей вида "Chrome" в ответе не бывает: чтобы получить итог по семейству, сложите значения сами. Это те же счётчики, что приходят в поле profile_counts у GET /api/v1/status, и их сумма равна profile_count оттуда же.

Идентичность порта

POST/api/v1/port/{port}/rotateТребуется авторизация

Меняет идентичность порта на другую в пределах его текущих фильтров.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" -X POST http://127.0.0.1:8891/api/v1/port/20134/rotate
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/rotate
Ответ
{
  "name": "firefox_152_macos",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) …",
  "browser": "firefox",
  "os": "macos"
}

500 «no profile available», если движку нечего выдать: база профилей пуста или отфильтрована в ноль. Новая личность действует со следующего соединения — уже открытые keep-alive не рвутся.

GET/api/v1/port/{port}/profile/viewТребуется авторизация

Возвращает полную идентичность, которую порт в данный момент представляет.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8891/api/v1/port/20134/profile/view
Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/profile/view
Ответ
{
  "name": "chrome_152_windows",
  "browser": "chrome",
  "os": "windows",
  "version": 152,
  "user_agent": "Mozilla/5.0 …",
  "tls": {
    "available": true,
    "cipher_suites": ["TLS_AES_128_GCM_SHA256", "…"],
    "extensions": ["server_name", "…"],
    "curves": ["X25519MLKEM768", "…"],
    "alpn": ["h2", "http/1.1"],
    "ja3": "771,4865-4866-…",
    "ja3_hash": "cd08e31494f9531f560d64c695473da9",
    "ja3_note": "…"
  },
  "h2": { "settings": ["HEADER_TABLE_SIZE=65536", "…"] },
  "spec_source": "spec"
}

spec_source говорит, ОТКУДА взята форма: preset — из подготовленного набора, spec — собрана по спецификации, factory — запасная сборка, unavailable — форму собрать не удалось, и в этом случае tls.available=false. Это и есть ответ на вопрос «почему порт выглядит не так, как я просил».

Пресеты

Пресет — это сохранённый НАБОР портов вместе с их настройками, разложенный по папкам. Им поднимают готовую рабочую расстановку одним запросом.

GET/api/v1/presetsТребуется авторизация

Дерево сохранённых пресетов и папок.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/presets
Ответ
{
  "name": "",
  "path": "",
  "is_folder": true,
  "children": [
    { "name": "parsing", "path": "parsing", "is_folder": true,
      "children": [ { "name": "marketplaces", "path": "parsing/marketplaces",
                      "is_folder": false } ] },
    { "name": "daily-crawl", "path": "daily-crawl", "is_folder": false }
  ]
}
POST/api/v1/presetsТребуется авторизация

Сохраняет конфигурации открытых портов пресетом по указанному пути.

ПараметрТипОбязательныйОписание
pathstringДаПуть пресета в дереве, например parsing/marketplaces.
portsarrayНетНомера портов, которые сохранять. Пусто — сохранить все открытые.
Тело запроса
{ "path": "parsing/marketplaces", "ports": [20134, 20135] }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"path":"parsing/marketplaces","ports":[20134,20135]}' \
  http://127.0.0.1:8891/api/v1/presets
Ответ
{
  "status": "saved",
  "path": "parsing/marketplaces"
}
  • 400 «path is required»; 400 «port N is not open», если в списке закрытый порт; 400 «invalid path …», «invalid path segment …» или «path escapes root», если путь ведёт за пределы хранилища пресетов. 403 приходит только при реальном отказе файловой системы в правах.
POST/api/v1/presets/loadТребуется авторизация

Поднимает порты из пресета.

ПараметрТипОбязательныйОписание
pathstringДаПуть пресета.
modestringДаreplace или merge. Другое значение — 400.
Тело запроса
{ "path": "parsing/marketplaces", "mode": "merge" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"path":"parsing/marketplaces","mode":"merge"}' \
  http://127.0.0.1:8891/api/v1/presets/load
Ответ
{
  "results": [
    { "port": 20134, "status": "opened" },
    { "port": 20135, "status": "failed", "error": "port 20135 is already in use" }
  ]
}
  • 🔴 mode=replace ЗАКРЫВАЕТ ВСЕ открытые порты перед тем, как поднять порты пресета — идущий через них трафик обрывается. mode=merge закрывает только те порты, чьи номера есть в пресете.
  • Ответ — построчный итог по каждому порту: пресет мог быть записан давно, и часть портов может не подняться. Порт с несовместимым сочетанием mode=random и фильтров отклоняется здесь так же, как при обычном открытии.
  • 400 «path is required», 400 «mode must be replace or merge», 404 «preset not found».
DELETE/api/v1/presetsТребуется авторизация

Удаляет пресет.

ПараметрТипОбязательныйОписание
pathstringДаПуть пресета.
Тело запроса
{ "path": "parsing/marketplaces" }
Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"path":"parsing/marketplaces"}' \
  http://127.0.0.1:8891/api/v1/presets
Ответ
{
  "status": "deleted"
}
  • 400 «path is required»; 404 «preset not found». Тело обязательно.

Правила доменного роутинга

Правила разбирают трафик ОДНОГО порта по доменам: какой домен куда идёт и под какой личностью. Правила упорядочены, и выигрывает первое включённое, чьи образцы совпали с именем хоста.

GET/api/v1/domain_rulesТребуется авторизация

Возвращает упорядоченный список правил.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/domain_rules
Ответ
{
  "rules": [
    {
      "id": "r1",
      "name": "catalogue",
      "enabled": true,
      "matchers": ["example.com", "=shop.example.net"],
      "upstream": "socks5://203.0.113.10:1080",
      "chain_proxy": "",
      "upstream_gateway": "",
      "chain_gateway": "",
      "spoof": "inherit",
      "spoof_cfg": { "mode": "", "browser": "", "os": "",
                     "spoof_headers": null, "h2_spoofing": null }
    }
  ]
}
  • Образец без префикса совпадает с доменом И всеми его поддоменами; образец с «=» в начале — только с самим доменом.
PUT/api/v1/domain_rulesТребуется авторизация

Заменяет весь список правил целиком — присылать нужно все правила, а не одно.

ПараметрТипОбязательныйОписание
rulesarrayДаПравила в порядке применения.
rules[].namestringДаИмя правила — по нему его узнаёт человек.
rules[].enabledboolДаВыключенное правило пропускается при разборе.
rules[].matchersarrayДаДомены; «=» в начале делает совпадение точным.
rules[].upstreamstringНетВыход для этих доменов.
rules[].chain_proxystringНетПервое звено цепочки.
rules[].upstream_gatewaystringНетИмя сохранённого шлюза вместо адреса выхода.
rules[].chain_gatewaystringНетИмя шлюза для первого звена.
rules[].spoofstringНетinherit — брать личность у порта; custom — своя, из spoof_cfg; off — не подменять.
rules[].spoof_cfgobjectНетСвоя личность для spoof=custom: mode, browser, os, profile, spoof_headers, h2_spoofing.
Тело запроса
{ "rules": [ { "name": "catalogue", "enabled": true,
                "matchers": ["example.com"],
                "upstream": "socks5://203.0.113.10:1080",
                "spoof": "inherit" } ] }
Пример (curl)
curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"rules":[{"name":"catalogue","enabled":true,"matchers":["example.com"],"upstream":"socks5://203.0.113.10:1080","spoof":"inherit"}]}' \
  http://127.0.0.1:8891/api/v1/domain_rules
Ответ
{
  "rules": [ { "id": "r1", "name": "catalogue", "enabled": true,
               "matchers": ["example.com"],
               "upstream": "socks5://203.0.113.10:1080", "spoof": "inherit" } ]
}
  • 400 с объяснением, если правило не той формы — например, назван шлюз, которого нет. Ответ отдаёт список так, как он сохранён, вместе с назначенными идентификаторами.

Шлюзы (OpenVPN, VLESS, VMess, Trojan, Shadowsocks, Hysteria2, WireGuard)

Шлюз — сохранённая конфигурация туннеля, который поднимается на этой машине и отдаёт локальный SOCKS5. Дальше его назначают порту выходом (upstream_gateway) или первым звеном цепочки (chain_gateway) — по имени, а не по адресу.

GET/api/v1/ovpnТребуется авторизация

Список сохранённых шлюзов, состояние их туннелей и то, работает ли бэкенд вообще.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/ovpn
Ответ
{
  "configs": [
    {
      "name": "eu-demo",
      "kind": "openvpn",
      "size": 4821,
      "remote": "203.0.113.77:1194",
      "via": "",
      "uploaded_at": "2026-08-20T12:00:00Z",
      "tunnel": { "config": "eu-demo", "status": "up", "ports": 2, "socks_addr": "127.0.0.1:41675" },
      "ping": { "ms": 38, "at": "2026-09-04T09:41:12Z" }
    }
  ],
  "available": true
}
  • available=false означает, что на хосте нет ни OpenVPN, ни Xray; тогда рядом приходит reason. Поле via называет шлюз, через который этот выходит наружу — цепочку строят и здесь.
  • Объект tunnel приходит только у шлюза, который уже поднимали, и состояние лежит в поле status — up, connecting или error. Поля state нет, времени подъёма туннеля ручка не отдаёт вовсе. Рядом приходят ports (сколько портов сейчас держат туннель), socks_addr (локальный SOCKS5 этого туннеля) и config (имя шлюза), а при status=error — поле error с причиной, по которой туннель не поднялся.
POST/api/v1/ovpnТребуется авторизация

Добавляет шлюз. Конфигурация передаётся ТЕКСТОМ в поле content — файл .ovpn или конфигурация WireGuard целиком, либо ссылка vless://, trojan://, ss://, vmess:// или hysteria2:// (равнозначно hy2://); отдельной загрузки файла у ручки нет.

ПараметрТипОбязательныйОписание
namestringДаИмя, под которым шлюз будет виден портам.
contentstringДаТекст файла .ovpn или конфигурации WireGuard целиком, либо ссылка vless://, trojan://, ss://, vmess:// или hysteria2:// (hy2://).
kindstringНетОдно из: openvpn, vless, trojan, shadowsocks, vmess, hysteria2, wireguard. Пусто — определить по содержимому: по схеме ссылки (hy2:// приравнивается к hysteria2), по разделам [Interface] и [Peer] — wireguard, иначе openvpn.
viastringНетИмя другого шлюза, через который этот будет выходить наружу.
Тело запроса
{ "name": "eu-demo", "kind": "vless",
  "content": "vless://…@203.0.113.77:443?…" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d "{\"name\":\"eu-demo\",\"content\":\"$(cat eu-demo.ovpn | sed 's/\"/\\\\"/g')\"}" \
  http://127.0.0.1:8891/api/v1/ovpn
Ответ
{
  "status": "saved",
  "name": "eu-demo",
  "kind": "vless"
}
  • 400 «name is required»; 400 «content is required (the .ovpn text or a vless:// link)»; 400 «unknown gateway kind …» на неизвестном значении kind.
  • 400 с кодом via_cycle, если цепочка шлюзов замкнётся сама на себя — код приходит рядом с текстом, чтобы интерфейс мог отличить эту причину от прочих.
  • 501 «gateway config manager is not enabled» в сборке, где шлюзы не собраны.
PUT/api/v1/ovpn/{name}Требуется авторизация

Правит существующий шлюз: содержимое, звено via или и то и другое.

ПараметрТипОбязательныйОписание
contentstringНетНовый текст конфигурации. Пустая строка оставляет прежний.
viastring | nullНетnull — оставить как есть; пустая строка — снять звено; иначе имя шлюза.
Тело запроса
{ "via": "us-demo" }
Пример (curl)
curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"via":"us-demo"}' \
  http://127.0.0.1:8891/api/v1/ovpn/eu-demo
Ответ
{
  "status": "updated",
  "name": "eu-demo",
  "kind": "vless"
}
  • 404 «config "…" not found»; 400 с кодом via_cycle при замкнутой цепочке.
DELETE/api/v1/ovpn/{name}Требуется авторизация

Удаляет шлюз.

Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/ovpn/eu-demo
Ответ
{
  "status": "deleted",
  "name": "eu-demo"
}
  • 409 с кодом in_use, если шлюз назначен живому порту; 400 с кодом via_referenced, если через него выходит другой шлюз; 404, если такого шлюза нет.
GET/api/v1/gateway/openvpn-statusТребуется авторизация

Стоит ли на этой машине OpenVPN, и если нет — как его поставить именно на этой системе.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/gateway/openvpn-status
Ответ
{
  "os": "windows",
  "available": false,
  "reason": "openvpn executable not found in PATH",
  "install": {
    "title": "Install OpenVPN Community for Windows",
    "download_url": "https://openvpn.net/community-downloads/",
    "steps": ["…"],
    "note": "…"
  }
}
  • Поле install приходит только при available=false. Проверка ограничена тремя секундами.
  • Внутри install: title — заголовок, steps — шаги, download_url — ссылка на загрузку (именно download_url, не url), note — необязательная приписка. На linux и macOS дополнительно приходит command — готовая однострочная команда установки, например brew install openvpn. Поля download_url, command и note необязательные: при пустом значении их в ответе нет.
POST/api/v1/ovpn/pingТребуется авторизация

Замеряет время отклика каждого сохранённого шлюза и возвращает снимок результатов.

Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/ovpn/ping
Ответ
{
  "results": {
    "eu-demo": { "ms": 38, "at": "2026-09-04T09:41:12Z" },
    "us-demo": { "ms": 0, "at": "2026-09-04T09:41:12Z", "error": "timeout" }
  }
}
  • Замер идёт и по расписанию, поэтому в ответе могут оказаться значения, снятые раньше: у каждого результата есть время снятия.
  • 501 «gateway ping is not enabled»; 403 при неактивной лицензии — ручка за лицензией.
GET/api/v1/gateway/subsТребуется авторизация

Перечисляет подписки: сколько серверов дала каждая, какие шлюзы ей принадлежат, когда обновлялась и почему обновление не удалось в последний раз.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/gateway/subs
Ответ
{
  "subscriptions": [
    {
      "name": "demo-sub",
      "source": "https://example.com/…",
      "enabled": true,
      "interval_min": 60,
      "last_ok": "2026-09-04T08:00:00Z",
      "servers": 3,
      "gateways": ["demo-sub.eu-demo", "demo-sub.us-demo", "demo-sub.asia-demo"],
      "skipped": 0
    }
  ]
}
  • Поле source приходит ОТРЕДАКТИРОВАННЫМ: остаются схема и хост, путь заменяется на «/…», запрос — на «?…». Секрет подписки лежит именно в пути, поэтому рабочей ссылкой это значение не является — сохранять его как адрес подписки нельзя.
  • Имена шлюзов подписки имеют вид «имя-подписки.имя-сервера» (например demo-sub.DE-Germaniya): основа берётся из ремарки сервера. Двоеточие в имени недопустимо; префикс «gw:» в панели — только оформление строки маршрута, в API его не подставляют.
  • skipped — сколько серверов подписки пропущено как неподдерживаемые.
POST/api/v1/gateway/subsТребуется авторизация

Создаёт подписку или правит существующую по имени. Серверы подписки появляются как обычные шлюзы, и их можно назначить выходом или первым звеном.

ПараметрТипОбязательныйОписание
namestringДаИмя подписки: латиница, цифры, точка, дефис и подчёркивание, до 64 символов.
sourcestringНетСсылка на подписку. Обязательна при создании; при правке опустите её, чтобы сохранить прежнюю.
enabledboolНетОбновлять ли подписку по расписанию.
interval_minintНетПериод обновления в минутах.
Тело запроса
{ "name": "demo-sub", "source": "https://example.com/sub/demo",
  "enabled": true, "interval_min": 60 }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"demo-sub","source":"https://example.com/sub/demo","interval_min":60}' \
  http://127.0.0.1:8891/api/v1/gateway/subs
Ответ
{
  "subscription": {
    "name": "demo-sub",
    "source": "https://example.com/…",
    "enabled": true,
    "interval_min": 60,
    "last_ok": "2026-09-04T08:00:00Z",
    "servers": 3,
    "gateways": ["demo-sub.eu-demo", "demo-sub.us-demo", "demo-sub.asia-demo"],
    "skipped": 0
  },
  "result": {
    "added": 3, "updated": 0, "kept": 0,
    "removed": 0, "retained": 0,
    "skipped": 0, "unsupported": []
  }
}
  • Полей status и name на верхнем уровне НЕТ: запись приходит целиком в subscription — той же формы, что элементы списка в GET /api/v1/gateway/subs.
  • Подписка обновляется ПРЯМО в этом запросе, до 60 секунд: поэтому её серверы появляются сразу, а не после первого тика расписания. Ответ может прийти не мгновенно.
  • 🔴 Провал обновления — НЕ провал сохранения: код ответа всё равно 200, но вместо result приходит refresh_error с текстом ошибки, а запись уже на диске и будет пытаться снова. Клиент обязан проверять refresh_error: иначе нерабочая подписка засчитывается как успешно заведённая.
  • result — итог этого обновления: added, updated, kept, removed, retained, skipped и unsupported (список вида «схема×количество»).
  • Ссылка на подписку обратно в открытом виде не отдаётся: в source приходит сокращённая форма — схема и хост, а путь и параметры заменены многоточием.
POST/api/v1/gateway/subs/{name}/refreshТребуется авторизация

Обновляет одну подписку немедленно, не дожидаясь расписания.

Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/gateway/subs/demo-sub/refresh
Ответ
{
  "subscription": {
    "name": "demo-sub",
    "source": "https://example.com/…",
    "enabled": true,
    "interval_min": 60,
    "last_ok": "2026-09-04T08:00:00Z",
    "servers": 3,
    "gateways": ["demo-sub.eu-demo", "demo-sub.us-demo", "demo-sub.asia-demo"],
    "skipped": 0
  },
  "result": {
    "added": 1, "updated": 0, "kept": 2,
    "removed": 0, "retained": 0,
    "skipped": 0, "unsupported": []
  }
}
Ответ, если обновление не удалось (тоже 200)
{
  "subscription": { "name": "demo-sub", "servers": 3, "…": "…" },
  "refresh_error": "текст ошибки обновления"
}
  • Полей status и name на верхнем уровне нет. Имя, число серверов и список шлюзов лежат в subscription — это тот же объект, что в GET /api/v1/gateway/subs, и source в нём отредактирован: остаются только схема и хост.
  • result — итог одного обхода: added, updated, kept, removed, retained, skipped, unsupported. Поле skipped внутри result — про этот обход, а skipped внутри subscription — про состояние подписки.
  • Неудачное обновление возвращает 200 с полем refresh_error, а не ошибку HTTP: запись подписки остаётся на диске и попытки продолжатся по расписанию. Успех отличайте по наличию result и отсутствию refresh_error, а не по коду ответа.
  • 403 при неактивной лицензии; 404, если подписки с таким именем нет; 501 «gateway subscriptions are not enabled», если планировщик подписок выключен.
DELETE/api/v1/gateway/subs/{name}Требуется авторизация

Удаляет подписку вместе со шлюзами, которые она создала, — кроме тех, что удерживаются ссылкой.

Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/gateway/subs/demo-sub
Ответ
{
  "status": "deleted",
  "removed": ["demo-sub.eu-demo", "demo-sub.us-demo"],
  "retained": ["demo-sub.asia-demo"]
}
  • В ответе НЕТ имени подписки: приходят два списка шлюзов — removed (удалены) и retained (оставлены). Это единственный способ узнать, что уцелело.
  • 🔴 Сама подписка удаляется ВСЕГДА; отказа «порт занят» у этой ручки не бывает. Шлюз, на который ссылается открытый порт или включённое правило доменного роутинга, попадает в retained: он остаётся обычным шлюзом, но подписки-владельца у него больше нет и обновляться он не будет. Хотите, чтобы шлюз ушёл вместе с подпиской, — освободите порт ДО удаления.
  • 404, если подписки с таким именем нет; 501, если хранилище шлюзов выключено.

Папки пресетов

Пресеты складываются в папки, и папка — не украшение: пресет загружают целым набором портов, а набор удобнее держать вместе.

POST/api/v1/presets/folderТребуется авторизация

Создаёт папку.

ПараметрТипОбязательныйОписание
pathstringДаПуть папки в дереве.
Тело запроса
{ "path": "parsing" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"path":"parsing"}' \
  http://127.0.0.1:8891/api/v1/presets/folder
Ответ
{
  "status": "created",
  "path": "parsing"
}
  • 400 «path is required»; 400 с объяснением, если путь не той формы.
DELETE/api/v1/presets/folderТребуется авторизация

Удаляет папку вместе со всем, что в ней лежит.

ПараметрТипОбязательныйОписание
pathstringДаПуть папки.
Тело запроса
{ "path": "parsing" }
Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"path":"parsing"}' \
  http://127.0.0.1:8891/api/v1/presets/folder
Ответ
{
  "status": "deleted",
  "path": "parsing"
}
  • 400 «path is required»; 404 «folder not found»; 400 «invalid path …», «invalid path segment …» или «path escapes root», если путь ведёт за пределы хранилища пресетов. 403 приходит только при реальном отказе файловой системы в правах.
POST/api/v1/presets/moveТребуется авторизация

Переносит пресет или папку в другое место дерева.

ПараметрТипОбязательныйОписание
fromstringДаЧто переносить.
tostringДаКуда.
Тело запроса
{ "from": "daily-crawl", "to": "parsing/daily-crawl" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"from":"daily-crawl","to":"parsing/daily-crawl"}' \
  http://127.0.0.1:8891/api/v1/presets/move
Ответ
{
  "status": "moved",
  "from": "daily-crawl",
  "to": "parsing/daily-crawl"
}
  • 400 «from and to are required»; 404 «source not found»; 409 «destination already exists».

Профили и отпечатки

Отпечаток, снятый с настоящего браузера, можно разобрать, посмотреть и сохранить под именем — чтобы потом выбирать его как обычный профиль.

POST/api/v1/fingerprint/parseТребуется авторизация

Разбирает снятый отпечаток и показывает, из чего он состоит, ничего не сохраняя. Тело запроса — СЫРОЙ JSON снятого отпечатка целиком, как его отдал сервис снятия.

Тело запроса
{ "tls": { "ja3": "771,4865-4866-…", "ja4": "t13d1516h2_…" },
  "http2": { "akamai_fingerprint": "1:65536;…" },
  "user_agent": "Mozilla/5.0 …" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  --data-binary @captured.json \
  http://127.0.0.1:8891/api/v1/fingerprint/parse
Ответ
{
  "custom_tls": { "ja3": "…", "ja4": "…", "ciphers": ["…"], "extensions": [{"name": "server_name (0)"},
                    {"name": "application_layer_protocol_negotiation (16)", "protocols": ["h2", "http/1.1"]}],
                  "alpn": ["h2", "http/1.1"], "userAgent": "Mozilla/5.0 …" },
  "summary": { "ja3_hash": "…", "ja4": "…", "user_agent": "Mozilla/5.0 …",
                "browser": "chrome", "ciphers": 16, "extensions": 15 }
}
  • custom_tls — готовое тело для PUT /api/v1/port/{port}/custom_tls: разобрали, посмотрели, применили.
  • Принимаются форматы tls.peet.ws /api/all и ChromeApi /tls. Тело ограничено 1 МиБ. 400 с объяснением, если разобрать не удалось.
  • Поле custom_tls.extensions — список ОБЪЕКТОВ: у каждого обязательное name, плюс необязательные supported_groups, signature_algorithms, versions, protocols. Это НЕ то же поле, что extensions в ответе /profile/view: там элементы — строки. Поле summary — тоже объект, а не строка.
POST/api/v1/profiles/importТребуется авторизация

Сохраняет ОДИН отпечаток в базу профилей под именем — после этого его можно выбрать как mode=specific с этим именем.

ПараметрТипОбязательныйОписание
namestringДаИмя, под которым сохранить.
source_jsonstringНетСнятый отпечаток СТРОКОЙ, как он пришёл от сервиса снятия.
custom_tlsobjectНетУже разобранный отпечаток — то, что вернул /fingerprint/parse.
Тело запроса
{ "name": "chrome_152_captured",
  "source_json": "{\"tls\": … }" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"chrome_152_captured","custom_tls":{ … }}' \
  http://127.0.0.1:8891/api/v1/profiles/import
Ответ
{
  "name": "chrome_152_captured",
  "summary": { "ja3_hash": "…", "ja4": "…", "user_agent": "Mozilla/5.0 …",
                "browser": "chrome", "ciphers": 16, "extensions": 15 }
}
  • Одно из полей source_json или custom_tls обязательно: без обоих ответ 400 «custom_tls or source_json required». Без имени — 400 «name is required».
  • 409, если профиль с таким именем уже есть. Тело ограничено 1 МиБ.
GET/api/v1/scraper/tasks/{id}/profile/viewТребуется авторизация

Показывает профиль, под которым идёт задача пула портов: тот же состав отпечатка, что уходит в сеть.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/tasks/TASK_ID/profile/view
Ответ
{
  "port": 20134,
  "profile": { "name": "chrome_152_windows", "browser": "chrome",
                "os": "windows", "tls": { "ja3_hash": "…" }, "spec_source": "spec" }
}

port называет, какой именно порт пула проверялся: пул держит их много, и профиль у каждого свой. 409 «scraper task has no open ports yet» и «scraper task ports are not live»; 404, если пула нет.