QRQR Code Agency
API reference

POST /api/v1/generate/

Render a single QR and return its PNG or SVG bytes. Every parameter, every header, every response code.

Render a QR code and return its PNG or SVG bytes.

Endpoint

POST /api/v1/generate/
Host: api.qrstudio.agency
X-Api-Key: smk_...
Content-Type: application/json

For logo or background-image uploads, use multipart instead:

POST /api/v1/generate/
Host: api.qrstudio.agency
X-Api-Key: smk_...
Content-Type: multipart/form-data

Body parameters

Payload (what to encode)

FieldTypeRequiredDefaultDescription
datastringfor url and textn/aRaw URL or text. Max 2 048 chars.
data_typeenum"url"url, text, vcard, wifi, email, sms, geo, dynamic
payloadobjectfor non-url/text typesn/aStructured payload. Schema depends on data_type.

For url and text, just pass data. For all others, build a payload object. See Data types.

Output format and dimensions

FieldTypeRequiredDefaultDescription
formatenum"png"png or svg
size_inchesnumberyesn/a0.5 to 15. Plan capped.
dpiinteger30072 to 600. Plan capped.

Pixel size = size_inches x dpi. So 4 inches at 300 DPI = 1 200 x 1 200 px.

Style

FieldTypeRequiredDefaultDescription
colorstring"black"black, white, or #RRGGBB[AA]
backgroundstring"transparent"Same as color, plus "transparent"
patternenum"rounded"rounded, squares, dot, diamond, liquid, connected-h, connected-v, hexagon, star, halftone, frame
gradient_fromhexn/aTop stop of vertical gradient (must pair with gradient_to)
gradient_tohexn/aBottom stop of vertical gradient

Eye styling

FieldTypeDefaultDescription
eye_shapeenum"square"Coarse knob: square, rounded, circle, frame
eye_colorhexinherits colorCustom eye color
eye_outer_frameboolfalseAdd a subtle outer ring
eye_outer_frame_matchboolfalseMatch the outer ring to the eye color
eye_border_shapeenumderivedsquare, rounded, circle, leaf-tl, leaf-tr, leaf-br, leaf-bl, frame
eye_center_shapeenumderivedsquare, rounded, circle, leaf-tl, sunburst, star, diamond, cross
eye_border_colorhexderivedIndependent border color
eye_center_colorhexderivedIndependent center color

Frame

FieldTypeDefaultDescription
frame_styleenum"none"none, rounded, scan-me-top, scan-me-bottom, wave, topo, botanical
frame_colorhex#6c48e8Frame fill color
frame_labelstringemptyUp to 24 chars, shown in scan-me-* styles
frame_label_colorhex#ffffffLabel text color

Logo (optional, Starter+ only)

FieldTypeDefaultDescription
logo_filefilen/aMultipart upload. Max 2 MB, max 1024 x 1024 px.
logo_urlURLn/aHTTPS URL we fetch. SSRF protected.
logo_size_rationumber0.240.0 to 0.30. Logo diameter as fraction of QR.
logo_clear_zonebooltrueCarve a clean zone behind the logo.
logo_neon_alphaboolfalseStrip dark backings via luminance alpha.

You can supply either logo_file or logo_url, not both. Custom logos require Starter plan or higher.

Background image (optional, Starter+ only)

FieldTypeDefaultDescription
bg_image_filefilen/aMultipart upload, painted under the modules
bg_image_urlURLn/aHTTPS URL, SSRF protected
bg_image_opacitynumber0.350.0 to 1.0

Caption (optional)

A human-readable identifier rendered as a centered text band BELOW the QR matrix. Sits outside the QR's quiet zone, so scannability is unaffected. Use case: print shops generating sticker batches need to match each printed QR to its order without scanning - the caption ("USER-2401", "SKU-A14", "ORDER-9821") sits visibly under the QR.

FieldTypeDefaultDescription
captionstringemptyUp to 60 chars. Allowed: A-Z a-z 0-9 and -_./#:() plus space
caption_colorhexmatches colorOverride the caption text color
caption_size_rationumber0.05Font height as fraction of QR width. Range 0.02-0.50.

The text scales with the QR's printed width, so a 1" sticker, a 3" flyer, and a 15" poster all get a caption that occupies the same visual fraction of the printed image (~9% with the default 0.05). Adjust caption_size_ratio for the look you want:

RatioUse caseBand size on a 15" poster
0.02Discreet ID162px (3.6%)
0.05 (default)Standard label405px (9%)
0.10Bold label720px (16%)
0.25Hero/event banner2025px (45%)
0.50"Banner with mini-QR on top"4050px (90%)

