Ку-Ку Код

API генерации QR-кодов: запрос, ответ, лимиты и примеры на Python, Node.js и PowerShell

Три рабочих примера запроса к API на Python, Node.js и PowerShell и ответы сервера: файл с заголовками, 422 с номером строки, 429 с Retry-After.

Массовая генерация 9 мин на чтение 19.10.2026

Если список кодов собирает программа, лист с кодами проще получать запросом. API принимает такой же список и такие же параметры, как форма, и отдает такой же файл: PDF с сеткой кодов на A4, листы SVG или архив с кодами поштучно. Ключ и регистрация не обязательны.

Таблица параметров и коды ошибок уже есть на странице API генератора QR-кодов. Эта статья дополняет страницу примерами на Python, Node.js и PowerShell и ответами сервера на 422 и 429. Пример на встроенном языке 1С вынесен в статью про печать QR-листа из 1С через API.

Запрос

Адрес https://qqkod.ru/api/v1/sheets, метод POST, тело в JSON с заголовком Content-Type: application/json. Обязательное поле одно, lines: массив строк или текст с переносами строки. Одна строка это один код в формате поля списка формы, у ссылки подпись после пробела. Например, три ссылки с подписями в PDF:

{"lines": ["https://example.com/1 Стол 1", "https://example.com/2 Стол 2", "https://example.com/3 Стол 3"], "output": "pdf"}

Без остальных полей код выходит со стороной 2 см на книжном листе A4, а формат ответа по умолчанию svg, так что output в примере стоит ради PDF. Тип содержимого задает content_type, по умолчанию url; у типа payment к lines добавляются обязательные реквизиты получателя. Примеры строк других типов есть в базе знаний. В ячейку Excel API не вставить, картинку по формуле ИЗОБРАЖЕНИЕ он не отдает; из таблицы в lines уходит колонка значений. Почему картинки из таких формул не годятся для печати, мы разобрали в статье QR-код в Excel.

Ответ

При успехе в теле ответа лежит сам файл без JSON-обертки, его тип зависит от output и от числа кодов.

outputContent-TypeФайл
pdfapplication/pdfqqkod.pdf со всеми листами
svgimage/svg+xml или application/zipqqkod.svg при одном листе, qqkod.zip при нескольких
svg_filesimage/svg+xml или application/zipфайл с одним кодом или qqkod-svg.zip
pngimage/png или application/zipфайл с одним кодом или qqkod-png.zip

Один код в png приходит файлом qr-0001.png; имя с подписью, «qr-0001 Стол 1.png», стоит в поле filename* заголовка Content-Disposition.

Число листов, сетку, число мест на листе и число кодов в запросе отдают заголовки X-QQkod-Total-Sheets, X-QQkod-Grid, X-QQkod-Per-Sheet и X-QQkod-Items, разбирать PDF не нужно.

При стороне 2 см три ссылки с подписями ложатся сеткой 5×7 на один лист, 35 мест на листе, PDF весит 59 КБ. Самый длинный список, 1500 строк с подписями, дает 43 листа; такой PDF весит 1,2 МБ и собирается около 8 с. Два одинаковых запроса в svg дают один и тот же файл; хеши двух PDF не совпадут из-за даты создания и идентификатора в служебной части.

Примеры на Python, Node.js и PowerShell

Все три примера шлют POST с JSON в UTF-8, пишут файл из тела ответа на диск и печатают число листов и кодов из заголовков, для трех строк это «1 3». Python и Node.js разбирают ответ с ошибкой как JSON, PowerShell перепечатывает его с отступами, и кириллица в нем выходит \u-кодами.

Сохраните их как example.py, example.mjs и example.ps1 и запускайте: python example.py, node example.mjs, pwsh -File example.ps1. Проверено на Python 3.12, Node.js 24 и PowerShell 7.6.

Пример на Python обходится стандартной библиотекой. При успехе он печатает два числа, при ошибке русский текст из fields. Если вывод перенаправлен в файл или канал, как у планировщика, а кодовая страница Windows не кириллическая, например cp1252, print падает с UnicodeEncodeError; спасает запуск python -X utf8 example.py.

import json
import urllib.request
import urllib.error

body = {
    "lines": ["https://example.com/1 Стол 1", "https://example.com/2 Стол 2", "https://example.com/3 Стол 3"],
    "output": "pdf",
}
req = urllib.request.Request(
    "https://qqkod.ru/api/v1/sheets",
    data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
    headers={"Content-Type": "application/json"},
    method="POST",
)
try:
    with urllib.request.urlopen(req) as res:
        with open("qqkod.pdf", "wb") as f:
            f.write(res.read())
        print(res.headers["X-QQkod-Total-Sheets"], res.headers["X-QQkod-Items"])
except urllib.error.HTTPError as e:
    err = json.load(e)
    print(e.code, err["error"]["code"], err["error"]["fields"], err["meta"]["request_id"])
    raise SystemExit(1)

В Node.js хватает встроенного fetch. Ответ читается как arrayBuffer, потому что это двоичный файл.

import { writeFile } from 'node:fs/promises';

