Snapi
Документация для разработчиков

API Документация

Базовый адрес: https://yhfdghkh6544.com/v1. Все методы используют стандартный HTTP. Параметры можно передавать в строке запроса (GET) или в теле JSON (POST).

Быстрый старт

  1. Зарегистрируйтесь бесплатно и создайте ключ на странице «API Key» в консоли.
  2. Передайте ключ в заголовке 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 показывается в открытом виде только один раз — при создании. Сохраните его в надёжном месте. При утечке ключ можно отключить в консоли.

Создание скриншотов

GET/v1/screenshot
POST/v1/screenshot

Параметры только для скриншотов:

ПараметрТипПо умолчаниюОписание
formatenumpngpng, jpeg или webp.
device_scale_factornumber1Коэффициент масштабирования: 1–3. Значение 2 даёт чёткость Retina, размер файла увеличивается примерно в 4 раза.
qualityinteger80Качество JPEG / WebP: 1–100.
full_pagebooleanfalseСкриншот всей страницы. Страница прокручивается экран за экраном для запуска отложенной загрузки. Максимальная высота — 20,000 пикселей.
selectorstring—Скриншот только первого элемента, соответствующего CSS-селектору. Нельзя использовать вместе с full_page.

Общие параметры для скриншотов и PDF:

ПараметрТипПо умолчаниюОписание
urlstring—Адрес страницы для скриншота: http или https. Укажите либо этот параметр, либо html. Фрагмент # в URL сохраняется: можно сразу снять нужную страницу одностраничного приложения с hash-маршрутизацией, например https://app.com/#/report/1.
htmlstring—HTML-код напрямую (не более 2 MB). Только POST.
viewport_widthinteger1280Ширина области просмотра: 320–3840. При значении меньше 768 страница отображается как на мобильном устройстве.
viewport_heightinteger800Высота области просмотра: 200–3840.
wait_untilenumnetworkidleСтратегия ожидания: domcontentloaded (самая быстрая, для статических страниц), load, networkidle (отслеживает все запросы с момента открытия страницы; после загрузки ждёт 500 мс без активных запросов, но не более 8 секунд дополнительно; подходит для одностраничных приложений).
wait_for_selectorstring—После загрузки страницы ждёт появления и видимости элемента, соответствующего CSS-селектору, и только затем делает скриншот. Ожидание — до 10 секунд; если элемент не появился, возвращается selector_not_found (без списания квоты). Подходит для одностраничных приложений, где контент появляется по таймеру или после анимации, а не по завершении сетевых запросов, например .chart-ready.
delayinteger0Дополнительная задержка после завершения ожидания, в миллисекундах: 0–10000. Позволяет дождаться анимации или отложенной загрузки компонентов.
block_adsbooleantrueБлокирует запросы к рекламным и отслеживающим доменам.
block_cookie_bannersbooleantrueСкрывает баннеры согласия на использование cookie.
dark_modebooleanfalseЭмулирует prefers-color-scheme: dark.
hide_selectorsstring—Список CSS-селекторов элементов, которые нужно скрыть, например .ad,#popup.
cachebooleantrueПроверять ли кеш. При false страница рендерится заново, но результат всё равно сохраняется в кеше.
cache_ttlinteger86400Срок хранения в кеше, в секундах: 60–2592000.
response_typeenumbinarybinary — вернуть файл напрямую; json — вернуть адрес файла и метаданные.

Скриншоты одностраничных приложений (React, Vue и др.):Перед созданием скриншота страница выполняет JavaScript в настоящем браузере. По умолчанию networkidle ждёт завершения загрузки данных. Если после этого контент появляется по таймеру или после анимации, укажите в wait_for_selector элемент, означающий готовность контента. Если подходящего элемента нет, добавьте время ожидания через delay.

Создание PDF

GET/v1/pdf
POST/v1/pdf

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

ПараметрТипПо умолчаниюОписание
paperenuma4Формат бумаги: a4 или letter. Если на странице задано правило @page, настройки страницы имеют приоритет.
print_backgroundbooleantrueПечатать фоновые цвета и изображения.
pdf_mediaenumprintprint — стили для печати; screen — экранные стили (как на экране).
HTML в PDF
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 или MISS
  • X-Snapi-Truncated: true: скриншот всей страницы обрезан из-за превышения высоты 20,000 пикселей
  • X-Snapi-Format-Changed: webp: PNG размером более 5 MB автоматически преобразован в WebP (при явном указании format=png преобразование не выполняется)

При response_type=json возвращаются адрес файла и метаданные:

Ответ 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…"
  }
}
HTTPcodeЗначениеОплачивается
400invalid_paramsПараметр отсутствует или выходит за допустимый диапазон; message указывает, какой именноНет
400html_too_largehtml превышает 2 MBНет
401unauthorizedAPI Key отсутствует, недействителен или отключёнНет
401signature_invalidПодпись не совпадает (параметры изменены или секрет неверен)Нет
401signature_expiredСрок действия подписанного URL, заданный в expires, истёкНет
403url_blockedДоступ к целевому адресу запрещён (например, адрес внутренней сети или localhost)Нет
408render_timeoutРендеринг превысил 30 секундНет
422render_failedЦелевой сайт вернул 4xx/5xx, произошёл сбой DNS или ошибка сертификата; ответ содержит upstream_statusНет
422selector_not_foundПо selector не найден элемент или истекло время ожидания wait_for_selectorНет
422too_many_redirectsБолее 5 перенаправленийНет
429rate_limitedПревышен лимит запросов в секунду; повторите запрос согласно Retry-AfterНет
429concurrency_exceededПревышен лимит одновременных запросовНет
429quota_exceededМесячная квота исчерпана (бесплатный тариф)Нет
429too_many_batchesБолее 5 незавершённых пакетных заданий одновременноНет
500internalОшибка сервера. Свяжитесь с нами и укажите request_idНет

Асинхронная обработка

POST/v1/batch
GET/v1/batch/{id}

Отправьте 1–100 адресов одним запросом. Метод сразу вернёт ID задания (HTTP 202), а скриншоты будут создаваться по очереди в фоне. Подходит для пакетной обработки без долгого ожидания ответа. Можно периодически запрашивать статус или указать webhook_url, чтобы получить уведомление по завершении. Аутентификация такая же, как у синхронных методов (только через заголовок или параметр access_key; подписанные URL не поддерживаются).

ПараметрТипПо умолчаниюОписание
typeenumscreenshotscreenshot (скриншот) или pdf.
urlsstring[]—Адреса для обработки: 1–100. Если хотя бы один некорректен, весь запрос возвращает 400; message указывает его порядковый номер.
optionsobject{}Общие параметры для всех адресов. Совпадают с параметрами синхронных методов (например, full_page, format, viewport_width, cache_ttl), но не могут содержать url, html, response_type.
webhook_urlstring—Необязательный параметр. Адрес для уведомления после завершения всей обработки. См. раздел «Уведомления о завершении».
Создание пакетного задания
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)

Статистика использования

GET/v1/usage
shell
curl "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", ... }

Проверка доступности

GET/v1/health

Не требует авторизации. Возвращает состояние узла и пула браузеров: общее число, занятые и свободные.