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

Создайте ключ, вызовите POST /v1/generate с бэкенда, используйте SVG на сайте. Пошаговая инструкция ниже.

Подключение Static QR API

Полный путь: создайте ключ в WebQR, вызовите API со своего бэкенда, возьмите SVG из ответа и используйте на своём сайте. API stateless — WebQR ваши QR не хранит.

Пошаговая интеграция

Идите по шагам по порядку. SDK не нужен — HTTPS и JSON только с вашего сервера. Не кладите API-ключ в JavaScript браузера и не вшивайте в приложение.

  1. Аккаунт и тариф

    Войдите в WebQR на тарифе с доступом к Static QR API. Сравните включённые генерации в месяц и лимиты ключей на странице тарифов.

    Тарифы и цены

  2. Создайте API-ключ

    В кабинете откройте API-ключи, создайте ключ и сразу скопируйте секрет (показывается один раз). При желании ограничьте IP и включите обязательную подпись запросов для продакшена.

    API-ключи

  3. Сохраните ключ на сервере

    Положите секрет в переменные окружения или менеджер секретов. Все вызовы — только с бэкенда. Не отдавайте ключ на фронт и не вшивайте в мобильное приложение.

  4. Вызовите POST /v1/generate

    Заголовки: X-API-Key и Content-Type: application/json. Тело: обязательная строка data (URL или текст) и опциональный объект design. Примеры ниже — подставьте свой ключ вместо образца.

    https://api.webqr.io/v1/generate
    curl -X POST https://api.webqr.io/v1/generate \
      -H "Content-Type: application/json" \
      -H "X-API-Key: wq_xxxxxxxxxx_your_secret" \
      -d '{
        "data": "https://webqr.io",
        "design": {
            "size": 512,
            "color": "#000000",
            "backgroundColor": "#FFFFFF",
            "styleType": "a7k2m9",
            "eyeType": "r2s4t6",
            "showColorGradient": false
        }
    }'
    <?php
    $ch = curl_init('https://api.webqr.io/v1/generate');
    curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-API-Key: wq_xxxxxxxxxx_your_secret'
      ],
      CURLOPT_POSTFIELDS => '{
        "data": "https://webqr.io",
        "design": {
            "size": 512,
            "color": "#000000",
            "backgroundColor": "#FFFFFF",
            "styleType": "a7k2m9",
            "eyeType": "r2s4t6",
            "showColorGradient": false
        }
    }',
    ]);
    echo curl_exec($ch);
    const res = await fetch('https://api.webqr.io/v1/generate', {
      method: 'POST',
      headers: {
          "Content-Type": "application/json",
          "X-API-Key": "wq_xxxxxxxxxx_your_secret"
      },
      body: JSON.stringify({
        "data": "https://webqr.io",
        "design": {
            "size": 512,
            "color": "#000000",
            "backgroundColor": "#FFFFFF",
            "styleType": "a7k2m9",
            "eyeType": "r2s4t6",
            "showColorGradient": false
        }
    }),
    });
    const data = await res.json();
    console.log(data);
    import requests
    
    r = requests.post(
      "https://api.webqr.io/v1/generate",
      headers={
          "Content-Type": "application/json",
          "X-API-Key": "wq_xxxxxxxxxx_your_secret"
      },
      json={
          "data": "https://webqr.io",
          "design": {
              "size": 512,
              "color": "#000000",
              "backgroundColor": "#FFFFFF",
              "styleType": "a7k2m9",
              "eyeType": "r2s4t6",
              "showColorGradient": false
          }
      },
    )
    print(r.json())
  5. Заберите SVG из ответа

    При HTTP 200 возьмите data.qr_code (разметка SVG) и data.format ("svg"). В usage — счётчики квоты по этому запросу. При ошибках смотрите HTTP-статус и error.code, если он есть.

    Ответ

  6. Используйте SVG на своём сайте

    Сохраните файл, выложите на CDN, вставьте inline, приложите к письму или отправьте в печать. WebQR результат не хранит — хранение и id у вас.

  7. Следите за квотой и биллингом

    GET /v1/usage отдаёт счётчики без расхода квоты. Лимиты тарифа — в разделе Limits; если можете выйти за включённые генерации, подключите overage-биллинг в кабинете.

    Usage · Лимиты API · Биллинг API в кабинете

  8. Дизайн, безопасность, ошибки

    Дальше — поля дизайна, защита ключа и обработка ошибок. Чеклист для продакшена:

    • В проде ограничьте ключ по IP allowlist, если у серверов стабильный исходящий IP.
    • Включите обязательную подпись запросов (HMAC) для ключей вне закрытой сети.
    • При 429 учитывайте Retry-After, если есть; при 402 сначала проверьте биллинг или потолок трат.

    Параметры тела · Безопасность · Ошибки