const body = {
  lines: ['https://example.com/1 Стол 1', 'https://example.com/2 Стол 2', 'https://example.com/3 Стол 3'],
  output: 'pdf',
};
const res = await fetch('https://qqkod.ru/api/v1/sheets', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
});
if (res.ok) {
  await writeFile('qqkod.pdf', Buffer.from(await res.arrayBuffer()));
  console.log(res.headers.get('x-qqkod-total-sheets'), res.headers.get('x-qqkod-items'));
} else {
  const err = await res.json();
  console.error(res.status, err.error.code, err.error.fields, err.meta.request_id);
  process.exitCode = 1;
}

В PowerShell 7 тело запроса уходит байтами UTF-8. Параметр -PassThru вместе с -OutFile отдает и файл, и заголовки ответа. Текст ошибки приходит в $_.ErrorDetails.Message; читаемую кириллицу вместо \u-кодов даст ($_.ErrorDetails.Message | ConvertFrom-Json).error.fields.

$body = @{
  lines  = @('https://example.com/1 Стол 1', 'https://example.com/2 Стол 2', 'https://example.com/3 Стол 3')
  output = 'pdf'
} | ConvertTo-Json
try {
  $r = Invoke-WebRequest -Uri 'https://qqkod.ru/api/v1/sheets' -Method Post `
    -ContentType 'application/json; charset=utf-8' `
    -Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -OutFile 'qqkod.pdf' -PassThru
  "$($r.Headers['X-QQkod-Total-Sheets']) $($r.Headers['X-QQkod-Items'])"
} catch {
  [int]$_.Exception.Response.StatusCode
  $_.ErrorDetails.Message
  exit 1
}

Ошибки

Ошибка приходит в JSON: в error.code код ошибки для программы, в error.message пояснение на русском, в error.fields ошибки по полям, в meta.request_id идентификатор запроса. Так выглядит ответ 422 на список, в котором вторая строка «просто текст без ссылки».

{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Проверьте значения полей",
    "fields": {
      "urls": ["Строка №2: не похоже на ссылку, напишите ее как site.ru/page или https://site.ru/page"]
    }
  },
  "meta": {"request_id": "3e202e0bc700f78a"}
}

Построчные ошибки списка идут под ключом urls, а не lines: API переводит запрос в поля формы на сайте, и поле списка там называется urls. Номер строки считается по вашему списку с единицы, ошибка параметра приходит под его именем.

При типовой ошибке списка к ответу добавляется error.hint, та же подсказка, что показывает форма, одна на ответ. Если подписи стоят перед ссылками, придет код label_before_url, заголовок «Ссылка должна идти первой», а в hint.action.text список, переставленный как надо.

Коды 400, 401, 404, 405, 413, 415 и 500 описаны в таблице на странице API; у ошибок без полей, например 429 или 401, fields приходит пустым массивом вместо объекта. Если ответ выглядит ошибкой на нашей стороне, приложите meta.request_id к письму на info@qqkod.ru.

Лимиты и ключ

Без ключа с одного IP проходит 10 запросов в минуту и 300 за сутки по UTC. Остаток виден в заголовках X-RateLimit-Remaining-Minute и X-RateLimit-Remaining-Day у успешного ответа и у ответов с ошибками, которые идут в счет лимита (перечень на странице API).

Одиннадцатый запрос в минуту получает 429 с кодом RATE_LIMITED и сообщением «Слишком много запросов. Повторите попытку через минуту.» В заголовке Retry-After стоит число секунд до конца минуты, например 9. Когда исчерпан суточный лимит, Retry-After считает до 00:00 UTC, а сообщение меняется на «Исчерпан лимит запросов за сутки. Повторите попытку после 00:00 UTC.»

С ключом API из кабинета запросы считаются по вашей учетной записи вместо адреса, с которого они пришли. Без подписки это те же 10 запросов в минуту и 300 в сутки. С подпиской Pro проходит 60 в минуту и 3000 в сутки, с Business 300 в минуту и 30 000 в сутки.

FAQ

Нужен ли ключ API для генерации QR-кодов?

Нет, запрос без заголовка Authorization работает так же. Ключ нужен, когда адрес общий и чужие запросы съедают ваш лимит, и при подписке ради лимита повыше.

Как добавить ключ API в эти примеры?

Заголовком Authorization со значением Bearer и ключом через пробел: в Python и Node.js одной строкой рядом с Content-Type, в PowerShell параметром -Headers @{ Authorization = "Bearer $env:QQKOD_API_KEY" }. Ключ держите в переменной окружения, не в коде. С неверным ключом придет 401: такой запрос не пройдет и как запрос без ключа.

Можно ли получить QR-код по GET-ссылке как картинку для тега img?

Нет, API отвечает на POST файлом, адреса картинки у него нет. Сохраните PNG из ответа на свой сервер и ссылайтесь на него.

Что делать скрипту при 429?

Если Retry-After не больше 60, подождите столько секунд и повторите запрос один раз. Если больше, исчерпан суточный лимит: запишите request_id в журнал и остановитесь до следующего запуска.

Следующий шаг

Запустите пример на своем языке с тремя своими строками. Если он напечатал «1 3», все три кода легли на один лист, откройте qqkod.pdf и проверьте подписи. Если пришло 422, в fields.urls стоит номер строки, которую надо поправить.

Все параметры API генератора QR-кодов на одной странице: лист, рамка, цвета, типы содержимого и коды ошибок.

Соберите свой лист с QR-кодами

Вставьте список ссылок или загрузите файл, а мы разложим коды с подписями по листам A4 и отдадим PDF, SVG или PNG.

Открыть генератор
бесплатно, без регистрации