API Документация
Базовый адрес: https://yhfdghkh6544.com/v1. Все методы используют стандартный HTTP. Параметры можно передавать в строке запроса (GET) или в теле JSON (POST).
Быстрый старт
- Зарегистрируйтесь бесплатно и создайте ключ на странице «API Key» в консоли.
- Передайте ключ в заголовке
X-Access-Keyи вызовите метод создания скриншота:
curl "https://yhfdghkh6544.com/v1/screenshot?url=https://example.com&full_page=true" \
-H "X-Access-Key: $SNAPI_KEY" \
-o screenshot.pngАутентификация
Каждый запрос требует API Key. Передать его можно любым из способов:
- Заголовок
X-Access-Key: snp_…(рекомендуется для серверных запросов) - Параметр строки запроса
access_key=snp_… - Подписанный URL (для размещения адреса на веб-странице без раскрытия секрета)
API Key показывается в открытом виде только один раз — при создании. Сохраните его в надёжном месте. При утечке ключ можно отключить в консоли.
Создание скриншотов
/v1/screenshot/v1/screenshotПараметры только для скриншотов:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| format | enum | png | png, jpeg или webp. |
| device_scale_factor | number | 1 | Коэффициент масштабирования: 1–3. Значение 2 даёт чёткость Retina, размер файла увеличивается примерно в 4 раза. |
| quality | integer | 80 | Качество JPEG / WebP: 1–100. |
| full_page | boolean | false | Скриншот всей страницы. Страница прокручивается экран за экраном для запуска отложенной загрузки. Максимальная высота — 20,000 пикселей. |
| selector | string | — | Скриншот только первого элемента, соответствующего CSS-селектору. Нельзя использовать вместе с full_page. |
Общие параметры для скриншотов и PDF:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| url | string | — | Адрес страницы для скриншота: http или https. Укажите либо этот параметр, либо html. Фрагмент # в URL сохраняется: можно сразу снять нужную страницу одностраничного приложения с hash-маршрутизацией, например https://app.com/#/report/1. |
| html | string | — | HTML-код напрямую (не более 2 MB). Только POST. |
| viewport_width | integer | 1280 | Ширина области просмотра: 320–3840. При значении меньше 768 страница отображается как на мобильном устройстве. |
| viewport_height | integer | 800 | Высота области просмотра: 200–3840. |
| wait_until | enum | networkidle | Стратегия ожидания: domcontentloaded (самая быстрая, для статических страниц), load, networkidle (отслеживает все запросы с момента открытия страницы; после загрузки ждёт 500 мс без активных запросов, но не более 8 секунд дополнительно; подходит для одностраничных приложений). |
| wait_for_selector | string | — | После загрузки страницы ждёт появления и видимости элемента, соответствующего CSS-селектору, и только затем делает скриншот. Ожидание — до 10 секунд; если элемент не появился, возвращается selector_not_found (без списания квоты). Подходит для одностраничных приложений, где контент появляется по таймеру или после анимации, а не по завершении сетевых запросов, например .chart-ready. |
| delay | integer | 0 | Дополнительная задержка после завершения ожидания, в миллисекундах: 0–10000. Позволяет дождаться анимации или отложенной загрузки компонентов. |
| block_ads | boolean | true | Блокирует запросы к рекламным и отслеживающим доменам. |
| block_cookie_banners | boolean | true | Скрывает баннеры согласия на использование cookie. |
| dark_mode | boolean | false | Эмулирует prefers-color-scheme: dark. |
| hide_selectors | string | — | Список CSS-селекторов элементов, которые нужно скрыть, например .ad,#popup. |
| cache | boolean | true | Проверять ли кеш. При false страница рендерится заново, но результат всё равно сохраняется в кеше. |
| cache_ttl | integer | 86400 | Срок хранения в кеше, в секундах: 60–2592000. |
| response_type | enum | binary | binary — вернуть файл напрямую; json — вернуть адрес файла и метаданные. |
Скриншоты одностраничных приложений (React, Vue и др.):Перед созданием скриншота страница выполняет JavaScript в настоящем браузере. По умолчанию networkidle ждёт завершения загрузки данных. Если после этого контент появляется по таймеру или после анимации, укажите в wait_for_selector элемент, означающий готовность контента. Если подходящего элемента нет, добавьте время ожидания через delay.
Создание PDF
/v1/pdf/v1/pdfЗа каждый PDF списывается 2 единицы квоты. Перед печатью страница прокручивается, чтобы загрузить отложенный контент. Помимо общих параметров, доступны:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| paper | enum | a4 | Формат бумаги: a4 или letter. Если на странице задано правило @page, настройки страницы имеют приоритет. |
| print_background | boolean | true | Печатать фоновые цвета и изображения. |
| pdf_media | enum | print — стили для печати; screen — экранные стили (как на экране). |
curl -X POST "https://yhfdghkh6544.com/v1/pdf" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Квартальный отчёт</h1><p>Основной текст…</p>", "paper": "a4"}' \
-o report.pdfОтвет API
По умолчанию (response_type=binary) файл возвращается напрямую, а Content-Type соответствует его формату. Заголовки ответа:
X-Request-Id: ID запроса — укажите его при обращении в поддержкуX-Snapi-Cache:HITилиMISSX-Snapi-Truncated: true: скриншот всей страницы обрезан из-за превышения высоты 20,000 пикселейX-Snapi-Format-Changed: webp: PNG размером более 5 MB автоматически преобразован в WebP (при явном указании format=png преобразование не выполняется)
При response_type=json возвращаются адрес файла и метаданные:
curl -X POST "https://yhfdghkh6544.com/v1/screenshot" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "response_type": "json"}'
# Возвращает
{
"url": "https://yhfdghkh6544.com/i/3f/3f9c….png",
"cache_hit": false,
"duration_ms": 1284,
"format": "png",
"width": 1280,
"height": 800,
"request_id": "req_01j…"
}Подписанные URL
Хотите указать адрес скриншота прямо в <img src>, не раскрывая API Key? Вычислите подпись на своём сервере с помощью секрета, связанного с ключом (секрет доступен на странице API Key в консоли):
canonical = Отсортируйте все параметры, кроме signature, в лексикографическом порядке по имени.
Представьте каждый параметр как k=urlencode(v) (RFC 3986, пробел кодируется как %20) и соедините через &
signature = hex(HMAC-SHA256(secret, canonical))Корректная подпись подтверждает аутентификацию. Изменение любого параметра (например, url) делает подпись недействительной. Необязательный параметр expires (в секундах Unix) участвует в подписи; после истечения срока возвращается signature_expired. Чтобы обновить кеш, измените незначимый параметр (например, v=2) и заново подпишите URL. Подписанные URL поддерживают только GET.
import crypto from "node:crypto";
// Кодирование по RFC 3986 (пробел — %20) должно соответствовать правилам сервера
const enc = (v) =>
encodeURIComponent(v).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());
function signedUrl(params, accessKey, secret) {
const all = { ...params, access_key: accessKey };
const canonical = Object.keys(all).sort().map((k) => `${k}=${enc(String(all[k]))}`).join("&");
const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
return `https://yhfdghkh6544.com/v1/screenshot?${canonical}&signature=${signature}`;
}
const src = signedUrl({ url: "https://example.com", viewport_width: 1200 }, KEY, SECRET);
// <img src={src} alt="Предпросмотр веб-страницы" />Кеширование
Запросы с одинаковыми параметрами используют кеш (пропущенный параметр и явно заданное значение по умолчанию считаются одинаковыми; параметры аутентификации и кеширования не сравниваются). При попадании в кеш в бинарном режиме возвращается перенаправление 302 на адрес файла с заголовком Cache-Control; браузер автоматически переходит по адресу и кеширует файл. В режиме JSON возвращается cache_hit: true.
Запросы из кеша не оплачиваются.Одновременные запросы с одинаковыми параметрами запускают рендеринг только один раз.
Лимиты и оплата
- Оплачиваются только успешные запросы (2xx), не попавшие в кеш: скриншот — 1 единица квоты, PDF — 2.
- Тайм-ауты, сбои рендеринга, ошибки целевого сайта, неверные параметры и ошибки аутентификации не расходуют квоту.
- Квота обновляется каждый календарный месяц (UTC). В бесплатном тарифе доступно 100 запросов в месяц; после исчерпания квоты возвращается quota_exceeded.
- При превышении лимита запросов в секунду или одновременных запросов возвращается 429. Заголовок Retry-After содержит рекомендуемое время ожидания в секундах.
- Рендеринг — не более 30 секунд; загрузка данных одной страницы — не более 50 MB; размер итогового файла — не более 50 MB.
Подробные лимиты тарифов: Тарифы.
Коды ошибок
Все ошибки возвращаются в формате JSON:
{
"error": {
"code": "render_failed",
"message": "target responded with an error status",
"upstream_status": 404,
"request_id": "req_01j…"
}
}| HTTP | code | Значение | Оплачивается |
|---|---|---|---|
| 400 | invalid_params | Параметр отсутствует или выходит за допустимый диапазон; message указывает, какой именно | Нет |
| 400 | html_too_large | html превышает 2 MB | Нет |
| 401 | unauthorized | API Key отсутствует, недействителен или отключён | Нет |
| 401 | signature_invalid | Подпись не совпадает (параметры изменены или секрет неверен) | Нет |
| 401 | signature_expired | Срок действия подписанного URL, заданный в expires, истёк | Нет |
| 403 | url_blocked | Доступ к целевому адресу запрещён (например, адрес внутренней сети или localhost) | Нет |
| 408 | render_timeout | Рендеринг превысил 30 секунд | Нет |
| 422 | render_failed | Целевой сайт вернул 4xx/5xx, произошёл сбой DNS или ошибка сертификата; ответ содержит upstream_status | Нет |
| 422 | selector_not_found | По selector не найден элемент или истекло время ожидания wait_for_selector | Нет |
| 422 | too_many_redirects | Более 5 перенаправлений | Нет |
| 429 | rate_limited | Превышен лимит запросов в секунду; повторите запрос согласно Retry-After | Нет |
| 429 | concurrency_exceeded | Превышен лимит одновременных запросов | Нет |
| 429 | quota_exceeded | Месячная квота исчерпана (бесплатный тариф) | Нет |
| 429 | too_many_batches | Более 5 незавершённых пакетных заданий одновременно | Нет |
| 500 | internal | Ошибка сервера. Свяжитесь с нами и укажите request_id | Нет |
Асинхронная обработка
/v1/batch/v1/batch/{id}Отправьте 1–100 адресов одним запросом. Метод сразу вернёт ID задания (HTTP 202), а скриншоты будут создаваться по очереди в фоне. Подходит для пакетной обработки без долгого ожидания ответа. Можно периодически запрашивать статус или указать webhook_url, чтобы получить уведомление по завершении. Аутентификация такая же, как у синхронных методов (только через заголовок или параметр access_key; подписанные URL не поддерживаются).
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| type | enum | screenshot | screenshot (скриншот) или pdf. |
| urls | string[] | — | Адреса для обработки: 1–100. Если хотя бы один некорректен, весь запрос возвращает 400; message указывает его порядковый номер. |
| options | object | {} | Общие параметры для всех адресов. Совпадают с параметрами синхронных методов (например, full_page, format, viewport_width, cache_ttl), но не могут содержать url, html, response_type. |
| webhook_url | string | — | Необязательный параметр. Адрес для уведомления после завершения всей обработки. См. раздел «Уведомления о завершении». |
curl -X POST "https://yhfdghkh6544.com/v1/batch" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "screenshot",
"urls": ["https://example.com", "https://example.org"],
"options": { "full_page": true, "format": "webp" },
"webhook_url": "https://your-app.com/hooks/snapi"
}'
# Возвращает 202
{
"id": "bat_01j…",
"type": "screenshot",
"status": "queued",
"total": 2, "succeeded": 0, "failed": 0,
"created_at": "2026-10-18T09:30:00Z", "completed_at": null,
"webhook": { "url": "https://your-app.com/hooks/snapi", "status": "none", "attempts": 0 },
"items": [
{ "index": 0, "url": "https://example.com", "status": "pending", "result": null, "error": null },
{ "index": 1, "url": "https://example.org", "status": "pending", "result": null, "error": null }
]
}Проверка прогресса: статус задания status последовательно принимает значения queued (в очереди), running (обрабатывается), completed (обработка завершена, независимо от результата). Статус каждого адреса — pending, running, succeeded или failed. При успехе result содержит адрес файла, при ошибке error содержит код ошибки (такой же, как у синхронных методов).
curl "https://yhfdghkh6544.com/v1/batch/bat_01j…" -H "X-Access-Key: $SNAPI_KEY"
{
"id": "bat_01j…",
"status": "completed",
"total": 2, "succeeded": 1, "failed": 1,
"completed_at": "2026-10-18T09:30:12Z",
"items": [
{
"index": 0, "url": "https://example.com", "status": "succeeded", "duration_ms": 1830,
"result": { "url": "https://yhfdghkh6544.com/i/3f/3f9c….webp", "mime": "image/webp", "width": 1280, "height": 2410, "cache_hit": false },
"error": null
},
{
"index": 1, "url": "https://example.org", "status": "failed", "duration_ms": 640,
"result": null,
"error": { "code": "render_failed", "message": "target responded with an error status" }
}
]
}- Оплата как у синхронных методов: за каждый успешно обработанный адрес списывается 1 единица квоты для скриншота или 2 для PDF. Результаты из кеша и ошибки квоту не расходуют.
- Если квота исчерпана при отправке задания, сразу возвращается
quota_exceeded. Если она закончится во время обработки, оставшиеся адреса завершатся с ошибкойquota_exceeded. - На аккаунт допускается не более 5 незавершённых пакетных заданий одновременно. При превышении возвращается
too_many_batches. - Срок хранения файлов результата определяется параметром
options.cache_ttl(по умолчанию 1 день). Скачайте и сохраните их вовремя. - В результатах запроса и уведомлениях
webhook.urlотображается только до пути, без строки запроса.
Уведомления о завершении
Если указан webhook_url, после завершения всего задания мы отправим на него запрос POST с телом JSON: { "event": "batch.completed", "batch": { … } }. Поле batch полностью совпадает с ответом метода проверки статуса. Заголовки:
X-Snapi-Signature: t=1760779812,v1=5d3f…:t— время отправки (в секундах Unix),v1— подписьX-Snapi-Event: batch.completed,X-Snapi-Delivery-Attempt: 1(номер попытки доставки)
v1 = hex(HMAC-SHA256(secret, t + "." + Исходное тело запроса))
secret — секрет для подписи, связанный с API Key, использованным при создании задачи (доступен на странице API Key в консоли).- Верните ответ 2xx в течение 10 секунд, иначе доставка считается неудачной. При сбое выполняется по одной повторной попытке через 10 секунд, 1 минуту и 5 минут (не более 4 попыток всего).
- В редких случаях уведомление об одной задаче может прийти повторно. Удаляйте дубликаты по
batch.id. - Для проверки подписи используйте полученные исходные байты. Убедитесь, что
tотличается от текущего времени не более чем на несколько минут, чтобы предотвратить атаки повторного воспроизведения. - Адрес обратного вызова должен быть общедоступным адресом http(s) и не может указывать на внутреннюю сеть.
import crypto from "node:crypto";
// rawBody должен содержать исходные полученные байты. Нельзя сначала выполнять JSON.parse, а затем повторную сериализацию
function verifySnapiWebhook(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // Защита от повторного воспроизведения
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const got = Buffer.from(parts.v1 ?? "");
return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}
// verifySnapiWebhook(req.rawBody, req.headers["x-snapi-signature"], SNAPI_SECRET)Статистика использования
/v1/usagecurl "https://yhfdghkh6544.com/v1/usage" -H "X-Access-Key: $SNAPI_KEY"
{ "month": "202610", "used": 37, "limit": 100, "remaining": 63, "overage": 0, "resets_at": "2026-11-01T00:00:00Z", ... }Проверка доступности
/v1/healthНе требует авторизации. Возвращает состояние узла и пула браузеров: общее число, занятые и свободные.