Скачать

POST /generate

Справка по POST /v1/generate: авторизация, URL, заголовки и верхний уровень тела. Полные примеры curl/PHP/JS/Python — в Инструкции.

Авторизация

ApiKeyAuth

Передавайте секрет в X-API-Key в каждом запросе. Если для ключа включена подпись — также X-WebQR-Timestamp и X-WebQR-Signature.

Метод запроса

POST

URL запроса

https://api.webqr.io/v1/generate

Заголовки

Параметр Тип Описание
X-API-Key Обязательно
string Секрет API-ключа.
Content-Type Обязательно
string Отправляйте application/json, иначе тело часто не парсится и всплывает валидация по data.
X-WebQR-Timestamp
string Unix timestamp (секунды). Нужен, если ключ требует подпись.
X-WebQR-Signature
string HMAC-SHA256 hex канонической строки. Нужен, если ключ требует подпись.

Параметры

Параметр Тип Описание
data Обязательно
string Данные для кодирования (URL, текст, Wi‑Fi и т.д.). Макс. 1000 символов.
design
object Необязательный объект дизайна. Пропущенные поля — по умолчанию.

Полный список полей design (styleType, глаза, рамки, логотипы, градиенты) — в разделе Параметры тела.

Параметры тела · Полные примеры запроса — в Инструкции

Успешный ответ (HTTP 200)

JSON: success, SVG в data.qr_code (format svg), content, нормализованный design и полный объект usage (счётчики, время сброса, текстовые подсказки).

{
    "success": true,
    "data": {
        "qr_code": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">…</svg>",
        "format": "svg",
        "content": "https://webqr.io",
        "design": {
            "size": 512,
            "color": "#000000",
            "backgroundColor": "#FFFFFF",
            "styleType": "a7k2m9",
            "eyeType": "r2s4t6",
            "showColorGradient": false
        }
    },
    "usage": {
        "period": "2026-05",
        "used_count": 42,
        "included_limit": 200,
        "paid_count": 0,
        "charged_cents": 0,
        "spending_cap_cents": 1000,
        "included_quota_resets_at": "2026-06-01T00:00:00+00:00",
        "what_counts_toward_limit": "Successful POST /v1/generate that returns SVG.",
        "what_does_not_count": "GET /v1/usage, validation errors, and failed generations.",
        "included_quota_note": "Included quota resets at the start of each calendar month.",
        "overage_linear_floor_hint": "Overage is billed per 1000 paid generations (linear floor)."
    }
}

Usage

Только чтение: счётчики за период, data.items пустой. Лимит генераций не тратит.

Авторизация

ApiKeyAuth

Передавайте секрет в X-API-Key в каждом запросе. Если для ключа включена подпись — также X-WebQR-Timestamp и X-WebQR-Signature.

Метод запроса

GET

URL запроса

https://api.webqr.io/v1/usage

Заголовки

Параметр Тип Описание
X-API-Key Обязательно
string Секрет API-ключа.
X-WebQR-Timestamp
string Unix timestamp (секунды). Нужен, если ключ требует подпись.
X-WebQR-Signature
string HMAC-SHA256 hex канонической строки. Нужен, если ключ требует подпись.

Если для ключа включена обязательная подпись, на GET /v1/usage тоже нужны X-WebQR-Timestamp и X-WebQR-Signature (пустое тело → SHA-256 от "").

Параметры

Нет query/body параметров. Авторизация только через заголовки.

Пример запроса

curl -X GET https://api.webqr.io/v1/usage \
  -H "X-API-Key: wq_xxxxxxxxxx_your_secret"
<?php
$ch = curl_init('https://api.webqr.io/v1/usage');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'X-API-Key: wq_xxxxxxxxxx_your_secret',
  ],
]);
echo curl_exec($ch);
const res = await fetch('https://api.webqr.io/v1/usage', {
  headers: {
    'X-API-Key': 'wq_xxxxxxxxxx_your_secret',
  },
});
const data = await res.json();
console.log(data);
import requests

