API reference
Create and update dynamic QR codes and read their scans from your own systems. JSON over HTTPS with a bearer key. The API is part of Pro.
Create an API keySee Pro pricing
Quick start
- Create a key on the Developers page in the app (Pro).
- Send it with every request as Authorization: Bearer <key>.
- Create a code with POST /codes, render its qr_payload and print it. Later, change where it goes with PATCH.
Base URL
https://qrglo.com/api/v1curl -X POST https://qrglo.com/api/v1/codes \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/menu","name":"Menu"}'Authentication
A key is shown once, when you create it; we keep only a fingerprint. Keys act for the whole workspace, so keep them on your server, never in front-end code. Revoking a key stops it at once.
Authorization: Bearer YOUR_KEYRate limits
Each key may send 60 requests a minute; more returns 429 rate_limited. The Developers page in the app shows the last 7 days of requests.
Errors
Errors use the matching HTTP status and a body of {"error": "<code>"}.
| HTTP | error | Description |
|---|---|---|
| 400 | invalid_json | The body is not valid JSON. |
| 400 | invalid | The URL is not a valid http(s) link, or points at our own short domain. |
| 400 | unsafe | Google Safe Browsing lists the URL as malware or phishing. |
| 400 | not_a_link_code | Only link codes have a URL to change; hosted pages (vCard, menu…) are edited in the app. |
| 400 | data | "data" is missing or longer than 2000 characters. |
| 400 | kind | Unknown barcode kind. |
| 401 | unauthorized | No key, an unknown key, or a revoked key. |
| 403 | pro_required | The workspace is not on Pro. |
| 403 | limit | The workspace has reached its code limit. |
| 403 | locked | The code is read-only because the plan’s limit was lowered. |
| 404 | not_found | No such code in this workspace, or no such endpoint. |
| 404 | template_not_found | No template with that id. |
| 405 | method_not_allowed | The path exists but not with this HTTP method. |
| 429 | rate_limited | More than 60 requests in the last minute with this key. |
| 500 | server_error | Something failed on our side. Retry with backoff. |
Endpoints
List codes
GET/api/v1/codes
Every editable QR code and Smart Link in the workspace, newest first, archived ones included (see "archived").
Parameters
| Name | In | Description |
|---|---|---|
kindstring · optional | query | Only QR codes (qr) or only Smart Links (link). Leave out for both.One of: qr | link |
Request
curl https://qrglo.com/api/v1/codes?kind=qr \
-H "Authorization: Bearer YOUR_KEY"Response · Status 200
{
"data": [
{
"id": "k3v9x2",
"name": "Menu",
"kind": "qr",
"type": "url",
"short_url": "https://q.qrglo.com/ab3cd",
"target_url": "https://example.com/menu",
"active": true,
"archived": false,
"scans": 128,
"tags": ["table-cards"],
"domain": null,
"created_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-20T14:02:11.000Z"
}
]
}Possible errors: unauthorized, pro_required, rate_limited
Create a code
POST/api/v1/codes
Creates an editable code that sends scans to "url". Put "qr_payload" into any QR renderer, or call GET /qr with it, to get the printable pattern.
Parameters
| Name | In | Description |
|---|---|---|
urlstring · required | body | Where scans go. http or https; checked against Google Safe Browsing. |
namestring · optional | body | Your label for the code. Defaults to the link’s host name. |
kindstring · optional | body | "qr" (default) for a QR code, "link" for a Smart Link with no pattern.One of: qr | link |
templatestring · optional | body | A style template id from GET /templates. The code gets that template’s colours, shapes and logo. |
Request
curl -X POST https://qrglo.com/api/v1/codes \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/menu","name":"Menu"}'Response · Status 201
{
"id": "k3v9x2",
"name": "Menu",
"kind": "qr",
"type": "url",
"short_url": "https://q.qrglo.com/ab3cd",
"target_url": "https://example.com/menu",
"active": true,
"archived": false,
"scans": 128,
"tags": ["table-cards"],
"domain": null,
"created_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-20T14:02:11.000Z",
"qr_payload": "https://q.qrglo.com/ab3cd"
}Possible errors: invalid_json, invalid, unsafe, limit, template_not_found, unauthorized, pro_required, rate_limited
Get a code
GET/api/v1/codes/{id}
One code, with the "qr_payload" to render it.
Parameters
| Name | In | Description |
|---|---|---|
idstring · required | path | The code id, as returned by list or create. |
Request
curl https://qrglo.com/api/v1/codes/ID \
-H "Authorization: Bearer YOUR_KEY"Response · Status 200
{
"id": "k3v9x2",
"name": "Menu",
"kind": "qr",
"type": "url",
"short_url": "https://q.qrglo.com/ab3cd",
"target_url": "https://example.com/menu",
"active": true,
"archived": false,
"scans": 128,
"tags": ["table-cards"],
"domain": null,
"created_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-20T14:02:11.000Z",
"qr_payload": "https://q.qrglo.com/ab3cd"
}Possible errors: not_found, unauthorized, pro_required, rate_limited
Update a code
PATCH/api/v1/codes/{id}
Changes where a printed code goes, renames it, or pauses it. Send only the fields you change. The printed pattern stays the same.
Parameters
| Name | In | Description |
|---|---|---|
idstring · required | path | The code id, as returned by list or create. |
urlstring · optional | body | New destination. Only for link codes (type "url"). |
namestring · optional | body | New label. |
activeboolean · optional | body | false pauses the code: scans see a "paused" page. true turns it back on. |
Request
curl -X PATCH https://qrglo.com/api/v1/codes/ID \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/menu-winter"}'Response · Status 200
{
"id": "k3v9x2",
"name": "Menu",
"kind": "qr",
"type": "url",
"short_url": "https://q.qrglo.com/ab3cd",
"target_url": "https://example.com/menu-winter",
"active": true,
"archived": false,
"scans": 128,
"tags": ["table-cards"],
"domain": null,
"created_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-20T14:02:11.000Z"
}Possible errors: not_found, locked, invalid_json, not_a_link_code, invalid, unsafe, unauthorized, pro_required, rate_limited
Archive a code
DELETE/api/v1/codes/{id}
Archives the code: it stops counting toward your limit and its statistics are kept. It can be restored in the app.
Parameters
| Name | In | Description |
|---|---|---|
idstring · required | path | The code id, as returned by list or create. |
Request
curl -X DELETE https://qrglo.com/api/v1/codes/ID \
-H "Authorization: Bearer YOUR_KEY"Response · Status 200
{
"id": "k3v9x2",
"archived": true
}Possible errors: not_found, unauthorized, pro_required, rate_limited
Scan statistics
GET/api/v1/codes/{id}/stats
Totals and a time series for one code. Unique visitors are counted per day, without cookies.
Parameters
| Name | In | Description |
|---|---|---|
idstring · required | path | The code id, as returned by list or create. |
windowstring · optional | query | Time range. Default 30d. Buckets are hours (24h), days, or months for long ranges.One of: 24h | 7d | 30d | 90d | 12m | all | custom |
fromstring · optional | query | With window=custom: first day, YYYY-MM-DD (UTC). |
tostring · optional | query | With window=custom: last day, YYYY-MM-DD (UTC). Up to 5 years after "from". |
Request
curl "https://qrglo.com/api/v1/codes/ID/stats?window=7d" \
-H "Authorization: Bearer YOUR_KEY"Response · Status 200
{
"id": "k3v9x2",
"window": { "k": "7d", "from": "2026-10-26T00:00:00.000Z", "to": "2026-11-01T18:20:00.000Z", "bucket": "day" },
"total": 42,
"unique": 37,
"series": [{ "label": "2026-10-26", "scans": 5 }, { "label": "2026-10-27", "scans": 8 }],
"countries": [{ "k": "US", "n": 20 }, { "k": "DE", "n": 9 }],
"devices": [{ "k": "mobile", "n": 39 }, { "k": "desktop", "n": 3 }],
"os": [{ "k": "iOS", "n": 25 }, { "k": "Android", "n": 14 }],
"browsers": [{ "k": "Safari", "n": 24 }],
"languages": [{ "k": "en", "n": 30 }]
}Possible errors: not_found, unauthorized, pro_required, rate_limited
Render a QR code (SVG)
GET/api/v1/qr
Draws any text or link as a static QR code. Use it with "qr_payload" to print your editable codes.
Parameters
| Name | In | Description |
|---|---|---|
datastring · required | query | What the code holds, up to 2000 characters. |
templatestring · optional | query | A style template id from GET /templates. The code gets that template’s colours, shapes and logo. |
fgstring · optional | query | Foreground colour as hex, e.g. #111827. Ignored when a template is given. |
bgstring · optional | query | Background colour as hex. Used together with fg. |
sizeinteger · optional | query | Width and height in pixels, 64 to 2000. Default 512. |
Request
curl "https://qrglo.com/api/v1/qr?data=https%3A%2F%2Fq.qrglo.com%2Fab3cd&size=1024" \
-H "Authorization: Bearer YOUR_KEY" -o code.svgResponse · Status 200
Returns an image/svg+xml image.
Possible errors: data, template_not_found, unauthorized, pro_required, rate_limited
Render a barcode (SVG)
GET/api/v1/barcode
Draws a retail, shipping or 2D barcode. Check digits are validated; a wrong one returns 400.
Parameters
| Name | In | Description |
|---|---|---|
kindstring · required | query | Barcode type.One of: ean13 | isbn | upca | ean8 | upce | code128 | gs1-128 | code39 | itf14 | datamatrix | gs1-datamatrix |
datastring · required | query | The digits or text to encode. GS1 types take (AI)value pairs, e.g. (01)09501101530003(17)261231. |
fgstring · optional | query | Bar colour as hex. |
bgstring · optional | query | Background colour as hex. |
heightinteger · optional | query | Bar height in modules. |
textstring · optional | query | 0 hides the human-readable digits under the bars.One of: 0 | 1 |
Request
curl "https://qrglo.com/api/v1/barcode?kind=ean13&data=4006381333931" \
-H "Authorization: Bearer YOUR_KEY" -o barcode.svgResponse · Status 200
Returns an image/svg+xml image.
Possible errors: kind, unauthorized, pro_required, rate_limited
List style templates
GET/api/v1/templates
Templates are saved from a code’s look on the Developers page in the app. Pass an id as "template" when creating or rendering codes.
Request
curl https://qrglo.com/api/v1/templates \
-H "Authorization: Bearer YOUR_KEY"Response · Status 200
{
"data": [
{ "id": "tpl_8f2k", "name": "Brand", "style": { "fg": "#111827", "bg": "#ffffff", "dots": "rounded" }, "created_at": "2026-09-12T08:00:00.000Z" }
]
}Possible errors: unauthorized, pro_required, rate_limited
Webhooks
Add an https URL on the Developers page in the app and pick the events you want. We POST JSON with a 5 second timeout; after 5 failures in a row the webhook is paused and you get a notification.
Events
scanSomeone scanned a code. Sent within seconds of the scan.code.createdA code was created, in the app or through the API.code.updatedA code’s link, name or status changed.code.archivedA code was archived.
Example body
{
"id": "6f1c2d4e-0b7a-4c55-9a1e-2f3b4c5d6e7f",
"event": "scan",
"created": "2026-11-01T18:20:04.512Z",
"data": {
"code": { "id": "k3v9x2", "name": "Menu", "short": "ab3cd" },
"scans": 129,
"country": "US",
"city": "Austin",
"device": "mobile",
"os": "iOS",
"browser": "Safari",
"lang": "en",
"referrer": null,
"destination": "https://example.com/menu"
}
}Verifying signatures
Every request carries QRGlo-Signature: t=<unix seconds>,v1=<signature>. The signature is the hex HMAC-SHA256 of "<t>.<raw body>" with the webhook’s secret. Compare it, and reject requests older than 5 minutes.
import crypto from 'node:crypto';
// header: the QRGlo-Signature request header, e.g. "t=1762021204,v1=5f2b…"
// body: the raw request body, exactly as received
export function verify(header, body, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${body}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const got = Buffer.from(parts.v1 ?? '');
return fresh && got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}For tools and AI assistants
The same reference in machine-readable form:
- /openapi.json OpenAPI 3.1
- /llms.txt