API генерации QR-кодов: запрос, ответ, лимиты и примеры на Python, Node.js и PowerShell
Три рабочих примера запроса к API на Python, Node.js и PowerShell и ответы сервера: файл с заголовками, 422 с номером строки, 429 с Retry-After.
Если список кодов собирает программа, лист с кодами проще получать запросом. 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 и от числа кодов.
| output | Content-Type | Файл |
|---|---|---|
| application/pdf | qqkod.pdf со всеми листами | |
| svg | image/svg+xml или application/zip | qqkod.svg при одном листе, qqkod.zip при нескольких |
| svg_files | image/svg+xml или application/zip | файл с одним кодом или qqkod-svg.zip |
| png | image/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.
Открыть генератор