r = requests.get(
  "https://api.webqr.io/v1/usage",
  headers={
    "X-API-Key": "wq_xxxxxxxxxx_your_secret",
  },
)
print(r.json())

Снимок usage для аккаунта ключа. Квоту генераций не тратит. Объект `usage` такой же, как в ответе POST /v1/generate.

Параметры тела

Тело POST /v1/generate: обязательное data, опциональный design. Коды берите из разделов Модули, Глаза, Рамки и Логотипы.

Параметр Тип Описание
data Обязательно
string Данные для кодирования (URL, текст, Wi‑Fi и т.д.). Макс. 1000 символов.
design
object Необязательный объект дизайна. Пропущенные поля — по умолчанию.
design.size
integer Размер в пикселях (100–2048).
design.color
string Цвет модулей #RRGGBB.
design.backgroundColor
string Цвет фона #RRGGBB.
design.borderColor
string Цвет внешних глаз #RRGGBB.
design.centerColor
string Цвет внутренних глаз #RRGGBB.
design.markerOutColor
string Алиас borderColor.
design.markerInColor
string Алиас centerColor.
design.styleType
string Код формы модулей (см. Коды модулей).
design.eyeType
string Код внешних глаз (см. Внешние глаза).
design.eyeInnerType
string Код внутренних глаз (см. Внутренние глаза).
design.showColorGradient
boolean Градиент на модулях.
design.unifiedGradient
boolean Один градиент на модули и глаза.
design.showAllColorGradient
boolean Алиас unifiedGradient.
design.showEyeGradient
boolean Градиент на глазах.
design.styleColorGradient
string Направление градиента модулей.
design.eyeGradientStyle
string Направление градиента глаз.
design.fromColor
string Начало градиента модулей #RRGGBB.
design.toColor
string Конец градиента модулей #RRGGBB.
design.eyeFromColor
string Начало градиента глаз #RRGGBB.
design.eyeToColor
string Конец градиента глаз #RRGGBB.
design.transparent
boolean Прозрачный фон.
design.title
string Метаданные title (макс. 100).
design.frameType
string Код рамки из раздела Frames. Не передавайте поле или оставьте пустым, если рамка не нужна.
design.frameColor
string Цвет рамки #RRGGBB.
design.showFrameGradient
boolean Градиент рамки.
design.frameGradientFrom
string Начало градиента рамки #RRGGBB.
design.frameGradientTo
string Конец градиента рамки #RRGGBB.
design.frameGradientStyle
string Направление градиента рамки.
design.frameGradientTextColor
string Цвет подписи при градиенте рамки.
design.textColor
string Цвет текста рамки #RRGGBB.
design.textUnderQr
string Подпись под QR (макс. 40; с рамками).
design.roundedCorners
boolean Скругление углов холста QR.
design.cornerRadius
integer Радиус скругления в px (0–120).
design.logo
string Рекомендуется: код пресета (Lxxxxx) или публичный HTTPS URL. Advanced: data URI и часть внутренних storage-путей; для интеграций предпочтителен HTTPS.
design.selectedLogo
string Код пресета или HTTPS URL.
design.logoBackgroundEnabled
boolean Подложка под логотипом.
design.loadLogoBackgroundOut
string Цвет подложки логотипа #RRGGBB (пресеты Lxxxxx).
design.loadLogoBackgroundIn
string Цвет иконки логотипа #RRGGBB (пресеты Lxxxxx).

Формат ответа только SVG (data.format: "svg"). PNG/PDF через этот API недоступны.

Коды модулей (design.styleType)

Используйте строку кода в design.styleType, eyeType, eyeInnerType, frameType или logo.

  • a7k2m9 Квадрат
  • b3n5p1 Квадрат с выемкой
  • c8q4r6 Сквиркл
  • d1s7t9 Скруглённые соединённые
  • e6u2v4 Острые соединённые
  • f9w5x7 Точка
  • g2y8z0 Скруглённая точка
  • h4a6b8 Точка (горизонталь)
  • i0c2d4 Точка (вертикаль)
  • j6e8f0 Ромб
  • k2g4h6 Звезда
  • l8i0j2 Пиксельная звезда
  • m4k6l8 Сердце
  • n0o2p4 Плюс
  • p6q8r0 Скруглённый плюс
  • q1r3s5 Скруглённый квадрат
  • s1r3s5 Скруглённый квадрат