The output PNG is no longer a perfect square when caption is set: the height grows to fit the text band. The width still matches size_inches * dpi.

{
  "data": "https://app.example.com/u/2401",
  "caption": "USER-2401",
  "caption_size_ratio": 0.05,
  "size_inches": 3,
  "dpi": 300,
  "color": "black",
  "background": "white"
}

Pair with pattern: "halftone" for the photo-revealed-through-dots effect.

Response

Success: 200 OK

HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 59143
X-QR-Cache: MISS
X-QR-Duration-Ms: 318
X-QR-Plan: starter
X-QR-Quota-Remaining: 487
X-Request-Id: 01HZ8X...

<binary PNG bytes>

The body is the raw image. Save it directly, stream it, or pipe to S3.

For dynamic items (data_type: "dynamic") the response also adds:

X-QR-Dynamic-Short-Id: aBc12dEf
X-QR-Dynamic-Public-Url: https://q.qrstudio.agency/q/aBc12dEf/

400 Bad Request

Validation failure. Body is JSON describing the bad fields:

{
  "size_inches": ["Ensure this value is less than or equal to 15."],
  "logo_url": "refusing to fetch evil.com: resolves to private/internal IP 10.0.0.5"
}

401 Unauthorized

Missing or invalid X-Api-Key.

{ "detail": "Invalid or revoked API key." }

403 Forbidden

Your plan does not allow what you asked for.

{ "detail": "Plan 'free' allows up to 3\". Upgrade for larger sizes." }

429 Too Many Requests

Monthly quota exhausted (Free / Enterprise) or rate limit exceeded.

{ "detail": "Monthly quota of 5 exceeded for plan 'free'." }

500 Internal Server Error

Render engine failed. Rare, retry once with backoff. If it persists, include X-Request-Id in your support ticket.

Validate

Same body schema as /generate/, but it only validates and returns the parsed payload. No image is rendered, no quota is spent.

POST /api/v1/validate/
X-Api-Key: smk_...
Content-Type: application/json
{
  "data": "https://example.com",
  "size_inches": 4,
  "color": "rouge"
}

Response (validation failure):

{
  "color": "Invalid color 'rouge'. Use 'black', 'white', or hex like #RRGGBB / #RRGGBBAA."
}

Useful for client-side form validation before charging quota.

Examples

curl -X POST https://api.qrstudio.agency/api/v1/generate/ \
  -H "X-Api-Key: smk_..." \
  -H "Content-Type: application/json" \
  -d '{"data": "https://example.com", "size_inches": 4}' \
  --output qr.png
{
  "data_type": "wifi",
  "payload": {
    "ssid": "Cafe Plein Soleil",
    "password": "BienvenueChezNous2026",
    "auth": "WPA"
  },
  "size_inches": 8,
  "color": "#0d0820",
  "background": "white",
  "format": "svg"
}
{
  "data_type": "vcard",
  "payload": {
    "name": "Alex Tremblay",
    "org": "Acme Co",
    "phone": "+15145551234",
    "email": "alex@yourcompany.com",
    "url": "https://yourcompany.com"
  },
  "size_inches": 2,
  "color": "black"
}
{
  "data_type": "dynamic",
  "payload": {
    "name": "Spring 2026 menu",
    "destination_url": "https://example.com/menus/spring-2026"
  },
  "size_inches": 6
}
{
  "data": "https://yourbrand.com",
  "size_inches": 6,
  "background": "white",
  "pattern": "rounded",
  "gradient_from": "#6c48e8",
  "gradient_to": "#1a0d3a",
  "eye_color": "#1a0d3a",
  "frame_style": "scan-me-bottom",
  "frame_color": "#6c48e8",
  "frame_label": "Scan to view menu"
}
{
  "data": "https://yourbrand.com",
  "size_inches": 6,
  "background": "white",
  "color": "#0d0820",
  "pattern": "halftone",
  "bg_image_url": "https://cdn.yourbrand.com/coffee.jpg",
  "bg_image_opacity": 0.45
}

Notes

  • All requests must use HTTPS in production. HTTP is allowed only on localhost.
  • Maximum request body: 5 MB (covers logo and background uploads).
  • The data string is encoded as UTF-8 before being passed to the QR generator.
  • ERROR_CORRECT_H is hardcoded and not tunable. This gives you a 30% obstruction tolerance, which the logo never exceeds.

See also

On this page