QUICKCHART DYNAMIC QR API

Create once. Update anytime.

Create a permanent scan link, turn it into a QR image, and change the destination without reprinting. All existing QuickChart API keys have access.

Create a code

Try the interactive dashboard demo — no account needed.

Send your existing key in the Authorization header. Keep it on your server.

curl https://qr.quickchart.io/api/codes \
  -H "Authorization: Bearer $QUICKCHART_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Summer menu",
    "targetUrl": "https://example.com/menu",
    "campaign": "Summer 2026",
    "tags": ["menu", "in-store"],
    "externalId": "menu-2026",
    "appearance": {"size": 512, "dark": "175e35", "light": "ffffff"}
  }'

A successful response returns HTTP 201 and {"success":true,"code":{...}}. The code includes its id, scanUrl, imageUrl, destination, appearance, tags, timestamps, and status. Print the image containing scanUrl. Linking directly to the destination produces a static code.

Create codes in bulk

Choose Bulk create in the dashboard to upload a CSV or paste URLs. CSV columns are targetUrl (required), name, externalId, campaign, and tags. Quote tags separated by commas. Choose shared appearance and campaign/tag defaults, review validation errors by source row, and create up to 10,000 codes per import (2 MB CSV). Keep the page open while importing. Resume a saved import in the same browser after interruptions; starting another import keeps the previously created codes.

curl https://qr.quickchart.io/api/codes/batch \
  -H "Authorization: Bearer $QUICKCHART_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "batchId": "catalog-2026:0",
    "codes": [
      {"targetUrl":"https://example.com/one","externalId":"sku-1","name":"One"},
      {"targetUrl":"https://example.com/two","externalId":"sku-2","name":"Two"}
    ]
  }'

Send 1–100 codes and at most 256 KiB of JSON per request. Each code accepts the same fields as single creation. A batch is atomic: either every code is created or none is. Success returns HTTP 201 with {"success":true,"batchId":"...","replayed":false,"codes":[...]}, in input order. Keep a unique batchId for each chunk (1–100 letters, numbers, underscores, colons, or hyphens). Retry with that same ID and payload after a timeout: HTTP 200 returns the original creation results and replayed:true. Reusing an ID with different data returns HTTP 409. Receipts do not expire or change when codes are edited or deleted; a retry never restores a deleted code.

To check rows without creating anything, POST {"codes":[...]} to /api/codes/batch/validate. Row errors include an errors array of {"row":2,"error":"..."}, with row numbers starting at 1 within the submitted chunk. Invalid rows return HTTP 422; external ID conflicts return HTTP 409. Validation does not reserve external IDs or account capacity. Creation enforces both again atomically, including paused, archived, and concurrent codes.

Download a ZIP

curl https://qr.quickchart.io/api/codes/download \
  -H "Authorization: Bearer $QUICKCHART_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"ids":["CODE_ID_1","CODE_ID_2"],"format":"png"}' \
  -o qr-codes.zip

Returns a ZIP with filenames based on each code's name and ID, such as spring-flyer-CODE_ID.png or spring-flyer-CODE_ID.svg. Names are shortened and converted to lowercase ASCII. Unnamed codes and names without a usable ASCII filename use quickchart-qr-CODE_ID.png or quickchart-qr-CODE_ID.svg. Images encode each code's permanent scan URL and use its current saved appearance. Send up to 1,000 distinct IDs with at most 450 million total output pixels; split larger downloads. SVG cannot include logos. Each rendered image counts toward the account's rendering usage. ZIP downloads reuse the existing QR batch renderer. Retrying a failed download never creates additional codes. The dashboard's results CSV maps source rows and external IDs to code IDs, destinations, scan links, and image URLs.

Manage codes

Method Endpoint Purpose
GET /api/codes List your codes
POST /api/codes Create a code
GET /api/codes/:id Read a code
PATCH /api/codes/:id Update destination, name, appearance, tags, campaign, or external ID
GET /api/codes/:id/image?format=svg Download PNG or SVG
POST /api/codes/:id/pause Stop redirects temporarily
POST /api/codes/:id/activate Resume redirects
POST /api/codes/:id/archive Hide from the main list; keep redirects
POST /api/codes/:id/restore Restore an archived code to the main list
DELETE /api/codes/:id Permanently disable a code; cannot be undone
curl -X PATCH https://qr.quickchart.io/api/codes/CODE_ID \
  -H "Authorization: Bearer $QUICKCHART_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"targetUrl":"https://example.com/new-menu"}'

Destinations must be public HTTP or HTTPS URLs, at most 4,096 characters after URL encoding. Updates take effect on subsequent scans. Redirect responses are never cached. Codes do not expire automatically when a subscription ends. Pausing, deleting, or an abuse takedown can stop a code.

The account limit is one million undeleted codes; archived and paused codes count. An externalId must be unique within your account, including previously deleted codes. After an uncertain create response, search for that external ID before retrying.

List and search

Supported query parameters: q (name, destination, external ID, or code ID), status=active|paused, campaign, tag, archived=true, and limit (1–100, default 25). Pass the returned nextCursor as cursor for another page. A null cursor means the end.

Appearance

Images use the existing QuickChart QR renderer. Store renderer options in appearance: size, margin, dark/light colors, error correction, logo URL and size, caption options, dot styles, and finder styles/colors. Use centerImageUrl for a public logo URL. Size must be 100–3000 pixels. PNG and SVG downloads use the same saved options. Codes with a logo must use PNG; the renderer does not support embedded logos in SVG. Replacing appearance replaces the saved options; changing the destination leaves them intact. Always scan-test before printing.

Scan analytics

curl 'https://qr.quickchart.io/api/analytics?days=30&codeId=CODE_ID' \
  -H "Authorization: Bearer $QUICKCHART_API_KEY"

Use days=1|7|30|60 (default 7). Omit codeId for account totals. Optional filters: campaign, tag, includeBots=true. Add format=csv for export. Campaign and tag filters reflect the labels at scan time.

Results include total scans, observed daily unique scans, scans by UTC day, last scan, top codes, and country/region/city, device, browser, operating system, referrer, and destination-host breakdowns. Previous-period comparison is available for windows up to 30 days; the total history window is 60 days.

A scan is a visit to the redirect link. Opening the link directly also counts; generating or downloading an image does not. Known bots are excluded by default, but bot detection is imperfect. Counts may be sampled and delayed. Daily unique scans count observed devices per code per UTC day; they are not a count of distinct people across codes or days. When summary.sampled is true, dailyUniqueScans counts only devices observed in the sample and may undercount. Total scans are adjusted for sampling; daily uniques are not extrapolated. The same flag is included in previous. Location is approximate. No tracking cookies or raw scanner IPs are stored. Referrers are reduced to hostnames and destination query strings are omitted from scan analytics.

Errors

Errors return JSON: {"success":false,"error":"..."}. HTTP 400 means invalid input, 401 invalid authentication, 403 denied access, 404 an unavailable code, 409 a conflicting external ID or code limit, 413 an oversized request, and 503 a temporarily unavailable dependency. Retry temporary failures with backoff. Never expose API keys in client-side JavaScript or URLs.

Open the QR dashboard · Contact support