Внешние искатели (design.eyeType)

Используйте строку кода в design.styleType, eyeType, eyeInnerType, frameType или logo.

  • r2s4t6 Квадрат
  • u8v0w2 Скруглённый квадрат
  • x4y6z8 Круг
  • a1b3c5 Ромб
  • d7e9f1 D-форма
  • g3h5i7 D-форма инверсия
  • j9k1l3 Лист
  • m5n7o9 Лист с кругом
  • p1q3r5 Лист с кругом (поворот)
  • s7t9u1 Лист (вариант)
  • v3w5x7 Квадрат с кругом
  • y9z1a3 Скруглённый острый
  • b5c7d9 Сквиркл
  • e1f3g5 Капля

Внутренние искатели (design.eyeInnerType)

Используйте строку кода в design.styleType, eyeType, eyeInnerType, frameType или logo.

  • h7i9j1 Квадрат
  • k3l5m7 Сквиркл
  • n9o1p3 Круг
  • q5r7s9 Скошенный квадрат
  • t1u3v5 D-форма
  • w7x9y1 D-форма инверсия
  • z3a5b7 Лист
  • c9d1e3 Звезда
  • f5g7h9 Ромб
  • i1j3k5 X-форма
  • l7m9n1 Плюс
  • o3p5q7 Клевер
  • r9s1t3 Скруглённый X
  • u5v7w9 Сердце

Рамки (design.frameType)

n0f1r2 = без рамки (то же, что не передавать frameType или пустая строка). t7r8d9 — толстая скруглённая обводка без подписи. У остальных рамок подпись может быть в полосе; textUnderQr необязателен — иначе сервер подставляет подпись по умолчанию.

  • n0f1r2 Без рамки
  • t7r8d9 Толстая скруглённая рамка
  • l4b5t6 Метка снизу
  • b3n4t5 Баннер сверху
  • b7b8d9 Бэдж снизу
  • b5d6t7 Бэдж сверху
  • b9b0t1 Планка снизу
  • b2t3p4 Планка сверху
  • w4t5p6 Широкая планка сверху
  • w1d2b3 Широкая планка снизу
  • p0l1b2 Пилюля снизу
  • p8t9p0 Пилюля сверху
  • s6b7g8 Пакет

Готовые логотипы (design.logo)

