QRQR Code Agency
API reference

Errors

Every status code, every error message, and what to do about each one.

Errors

Every error response uses a standard HTTP status code and a JSON body.

Status code reference

CodeMeaningCaused by
200 OKSuccessn/a
201 CreatedResource createdSuccessful POST to /keys/, /dynamic/, /webhooks/
204 No ContentResource deletedSuccessful DELETE on /keys/{id}/, /dynamic/{short_id}/, /webhooks/{id}/
302 FoundRedirectThe public scan endpoint /q/{short_id}/
400 Bad RequestValidation failedBad params, invalid hex, SSRF refusal, plan-cap on bulk size
401 UnauthorizedAuth missing or invalidNo X-Api-Key or no valid JWT, revoked key
402 Payment RequiredLocked behind upgradeFree tier exceeded its 1 lifetime destination edit
403 ForbiddenPlan gate hitAsked beyond your plan's caps
404 Not FoundResource missingDELETE / GET on a non-existent key, dynamic, or webhook
409 ConflictIdempotency mismatchSame Idempotency-Key reused with different body
422 Unprocessable EntityPer-item validationBulk endpoint with at least one invalid item
426 Upgrade RequiredHTTP not allowedPlain HTTP on /api/*
429 Too Many RequestsQuota or rate limitFree / Enterprise hit monthly cap, or rate limit triggered
500 Internal Server ErrorRender or unexpected failureShould be rare. Retry once.
503 Service UnavailableMaintenance or overloadStatus page tells you which

Body shape

For top-level errors:

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

For per-field validation errors:

{
 "size_inches": ["Ensure this value is less than or equal to 15."],
 "color": "Invalid color 'rouge'. Use 'black', 'white', or hex like #RRGGBB."
}

For bulk per-item errors:

{
 "errors": {
 "3": "size_inches > plan max (8)",
 "17": "Invalid color 'rouge'."
 },
 "rendered_before_failure": 17
}

Common errors and their fixes

400: "data": ["This field is required."]

You sent data_type: "url" but no data. Add the URL string.

400: "size_inches": ["Ensure this value is less than or equal to 15.0"]

The hard cap is 15 inches. If you need bigger, render at 15 inches and tile or scale in your printing software.

400: "logo_url": "refusing to fetch ...: resolves to private/internal IP ..."

SSRF protection blocked the URL because it points to an internal network. Host the logo on a public CDN.

400: "logo_file": "Max 2097152 bytes."

Logo is over 2 MB. Optimize it (tinypng.com, imagemin) or scale it down before uploading.

401: "Invalid or revoked API key."

The X-Api-Key header is missing, malformed, or points to a revoked key. Verify it starts with smk_ and is the current value (not a rotated-out copy).

402: Free tier destination edit cap

{
 "detail": "Plan 'free' allows 1 destination edit per dynamic QR. This QR has been edited 1 time(s) already. Upgrade to Starter ($7/mo) or higher for unlimited edits, or buy a credit pack."
}

Upgrade to any paid plan or buy credits.

403: Size or DPI cap

{ "detail": "Plan 'free' allows up to 3\". Upgrade for larger sizes." }
{ "detail": "Plan 'starter' caps DPI at 300. Upgrade for higher resolution." }

Reduce the value or upgrade.

403: Custom logo not allowed

{ "detail": "Plan 'free' doesn't support custom logos." }

Upgrade to Starter or higher.

403: Bulk endpoint disabled

{ "detail": "Plan 'free' doesn't include bulk generation. Upgrade to Starter or higher." }

422: Bulk per-item failure

{
 "errors": {
 "3": "size_inches > plan max (8)",
 "17": "Invalid color 'rouge'."
 },
 "rendered_before_failure": 17
}

Fix every offending item and retry the whole batch.

429: Monthly quota exhausted

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

Wait until the 1st of next month, upgrade, or buy a credit pack.

429: Rate limited

HTTP/1.1 429 Too Many Requests
Retry-After: 12

Throttle your client to your plan's documented req/min and re-try after Retry-After seconds.

500: Internal error

{ "detail": "Render engine failure. Retry once or contact support." }

Should be rare. Retry the exact same request once. If it persists, file a support ticket including:

  • The full request body
  • The response status code and body
  • The X-Request-Id value from the response headers

Handling errors in code

Python

import requests

r = requests.post(
"https://api.qrstudio.agency/api/v1/generate/",
headers={"X-Api-Key": "smk_..."},
json={"data": "https://x.com", "size_inches": 4},
)

if r.status_code == 429:
showUpgradeUI()
elif r.status_code == 403:
log.warning("Plan gate: %s", r.json()["detail"])
elif r.status_code == 401:
alertOps("Bad API key")
elif not r.ok:
log.error("API error %s: %s", r.status_code, r.text)
else:
with open("qr.png", "wb") as f:
f.write(r.content)

JavaScript

const r = await fetch("/api/v1/generate/", );

if (r.status === 429) {
showUpgradeModal();
} else if (r.status === 403) {
const { detail } = await r.json();
console.warn("Plan gate:", detail);
} else if (r.status === 401) {
console.error("Bad API key");
} else if (!r.ok) {
console.error("Server error", r.status);
} else {
const blob = await r.blob();
// ...
}

Retry policy recommendation

StatusRetry?Why
200n/aYou got the result
400 to 403NoCaller bug, retry will not help
404NoResource truly missing
409NoIdempotency mismatch, resolve client-side
422NoPer-item validation failure on bulk
429After Retry-AfterRe-running before that does nothing
5xxYes, with backoffLikely transient

Recommended backoff for 5xx: exponential, base 1 second, max 3 retries, add jitter so concurrent clients do not stampede.

See also

On this page