API referenceQR endpoints

QR endpoints

The customer API has two parts:

  • /api/qr/codes — a resource API for dynamic QR codes: create, list, read, update, and read scan analytics. Dynamic codes route every scan through ExaRoutes, so they are editable and tracked.
  • /api/qr/static — a one-shot renderer for static QR codes. Static codes encode the payload directly, so there is nothing to track or edit. Nothing is stored.

Every endpoint here is authenticated with a Bearer API key. See Authentication.

The id used throughout is the QR code’s shortId (the redirect slug). It is the only identifier the API exposes.

Create a dynamic QR code

POST /api/qr/codes
Authorization: Bearer exr_live_...
Content-Type: application/json
 
{
  "data": "https://yourcompany.com/spring",
  "dataType": "url",
  "returnType": "png"
}

data is required. dataType defaults to url (other values: text, email, wifi, vcard, sms, phone, geo). returnType (png or svg) sets the format of the printable image. Styling fields (shape, margin, colours, logo, error correction) are optional.

Response:

{
  "id": "Ab3xK9pQ",
  "type": "dynamic",
  "dataType": "url",
  "destination": "https://yourcompany.com/spring",
  "redirectUrl": "https://qr.exaroutes.com/r/Ab3xK9pQ",
  "status": "active",
  "encrypted": false,
  "secure": [],
  "createdAt": "2026-05-21T15:00:00.000Z",
  "updatedAt": "2026-05-21T15:00:00.000Z"
}

The QR pattern encodes redirectUrl. Fetch the printable image separately (see below).

List QR codes

GET /api/qr/codes?limit=20&status=active
Authorization: Bearer exr_live_...

Query parameters: limit (1-100, default 25), cursor (from a prior response), type (filter by dataType), status (active or inactive).

{
  "data": [ { "id": "Ab3xK9pQ", "type": "dynamic", "...": "..." } ],
  "pagination": { "total": 42, "limit": 20, "cursor": "0", "nextCursor": "20" }
}

Pass nextCursor back as cursor to fetch the next page. nextCursor is null on the last page.

Get one QR code

GET /api/qr/codes/Ab3xK9pQ
Authorization: Bearer exr_live_...

Returns a single QR code object, the same shape as the create response.

Update a QR code’s destination

PUT /api/qr/codes/Ab3xK9pQ
Authorization: Bearer exr_live_...
Content-Type: application/json
 
{
  "destination": "https://yourcompany.com/spring-v2"
}

The QR keeps its id and printed appearance. Only the redirect target changes. Encrypted, password-protected, and compliance-locked codes cannot be updated via the API (409); edit those from the dashboard.

Read scan analytics

GET /api/qr/codes/Ab3xK9pQ/analytics?days=30
Authorization: Bearer exr_live_...

days is 1-90 (default 30). Returns a day-wise scan time series and a country breakdown.

Get the printable image

GET /api/qr/codes/Ab3xK9pQ/image
Authorization: Bearer exr_live_...

Returns the rendered QR image (image/png or image/svg+xml, whichever format the code was created with).

Bulk create

POST /api/qr/codes/bulk
Authorization: Bearer exr_live_...
Content-Type: application/json
 
[
  { "data": "https://yourcompany.com/a" },
  { "data": "https://yourcompany.com/b" }
]

Bulk creation is asynchronous. The response is a bulkId. Poll its status:

GET /api/qr/codes/bulk/{bulkId}

When the job is done, download the result archive once:

GET /api/qr/codes/bulk/{bulkId}/result

The result is a one-shot download; it is removed after the first successful fetch.

Render a static QR code

POST /api/qr/static
Authorization: Bearer exr_live_...
Content-Type: application/json
 
{
  "data": "https://yourcompany.com",
  "dataType": "url",
  "returnType": "svg"
}

Static codes are not stored and have no id, no analytics, and no destination updates. The rendered image is returned inline:

{
  "type": "static",
  "dataType": "url",
  "format": "svg",
  "image": "<svg ...>...</svg>"
}

For returnType: png the image field is a data: URI.