Те же пресеты, что в конструкторе: передайте код (La1b2c…) в design.logo или design.selectedLogo. Для пресетов — loadLogoBackgroundOut и loadLogoBackgroundIn (#RRGGBB). Также https-URL и data:image/…; logoBackgroundEnabled — подложка под своим логотипом. Подробнее: docs/qr-logos.md в репозитории.

  • La1b2c Facebook
  • Ld3e4f Messenger
  • Lg5h6i Instagram
  • Lj7k8l LinkedIn
  • Lm9n0o YouTube
  • Ls3t4u Phone
  • Lv5w6x Telegram
  • Ly7z8a SMS
  • Lb9c0d Email
  • Le1f2g Wi‑Fi
  • Lh3i4j Restaurant
  • Lk5l6m Location
  • Ln7o8p Gallery
  • Lq9r0s Profile
  • Lt1u2v WhatsApp
  • Lw3x4y Link
  • Lz5a6b Promo
  • Lc7d8e Lock

Поля логотипа

logo и selectedLogo — синонимы. Цвета loadLogoBackground* действуют только на пресеты Lxxxxx.

  • design.logo
  • design.selectedLogo
  • design.loadLogoBackgroundOut
  • design.loadLogoBackgroundIn
  • design.logoBackgroundEnabled

Направления градиентов

Используйте эти имена в styleColorGradient и eyeGradientStyle.

  • horizontal
  • vertical
  • diagonal
  • inverse_diagonal
  • radial

Лимиты API

Включённые генерации в месяц, число ключей и rate limit по тарифу. Цены продуктов — на странице тарифов.

По тарифам

Включённые успешные генерации в календарный месяц, активные ключи и rate limit по плану.

Тариф Включено / месяц Активные ключи Rate limit
Starter 200 генераций до 1 30/мин · burst 2/с
Premium 5000 генераций до 5 120/мин · burst 8/с
Business 25000 генераций до 15 300/мин · burst 15/с
Enterprise 50000 генераций до 30 600/мин · burst 25/с

Нужно больше или индивидуальные условия? Свяжитесь с нами — подберём лимиты и условия под ваш кейс.

Квота обновляется в начале календарного месяца. Лимит на ключ — после авторизации. До ключа (нет/неверный) — лимит по IP.

Что считается и что нет

  • Только успешный POST /v1/generate с SVG в ответе даёт +1 к used_count.
  • Включённый лимит обновляется каждый календарный месяц; неиспользованное не переносится.
  • GET /v1/usage квоту не тратит — для дашбордов и алертов.
  • С ключом: лимит на ключ (в минуту + короткий burst). До ключа / неверный ключ: около 45 запросов в минуту с IP.
  • API не складывает QR в библиотеку WebQR. SVG и свои id храните сами.
  • Успешный ответ — только SVG; эндпоинтов PNG/PDF у Static QR API нет.
  • После включённой квоты платный overage ≈ $2 за 1000 генераций, если включён в кабинете. Дефолтный spending cap ≈ $10/мес (настраивается, есть верхний предел).

Связанное

Как считается квота

В used_count идёт только успешный POST /v1/generate с SVG. GET /v1/usage квоту не тратит. Список полей ниже — как в объекте usage ответа.

  • Одна успешная генерация = +1 used_count. Ошибки валидации и 4xx/5xx не считаются.
  • GET /v1/usage — для дашбордов и алертов: те же счётчики, без списания.
  • Если overage подключён и статус payment_failed — весь Static QR API заблокирован (включая included quota), пока не исправлена оплата.
  • Строки-подсказки (what_counts_toward_limit и др.) — для людей; автоматику стройте по числам и error.code.

Поля объекта `usage` в ответах generate и usage:

  • period
  • used_count
  • included_limit
  • paid_count
  • charged_cents
  • spending_cap_cents
  • included_quota_resets_at
  • what_counts_toward_limit
  • what_does_not_count
  • included_quota_note
  • overage_linear_floor_hint

Биллинг API в кабинете

Типичные HTTP-ошибки

Одного HTTP-статуса мало — форма JSON зависит от слоя. Шлюз (нет/неверный ключ, IP) часто отдаёт `{ "error": "…" }`. Квота и биллинг — `{ "success": false, "error": { "code", "message" } }`. Валидация — Laravel `{ "errors": {…} }` (часто с `message`). Сбои генерации могут быть `{ "success": false, "message" }` без `error.code`. Язык текста может быть EN или RU — для машинной логики опирайтесь на статус и `error.code`, если он есть.

HTTP error.code Смысл
401 Нет заголовка X-API-Key.
401 Неверный, отозванный или просроченный ключ.
401 Подпись отсутствует/неверна при отправке заголовков, или неверна/просрочена при обязательной подписи.
403 IP клиента не в allowlist этого ключа.
402 billing_payment_required Overage-биллинг в статусе payment_failed — весь Static QR API заблокирован, включая включённую квоту, пока не исправлена оплата.
402 free_limit_reached Исчерпана месячная включённая квота, overage-биллинг не подключён.
402 spending_cap_reached Достигнут месячный потолок списаний за overage.
422 Ошибка валидации (типы, неизвестные коды стилей, HEX, длина полей, URL логотипа и т.д.).
429 rate_limited Слишком много запросов с IP (до ключа) или с ключа. Повторите позже; учитывайте Retry-After, если есть.
500 Сбой генерации — повторите позже.

Формы JSON по статусам

Типичные тела ответов. Текст может отличаться; стабильный сигнал — `error.code`, когда он есть.

{
    "error": "Invalid API key."
}
{
    "success": false,
    "error": {
        "code": "billing_payment_required",
        "message": "API billing payment failed or is overdue."
    }
}
{
    "message": "The data field is required.",
    "errors": {
        "data": [
            "The data field is required."
        ]
    }
}
{
    "success": false,
    "error": {
        "code": "rate_limited",
        "message": "Too many requests. Try again later."
    }
}
{
    "success": false,
    "message": "QR code generation failed. Please try again later."
}

Безопасность в продакшене

Относитесь к API‑ключу как к паролю к квоте WebQR.

  • Храните ключ только на сервере — не в браузере, мобильном приложении и не в публичных репозиториях.
  • Вызывайте api.* с бэкенда. CORS для Static QR API в браузере не рассчитан — используйте сервер или Конструктор API в кабинете.
  • Опционально ограничьте ключ IP серверов (или egress CDN). Пустой allowlist = любой IP.
  • Опционально включите HMAC в кабинете. Тогда оба заголовка нужны на каждом маршруте — включая GET /v1/usage.

HMAC-подпись запросов (если включена для ключа)

Строка для подписи (UTF‑8)

1714412345
POST
/v1/generate
<sha256_hex_сырого_тела>

Для GET тело пустое. В последней строке — SHA-256 пустой строки:

1714412345
GET
/v1/usage
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Секрет HMAC: 40 символов после второго «_». Подпись — lowercase hex. Timestamp: Unix-секунды, допуск ±5 минут. SHA-256 тела — по точным байтам запроса. Строка path должна совпадать с путём URL (/v1/generate или /v1/usage).

Как посчитать подпись

Секрет — 40 символов после второго «_» в ключе. Подпись — lowercase hex HMAC-SHA256 канонической строки. Допуск по времени: ±5 минут.

# Build signature on your server, then attach headers to curl
TS=$(date +%s)
BODY='{"data":"https://webqr.io"}'
BODY_HASH=$(printf %s "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')
SECRET="${API_KEY##*_}"  # 40 chars after the second underscore
CANON=$(printf '%s\nPOST\n/v1/generate\n%s' "$TS" "$BODY_HASH")
SIG=$(printf %s "$CANON" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -X POST https://api.webqr.io/v1/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "X-WebQR-Timestamp: $TS" \
  -H "X-WebQR-Signature: $SIG" \
  -d "$BODY"
<?php
$apiKey = getenv('WEBQR_API_KEY');
$secret = substr($apiKey, strrpos($apiKey, '_') + 1);
$ts = (string) time();
$body = json_encode(['data' => 'https://webqr.io'], JSON_UNESCAPED_SLASHES);
$canonical = $ts . "\nPOST\n/v1/generate\n" . hash('sha256', $body);
$signature = hash_hmac('sha256', $canonical, $secret);

$ch = curl_init('https://api.webqr.io/v1/generate');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',
    'X-API-Key: ' . $apiKey,
    'X-WebQR-Timestamp: ' . $ts,
    'X-WebQR-Signature: ' . $signature,
  ],
  CURLOPT_POSTFIELDS => $body,
]);
echo curl_exec($ch);
import crypto from 'node:crypto';

