pfpulse.tech
Независимый сервис распознавания капч Яндекс — принимает картинки и метаданные, возвращает готовый ответ. Интеграция — одна HTTPS-пара запросов (create + result), ничего на стороне клиента разворачивать не нужно.
Метрики собраны на реальном трафике (smart) и независимой валидации против референсных сольверов (pazl) — см. раздел каждого типа ниже.
Как работает интеграция
Клиентский софт делает два HTTPS-запроса к нашему API:
POST /create— отдаёт данные капчи (картинки + меташку), получаетtask_id.POST /result— опрашивает поtask_id, получает готовый ответ.
Всё. Никаких прокси, никаких hosts, никаких MITM — обычный JSON over HTTPS к нашему хосту. Среднее время решения капчи в одном полном цикле (create → solve → result) — <200 ms в p50.
# flow:
your client ──POST /create──▶ pfpulse.tech API
◀─── task_id ──
──POST /result─▶
◀── solution ──
(your client applies solution to the captcha widget)
Quickstart
Минимальный цикл для SmartCaptcha:
# 1) отправляем картинки капчи curl -X POST https://pfpulse.tech/captcha/create \ -H "Content-Type: application/json" \ -d '{ "type": "SmartCaptcha", "click": "<base64 PNG/JPG — основная картинка, на которой надо кликнуть>", "task": "<base64 PNG/JPG — картинка с целевыми силуэтами, в нужном порядке>" }' # ← {"task_id": "9051d4b4-…", "status": "pending"} # 2) опрашиваем результат (1-2 сек обычно достаточно) curl -X POST https://pfpulse.tech/captcha/result \ -H "Content-Type: application/json" \ -d '{"task_id": "9051d4b4-…"}' # ← {"status": "ready", "solution": "coordinates:x=34.7,y=108.0;…", "solve_time_ms": 87}
Для PazlCaptcha:
curl -X POST https://pfpulse.tech/captcha/create \
-H "Content-Type: application/json" \
-d '{
"type": "PazlCaptcha",
"image": "<base64 PNG — сама картинка паззла>",
"task": "[10,24,5,9,8,9,15,…]"
}'
# ← {"task_id": "…", "status": "pending"}
curl -X POST https://pfpulse.tech/captcha/result -d '{"task_id":"…"}' -H "Content-Type: application/json"
# ← {"status": "ready", "solution": "14"} — номер шага слайдера
Для TextCaptcha:
curl -X POST https://pfpulse.tech/captcha/create \
-H "Content-Type: application/json" \
-d '{
"type": "TextCaptcha",
"task": "<base64 PNG/JPG — картинка с текстом капчи>"
}'
# ← {"task_id": "…", "status": "pending"}
curl -X POST https://pfpulse.tech/captcha/result -d '{"task_id":"…"}' -H "Content-Type: application/json"
# ← {"status": "ready", "solution": "молоко дерево"} — 2 русских слова
SmartCaptcha
PRODUCTIONЯндекс-капча типа «silhouette»: две картинки. На click — область с иконками-силуэтами (обычно 5-7 штук). На task — те силуэты, которые нужно кликнуть на click-картинке, нарисованные слева-направо в правильном порядке. Решение = список координат кликов в том же порядке что идут силуэты на task-картинке.
Что присылает клиент
{
"type": "SmartCaptcha",
"click": "<base64 PNG/JPG>", // основная картинка с иконками, 320×180 native или 300×300 CSS
"task": "<base64 PNG/JPG>", // картинка с целевыми силуэтами в нужном порядке
"coord_space": "native" // optional: "native" | "css300" (default "css300")
}
coord_space: в каком пиксельном пространстве вернуть координаты.
native — пиксели исходной картинки как ты её прислал; css300 —
нормированные в 300×300 (для CSS-widget). Большинству клиентов нужен native.
Что возвращает сольвер
"solution": "coordinates:x=34.7,y=108.0;x=234.3,y=72.3;x=72.5,y=62.6;..."
Список пар (x,y) через ; в том же порядке, в котором целевые
силуэты идут на task-картинке (слева-направо). То есть i-я пара — это координата
на click-картинке для i-го силуэта из task. Клиент применяет клики в этом
порядке (human-like задержки 100-300 мс между кликами рекомендуются).
Как мы решаем
Пайплайн из 4 ML-компонент, обученных на ~100K размеченных сэмплов реального трафика:
- Object detection — находим координаты всех силуэтов на click-картинке.
- Task counter — предсказываем сколько силуэтов на task-картинке (обычно 5-7).
- Task parser — режем task-картинку на N отдельных силуэтов в порядке слева-направо.
- Contrastive matcher — эмбеддим каждый детект из click и каждый силуэт из task в общее пространство, считаем similarity-матрицу, Hungarian-матчинг → какой детект соответствует какому силуэту → порядок кликов.
Каждая компонента дообучается периодически на сложных кейсах из uncertain-бакета. Модели не публичные.
PazlCaptcha
PRODUCTION
Яндекс-капча типа «пазл/калейдоскоп»: картинка разбита на сетку N×N квадратных
плиток, и строки плиток сдвинуты серией pair-swap операций. Снизу — слайдер с
диапазоном 0..max, который контролирует сколько swap'ов применено.
При правильной позиции слайдера картинка становится целой. Решение = номер
правильного шага слайдера.
Что присылает клиент
Два формата на выбор:
{
"type": "PazlCaptcha",
"image": "<base64 PNG>", // сама картинка паззла (скрамблированная)
"task": "[10,24,5,9,8,...]" // JSON-массив swap-индексов из DOM-стейта капчи
}
Где клиенту взять поля:
image— загрузить по URL из JS-стейта (imageSrc), потом base64.task— массив из JS-стейта (task). Плоский JSON-массив целых чисел, длина всегда чётная.
Регекспы: imageSrc:"(https://[^"]+)" и task:"(\[[\d,]+\])".
{
"type": "PazlCaptcha",
"task": "<base64 полной HTML-страницы капчи>"
}
Клиент отправляет всю HTML-страницу капчи в base64. Сервер сам парсит HTML,
извлекает imageSrc и task permutation, скачивает картинку и решает.
Полностью совместим с Capsola API /create.
Что возвращает сольвер
"solution": "14" // номер шага слайдера, целое число 0..max
max = len(task) / 2. Клиент применяет ответ любым из двух способов:
- Клики — нажимает стрелку «→» на слайдере ровно N раз.
- Drag — тащит ползунок в позицию
N / maxвдоль длины трека.
Оба способа эквивалентны — в submit капча шлёт значение rep=N которое читается из aria-valuenow слайдера.
Как мы решаем
Чистый детерминистический CV, без обучения. Алгоритм — реверс-инжиниринг JS-рендера капчи:
- Размер сетки
N = ceil(sqrt(max(task)+1)). Для текущей версии капчи всегда 5×5 (25 тайлов). - Для каждого кандидата
t ∈ 0..max: начинаем с identity-перестановки[0..N²−1], применяем первыеtswap-пар из task-массива. - Рендерим картинку-гипотезу: для каждой клетки i берём тайл
source[perm[i]]. - Считаем joint coherence — среднюю L1-разницу цвета в 7-пиксельном окне по обе стороны каждого шва между плитками.
- Аrgmin по
t→ правильный шаг.
Проверено 99.4% exact совпадение с референсным сольвером на 353 независимых сэмплах. Latency ≈17 мс median. Zero-ML — numpy-only. Масштабируется тривиально.
TextCaptcha
BETA БЕСПЛАТНОКапча типа «введите текст с картинки» — Яндекс отдаёт её ночью или подозрительным клиентам. На картинке — два искажённых русских слова. Сольвер на базе OCR принимает изображение, возвращает распознанную строку. Точность — 89.5% на реальных сэмплах. На период бесплатно-теста решение бесплатно.
Что присылает клиент
{
"type": "TextCaptcha",
"task": "<base64 PNG/JPG — картинка с текстом>"
}
Поле task содержит base64-картинку капчи с искажённым текстом.
Совместим с Capsola API.
Что возвращает сольвер
"solution": "молоко дерево" // 2 русских слова через пробел
Строка из двух русских слов, разделённых пробелом. Клиент вводит строку целиком в поле ответа капчи.
Как мы решаем
TPS-STN (Thin Plate Spline Spatial Transformer Network) для выравнивания искажений + OCR-модель attention-based, дообученная на реальных и синтетических сэмплах. Post-processing: pymorphy3-коррекция для исправления ошибок на уровне русской морфологии.
Формат ответа
Все эндпоинты возвращают JSON.
POST /create
{
"task_id": "9051d4b4-0000-44d3-a4c2-db0215227862",
"status": "pending"
}
POST /result
// ready — готово { "task_id": "9051d4b4-...", "status": "ready", "solution": "coordinates:x=34.7,y=108.0;...", "solve_time_ms": 87 } // pending — ещё решается, повтори через 1-2 сек { "task_id": "...", "status": "pending" } // error — не смогли решить / внутренняя ошибка { "task_id": "...", "status": "error", "error": "<причина>" }
Коды ошибок
Все ошибки бьются на две категории:
- Неизвестный
type. - Отсутствуют обязательные поля под выбранный
type(SmartCaptcha требуетclick+task, PazlCaptcha —image+task). - Картинка не base64 / битый формат / меньше 100 байт / больше 200 КБ.
taskне парсится (для PazlCaptcha ожидается JSON-массив чисел).
status: "error" — сольвер не справилсяerror: "no detections"— не нашли нужные элементы в картинке (шумная/битая картинка).error: "counter mismatch"— внутренние модели не согласны между собой (сохранится в uncertain-бакет для дообучения).error: "timeout"— решение не завершилось за SLA (см. лимиты ниже).
При этих ошибках — обычно достаточно запросить у источника новую капчу и послать нам свежую. Retry той же картинкой не поможет.
- TTL task истёк (120 сек от момента
/create). - Неверный
task_id.
Polling
POST /create→ получаешьtask_id.- Ждёшь 1-2 секунды (внутренний solve обычно укладывается в <200 мс, но даём буфер на jitter сети).
POST /resultс{task_id}.- Если
status: "pending"— подожди 2-5 сек и повтори. - Если
status: "ready"— бериsolutionи применяй. - Если
status: "error"— запрашивай новую капчу у источника. - TTL задачи — 120 сек. После этого
task_idпропадает.
Лимиты и SLA
p50 <200 мс · p99 <600 мс
масштабируется горизонтально
/create до /resultFingerprint API
PRODUCTIONГенерация уникальных Android-отпечатков браузера. 457 реальных устройств, 9 браузеров, Canvas/WebGL хеши, сенсоры, шрифты, аудио — полный набор для антидетекта. Подписка по req/min с привязкой IP.
Эндпоинт
GET /api/v1/fingerprint/generate
Параметры
# Базовый запрос (Android Chrome, случайное устройство) curl https://pfpulse.tech/api/v1/fingerprint/generate \ -H "Authorization: Bearer YOUR_API_KEY" # С параметрами curl "https://pfpulse.tech/api/v1/fingerprint/generate?browser=yandex&perfectCanvas=true" \ -H "Authorization: Bearer YOUR_API_KEY"
browser — chrome (default), yandex, samsung, opera, edge, atom, mi, huawei, honor
os — android (default)
perfectCanvas — true для perfect canvas хешей
chrome — фильтр по мажорной версии Chrome, напр. 146
Ответ
JSON ~70KB с полным fingerprint:
// Основные поля { "ua": "Mozilla/5.0 ... Chrome/146.0...", "width": 412, "height": 915, "canvas": "0cd58ae4e8d88d57...", "webgl": "...", "audio": { ... }, "battery": { ... }, "sensor": { ... }, "plugins": [ ... ], "fonts": [ ... ], "webgl_properties": { ... }, "perfectcanvas": { ... } // если perfectCanvas=true }
Тарифы
Безлимит — по запросу. Все тарифы: подписка/мес, привязка IP обязательна.
Legacy API режим
Сервис также поддерживает совместимый «legacy» формат запросов/ответов для клиентов,
которые используют стороннюю API-схему и не могут переключиться на нашу нативную.
В этом режиме отдаются поля status: 1/0, response: <...>
и стандартные error-коды сторонней схемы.
Документация и примеры интеграции для legacy-режима предоставляются по запросу — напиши в поддержку с описанием кейса.