QR codes
Every short link can carry a QR code. Create one for a link, style its colours and error-correction level, and fetch it as a PNG or SVG through a signed URL that needs no API key — so an <img> tag, an email template, or an automation’s file field can load it directly.
Scans through a QR code are recorded separately from ordinary clicks.
| Method | Path | Scope | Auth |
|---|---|---|---|
GET | /v1/links/{id}/qrcode | links:read | Bearer token |
POST | /v1/links/{id}/qrcode | links:write | Bearer token |
GET | /v1/qrcodes/{id}/image/{format} | — | Signed URL |
The QR code object
{
"id": "5c8a2f61-7b93-4d05-8e14-a6f2c9d3b708",
"link_id": "9b1f4e2a-6c3d-4a17-9f2e-7c8d5a1b3e40",
"redirect_url": "go.acme.com/summer/?r=qr",
"foreground": "#1a73e8",
"background": "#FFFFFF",
"style": "dots",
"level": "H",
"image_url": "https://app.linksqueeze.io/api/v1/qrcodes/5c8a2f61-7b93-4d05-8e14-a6f2c9d3b708/image/png?signature=...",
"svg_url": "https://app.linksqueeze.io/api/v1/qrcodes/5c8a2f61-7b93-4d05-8e14-a6f2c9d3b708/image/svg?signature=...",
"scans": 42,
"created_at": "2026-08-28T10:14:02.000000Z",
"updated_at": "2026-08-28T11:02:44.000000Z"
}| Field | Type | Description |
|---|---|---|
id | uuid | The QR code’s identifier |
link_id | uuid | The link this code points at |
redirect_url | string | What the code actually encodes — the short link with a ?r=qr marker appended |
foreground | string | Module colour, as a hex value |
background | string | Background colour, as a hex value |
style | string | squares, dots, or fluid |
level | string | Error-correction level: L, M, Q, or H |
image_url | string | Signed URL rendering the code as a PNG |
svg_url | string | Signed URL rendering the code as an SVG |
scans | integer | Number of scans recorded |
created_at | timestamp | When the code was created |
updated_at | timestamp | When the code was last restyled |
The encoded redirect_url carries a ?r=qr marker, which is how a scan is
distinguished from an ordinary click. Encode redirect_url yourself rather
than the plain short link if you generate codes with your own library, or the
scans will be counted as clicks.
Error-correction levels
| Level | Recovers from | Use when |
|---|---|---|
L | ~7% damage | Clean digital display, smallest possible symbol |
M | ~15% damage | The default — fine for most print and screen use |
Q | ~25% damage | Print that may scuff, or a code with a logo overlaid |
H | ~30% damage | Harsh environments, stickers, packaging |
Higher levels pack more redundancy into the symbol, making it denser but more tolerant of damage.
Create or restyle a QR code
POST /v1/links/{id}/qrcodeScope: links:write
A link has at most one QR code, so this endpoint is an upsert: it creates the code the first time and restyles it thereafter.
| Field | Type | Description |
|---|---|---|
foreground | string | Module colour as a 3- or 6-digit hex value, with or without the leading #. Defaults to #000000. |
background | string | Background colour, same format. Defaults to #FFFFFF. |
style | string | squares, dots, or fluid. Defaults to squares. |
level | string | L, M, Q, or H. Defaults to M. |
title | string | null | A label stored alongside the code, maximum 255 characters. |
Every field is optional. Returns 201 Created when the code did not exist, and 200 OK when an existing one was restyled.
curl -X POST https://app.linksqueeze.io/api/v1/links/9b1f4e2a-6c3d-4a17-9f2e-7c8d5a1b3e40/qrcode \
-H "Authorization: Bearer $LINKSQUEEZE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"foreground": "#1a73e8",
"level": "H",
"style": "dots"
}'Settings are merged, not replaced. Sending only background restyles the
background and leaves the foreground, style, and level as they are. This makes
the endpoint safe to re-run — an automation firing the same request twice will
not quietly reset your colours to the defaults.
Colours must be hex. A named CSS colour or an rgb() string is rejected with 422:
{
"error": {
"code": "validation_error",
"message": "The given data was invalid.",
"errors": {
"foreground": ["The foreground must be a hex colour, e.g. #1a73e8."]
}
}
}An unrecognised level or style is likewise a 422.
Retrieve a QR code
GET /v1/links/{id}/qrcodeScope: links:read
Returns the link’s QR code, or 404 not_found if the link does not have one yet. A link belonging to another account also returns 404.
curl https://app.linksqueeze.io/api/v1/links/9b1f4e2a-6c3d-4a17-9f2e-7c8d5a1b3e40/qrcode \
-H "Authorization: Bearer $LINKSQUEEZE_API_KEY" \
-H "Accept: application/json"Create the QR code before fetching it. There is no implicit creation on
GET — POST to the same path first, which is a no-op beyond restyling if
the code already exists.
Render the image
GET /v1/qrcodes/{id}/image/{format}No API key required. Use the image_url (PNG) or svg_url (SVG) returned on the QR code object exactly as given — the signature is part of the URL and the request will be rejected without it.
Because these URLs carry no credentials, they can be used anywhere a plain image URL works:
<img src="https://app.linksqueeze.io/api/v1/qrcodes/5c8a2f61-.../image/png?signature=..." alt="Scan for the summer sale" />| Response detail | Value |
|---|---|
| Content type | image/png or image/svg+xml |
| PNG size | 512Ă—512 pixels |
| Cache | public, max-age=86400 |
| Unsigned or tampered request | 403 Forbidden |
| Unknown id or format | 404 Not Found |
Do not build these URLs by hand, and do not append query parameters to
them. The signature covers the entire query string, so adding anything —
including a size parameter — invalidates it and the request is rejected
with 403. Fetch the URL from the API and use it verbatim.
Getting a different size
The PNG renders at a fixed 512×512. For any other size, use svg_url instead — an SVG scales to whatever dimensions you render it at without losing sharpness, and needs no size parameter.
How the signature behaves
The signed URL is minted fresh each time you read the QR code object, and it does not expire on its own. It is scoped to one QR code in one format, so it cannot be edited into a URL for someone else’s code.
The image only ever encodes a short link, which is public by design — the signature exists to stop the URL being forged for another account’s code, not to keep the image secret. Treat the URL as shareable, and re-fetch it from the API rather than storing it indefinitely.
Scans versus clicks
A scan increments scans on the QR code, and is also recorded as a click on the link — it arrives with the ?r=qr marker that distinguishes it. So:
scanson the QR code object counts QR scans only;clickson the link object counts every visit, scans included.
Individual scans appear in the click log like any other click.