const apiKey = process.env.WEBQR_API_KEY;
const secret = apiKey.slice(apiKey.lastIndexOf('_') + 1);
const ts = String(Math.floor(Date.now() / 1000));
const body = JSON.stringify({ data: 'https://webqr.io' });
const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const canonical = `${ts}\nPOST\n/v1/generate\n${bodyHash}`;
const signature = crypto.createHmac('sha256', secret).update(canonical).digest('hex');

const res = await fetch('https://api.webqr.io/v1/generate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': apiKey,
    'X-WebQR-Timestamp': ts,
    'X-WebQR-Signature': signature,
  },
  body,
});
console.log(await res.json());
import hashlib, hmac, json, os, time, urllib.request

api_key = os.environ["WEBQR_API_KEY"]
secret = api_key.rsplit("_", 1)[-1]
ts = str(int(time.time()))
body = json.dumps({"data": "https://webqr.io"}, separators=(",", ":"))
body_hash = hashlib.sha256(body.encode()).hexdigest()
canonical = f"{ts}\nPOST\n/v1/generate\n{body_hash}"
signature = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

req = urllib.request.Request("https://api.webqr.io/v1/generate", data=body.encode(), method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("X-API-Key", api_key)
req.add_header("X-WebQR-Timestamp", ts)
req.add_header("X-WebQR-Signature", signature)
print(urllib.request.urlopen(req).read().decode())

Запрос с заголовками подписи

curl -X POST https://api.webqr.io/v1/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: wq_xxxxxxxxxx_your_secret" \
  -H "X-WebQR-Timestamp: 1714412345" \
  -H "X-WebQR-Signature: 64_hex_chars_lowercase_hmac_sha256_placeholder_do_not_use_as_is" \
  -d '{
    "data": "https://webqr.io",
    "design": {
        "size": 512,
        "color": "#000000",
        "backgroundColor": "#FFFFFF",
        "styleType": "a7k2m9",
        "eyeType": "r2s4t6",
        "showColorGradient": false
    }
}'
<?php
$ch = curl_init('https://api.webqr.io/v1/generate');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',
    'X-API-Key: wq_xxxxxxxxxx_your_secret',
    'X-WebQR-Timestamp: 1714412345',
    'X-WebQR-Signature: 64_hex_chars_lowercase_hmac_sha256_placeholder_do_not_use_as_is'
  ],
  CURLOPT_POSTFIELDS => '{
    "data": "https://webqr.io",
    "design": {
        "size": 512,
        "color": "#000000",
        "backgroundColor": "#FFFFFF",
        "styleType": "a7k2m9",
        "eyeType": "r2s4t6",
        "showColorGradient": false
    }
}',
]);
echo curl_exec($ch);
const res = await fetch('https://api.webqr.io/v1/generate', {
  method: 'POST',
  headers: {
      "Content-Type": "application/json",
      "X-API-Key": "wq_xxxxxxxxxx_your_secret",
      "X-WebQR-Timestamp": "1714412345",
      "X-WebQR-Signature": "64_hex_chars_lowercase_hmac_sha256_placeholder_do_not_use_as_is"
  },
  body: JSON.stringify({
    "data": "https://webqr.io",
    "design": {
        "size": 512,
        "color": "#000000",
        "backgroundColor": "#FFFFFF",
        "styleType": "a7k2m9",
        "eyeType": "r2s4t6",
        "showColorGradient": false
    }
}),
});
const data = await res.json();
console.log(data);
import requests

