QRQR Code Agency
API reference

API reference

Complete reference for every endpoint, parameter, header, and response code in the QR Code Agency API.

API reference

Complete reference for every endpoint, parameter, header, and response code.

Endpoints

Render

EndpointAuthPurpose
POST /api/v1/generate/API keyRender a single QR (PNG or SVG)
POST /api/v1/generate/bulk/API keyRender up to 5 000 QRs and return a ZIP archive
POST /api/v1/validate/API keyDry-run validation of a payload, no quota cost

Dynamic QRs

EndpointAuthPurpose
GET /api/v1/dynamic/JWTList your dynamic QRs (paginated)
POST /api/v1/dynamic/JWTCreate a dynamic QR (no render)
GET /api/v1/dynamic/{short_id}/JWTRetrieve one
PATCH /api/v1/dynamic/{short_id}/JWTUpdate destination, name, variants, active state
DELETE /api/v1/dynamic/{short_id}/JWTDelete (kills the redirect)
GET /api/v1/dynamic/{short_id}/analytics/JWTAggregate scan analytics
GET /q/{short_id}/noneThe public scan endpoint. 302 redirects to the live destination.

Webhooks

EndpointAuthPurpose
GET /api/v1/webhooks/JWTList subscriptions
POST /api/v1/webhooks/JWTCreate a subscription
PATCH /api/v1/webhooks/{id}/JWTUpdate events, URL, status
DELETE /api/v1/webhooks/{id}/JWTDelete
POST /api/v1/webhooks/{id}/rotate/JWTRotate the signing secret
GET /api/v1/webhooks/{id}/deliveries/JWTInspect recent deliveries

Usage and keys

EndpointAuthPurpose
GET /api/v1/usage/API keyCurrent month quota for the calling key
GET /api/v1/usage/me/JWTAggregate quota across every key in the workspace
GET /api/v1/keys/JWTList your API keys
POST /api/v1/keys/JWTCreate a new API key
DELETE /api/v1/keys/{id}/JWTRevoke a key
POST /api/v1/keys/{id}/rotate/JWTRotate (issues a new raw key, invalidates the old)

Health and status

EndpointAuthPurpose
GET /api/health/noneCheap liveness check, returns {"status":"ok"}
GET /api/status/noneStructured readiness, probes DB and cache

Account and billing

EndpointAuthPurpose
POST /api/auth/signup/noneCreate a workspace and user
POST /api/auth/login/noneIssue a JWT pair
POST /api/auth/token/refresh/refresh JWTRotate the access JWT
GET /api/auth/me/JWTCurrent user profile
POST /api/billing/checkout/JWTStripe checkout session for a plan
POST /api/billing/credits/checkout/JWTStripe checkout session for a credit pack
GET /api/billing/status/JWTCurrent subscription state
GET /api/billing/credits/JWTCurrent credit balance
POST /api/billing/portal/JWTStripe Customer Portal link
POST /api/billing/webhook/Stripe sigStripe webhook ingest (idempotent)

Base URL

EnvironmentURL
Productionhttps://api.qrstudio.agency
Local developmenthttp://localhost:8000

All production traffic is HTTPS only. Plain HTTP is allowed only on localhost.

Authentication

All /api/v1/generate/, /api/v1/generate/bulk/, and /api/v1/usage/ endpoints require an X-Api-Key header. Dashboard endpoints (keys, dynamic QRs, webhooks, billing, account) require a JWT bearer token.

The Stripe webhook endpoint authenticates using the Stripe-Signature header against STRIPE_WEBHOOK_SECRET.

See Authentication for the full flow.

Standard response headers

Every successful render response carries:

HeaderExampleMeaning
X-QR-CacheHIT or MISSWhether the result came from cache
X-QR-Duration-Ms0 to 15000Render time (0 if cache hit)
X-QR-PlanstarterThe current plan of the calling key
X-QR-Quota-Remaining487Calls left this calendar month
X-Request-Id01HZ8...ULID for support tickets and tracing

Error format

Errors use standard HTTP status codes plus a JSON body:

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

For per-field validation errors:

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

For per-item bulk errors:

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

See Errors for the full table.

Rate limiting

PlanRequests / minuteBurst
Free3060
Starter120240
Pro6001 200
Agency2 4004 800
EnterpriseCustomCustom

Rate limits are returned via X-RateLimit-Remaining and X-RateLimit-Reset headers. Exceeding them returns 429 Too Many Requests with Retry-After set in seconds.

Pagination

List endpoints (GET /api/v1/dynamic/, GET /api/v1/keys/, GET /api/v1/webhooks/, GET /api/v1/webhooks/{id}/deliveries/) use PageNumber pagination:

GET /api/v1/dynamic/?page=2&page_size=50

Default page_size is 25, max is 100. Response shape:

{
 "count": 137,
 "next": "https://api.qrstudio.agency/api/v1/dynamic/?page=3",
 "previous": "https://api.qrstudio.agency/api/v1/dynamic/?page=1",
 "results": [ ... ]
}

Versioning

The HTTP API path is versioned (/api/v1/). Breaking changes happen at major version boundaries (/api/v2/) with a 12 month overlap. Additive changes (new fields, new endpoints, new event types) ship within v1 without a version bump.

OpenAPI specification

GET https://api.qrstudio.agency/openapi.json

Regenerated on every deploy.

On this page