QR Codes API
The QR API has one endpoint: it creates up to 20 trackable QR codes in a single request. Each QR is backed by a new short link, and its image is served from a hosted MASK URL you can download or embed. There is no list, update, or delete endpoint, and the API does not set colors, size, error correction, or a logo — style QR codes in the dashboard instead.
Authentication and limits
Send your API key as a bearer token. Keys belong to one workspace, and every QR created here belongs to that workspace.
| Requirement | Detail |
|---|---|
| Authorization | Bearer mk_... |
| links:write | The scope this endpoint checks. Creating a QR also creates a short link, so it is a link write. A key without it returns 403. |
| Plan | The API is a Growth feature. Keys stop working if the workspace returns to the free plan. |
| Rate limit | 60 requests per minute per key. Over the limit returns 429 with the rate-limit headers. |
Create QR codes
Send between 1 and 20 destinations. For each one MASK creates a short link and a QR code pointing at it, and returns the hosted image URL. The request is processed item by item, so a response can contain both created and skipped entries.
/api/v1/qrRequest body
| Field | Type | Required | Description |
|---|---|---|---|
| items | object[] | Yes | 1 to 20 entries. More than 20 is a 400. |
| items[].url | string | Yes | The destination the QR resolves to. Must be http or https. |
| items[].name | string | No | Internal label for the QR, up to 120 characters. Defaults to API QR. |
curl -X POST https://mask.pk/api/v1/qr \
-H "Authorization: Bearer mk_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "url": "https://example.com/menu", "name": "Table tent" },
{ "url": "https://example.com/offer" }
]
}'{
"data": {
"created": [
{
"url": "https://example.com/menu",
"qrId": "clx8qr9a0001",
"slug": "a1b2c3",
"imageUrl": "https://mask.pk/api/qr/clx8qr9a0001/image"
},
{
"url": "https://example.com/offer",
"qrId": "clx8qr9a0002",
"slug": "d4e5f6",
"imageUrl": "https://mask.pk/api/qr/clx8qr9a0002/image"
}
],
"skipped": []
}
}Response fields
| Field | Type | Description |
|---|---|---|
| created[].url | string | The destination you sent, echoed back so you can match rows to your input |
| created[].qrId | string | Id of the new QR code |
| created[].slug | string | Slug of the short link created behind the QR, on the default domain |
| created[].imageUrl | string | Hosted image for this QR. See The QR image. |
| skipped[].url | string | A destination no QR was created for |
| skipped[].reason | string | Why it was skipped. See the table below. |
Partial success
A request where some items succeed and others do not still returns 201. Read data.skipped on every response rather than treating a 201 as all-or-nothing. Skipped items are not retried for you and consume no quota.
| Reason | Meaning |
|---|---|
| Plan limit reached | The batch ran past the remaining QR or link allowance. Earlier items in the same request were created. |
| Bio page URL not allowed | The destination is a MASK bio page. Create a QR for that page from the dashboard instead. |
| Failed to create | The link or QR could not be written. Safe to retry that item on its own. |
{
"data": {
"created": [
{
"url": "https://example.com/menu",
"qrId": "clx8qr9a0001",
"slug": "a1b2c3",
"imageUrl": "https://mask.pk/api/qr/clx8qr9a0001/image"
}
],
"skipped": [
{ "url": "https://example.com/offer", "reason": "Plan limit reached" }
]
}
}Quota
Each QR consumes one QR code and one short link from your plan, because the QR is created together with the link it resolves through. The batch is capped by whichever of the two allowances runs out first: if you have room for 3 more links and 10 more QR codes, a batch of 5 creates 3 and skips 2. When neither has room, the whole request returns 403 and nothing is created.
The QR image
The imageUrl in the response is the image for that QR. It is a public URL and takes no API key, so you can put it straight into a template, an email, or a print job.
/api/qr/:qrId/image| Parameter | Type | Description |
|---|---|---|
| format | string | svg (default) or png. PNG is rendered at 512px; use SVG for print, where it scales to any size. |
curl "https://mask.pk/api/qr/clx8qr9a0001/image?format=png" -o qr-code.pngcurl "https://mask.pk/api/qr/clx8qr9a0001/image?format=svg" -o qr-code.svgThe image encodes the MASK scan address for that QR, not the destination itself. That is what makes the code trackable: scans resolve through MASK, and you can repoint the short link later without reprinting.
Errors
Every failure returns a single error string. These are request-level failures; a problem with one item in an otherwise valid batch appears in data.skipped instead.
| Status | Meaning |
|---|---|
| 400 | The body is malformed: no items, an empty array, more than 20 entries, a name over 120 characters, or a URL that is not http/https |
| 401 | Missing, revoked, expired, or non-paid API key |
| 403 | Insufficient scope when the key lacks links:write, or QR or link quota reached for this plan. when there is no room for even one QR |
| 429 | Rate limit exceeded |
| 500 | Unexpected server error |