r = requests.post(
  "https://api.webqr.io/v1/generate",
  headers={
      "Content-Type": "application/json",
      "X-API-Key": "wq_xxxxxxxxxx_your_secret",
      "X-WebQR-Timestamp": "1714412345",
      "X-WebQR-Signature": "64_hex_chars_lowercase_hmac_sha256_placeholder_do_not_use_as_is"
  },
  json={
      "data": "https://webqr.io",
      "design": {
          "size": 512,
          "color": "#000000",
          "backgroundColor": "#FFFFFF",
          "styleType": "a7k2m9",
          "eyeType": "r2s4t6",
          "showColorGradient": false
      }
  },
)
print(r.json())

При «Требовать подписанные запросы» оба заголовка обязательны на каждом запросе. Один без другого — отказ.

Частые вопросы

Можно ли пользоваться API на бесплатном тарифе?

Да. Starter уже включает Static QR API: 200 успешных генераций за календарный месяц; Premium — 5000, Business — 25000. Отдельной API-подписки нет. Сверхквоту при необходимости включают в кабинете.

Сколько генераций входит в месяц?

Включённые успешные генерации за календарный месяц: Starter 200, Premium 5000, Business 25000, Enterprise 50000. Неиспользованные не переносятся. Подробности — в разделе «Лимиты API» на этой странице.

Где создать API-ключ?

После регистрации откройте «API-ключи» в кабинете, создайте ключ и сразу скопируйте секрет (он показывается один раз). Используйте только на бэкенде — не в браузерном JS и не в бинарнике мобильного приложения.

Можно вызывать API из браузера или мобильного приложения?

Нет. Ключ должен оставаться на сервере. Вызывайте POST /v1/generate с бэкенда, а клиенту отдавайте уже готовый SVG или ссылку на него.

Что приходит в ответе? WebQR сам хранит каждый QR?

В JSON приходит SVG в data.qr_code, те же content и design, плюс объект usage для квоты. API stateless: вызов сам по себе не попадает в библиотеку WebQR и не даёт постоянный id — SVG и связки храните у себя.

В каком формате отвечает API?

Только SVG (data.format = "svg"). Отдельного PNG/PDF-эндпоинта у Static QR API нет — конвертацию при необходимости делайте у себя.

Что будет, если исчерпать включённую квоту?

Включите биллинг API в кабинете. Сверхквота — около $2 за 1000 успешных генераций, с месячным лимитом трат (по умолчанию около $10). Без включённого биллинга запросы сверх квоты отклоняются.

Можно использовать и API, и конструктор на сайте?

Да. API — для серверных интеграций, конструктор и кабинет — для ручной работы. Коды дизайна общие, но вызовы API сами по себе не создают записи в библиотеке.

Готовы генерировать QR с сервера?

Зарегистрируйтесь, создайте API-ключ в кабинете и пройдите инструкцию на этой странице.

Starter — бесплатно навсегда Без кредитной карты Отмена в любое время