QRMaple API v1

Available on Pro and Premium. Anyone can read this page, but API keys can only be created on those plans.

A REST API for managing your dynamic QR codes and short links programmatically: create, read, update, delete, and pull scan stats.

See plan limits on the pricing page.

Base URL

https://qrmaple.com/api/v1

Authentication

Every request needs an `Authorization: Bearer <token>` header. There is no cookie-session access to this API — only an API key works.

Create a key from the dashboard's "API keys" screen. The full key is only ever shown once, right after creation.

Quickstart

curl -X POST https://qrmaple.com/api/v1/links \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://example.com/landing","title":"Spring promo"}'
{
  "id": "018f3a2e-...-b1c2",
  "code": "aB3xQ9z",
  "destination": "https://example.com/landing",
  "title": "Spring promo",
  "domain": null,
  "expires_at": null,
  "active": true,
  "created_at": "2026-09-24T12:00:00.000Z"
}
curl https://qrmaple.com/api/v1/links \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx"
curl https://qrmaple.com/api/v1/links/018f3a2e-...-b1c2 \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx"
curl -X PATCH https://qrmaple.com/api/v1/links/018f3a2e-...-b1c2 \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://example.com/new-landing"}'
curl "https://qrmaple.com/api/v1/links/018f3a2e-...-b1c2/stats?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx"
curl -X DELETE https://qrmaple.com/api/v1/links/018f3a2e-...-b1c2 \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx"

Endpoints

MethodPath
GET/api/v1/linksList links, paginated.
POST/api/v1/linksCreate a new dynamic link.
GET/api/v1/links/:idGet a single link.
PATCH/api/v1/links/:idUpdate a link (destination, title, expiry, active).
DELETE/api/v1/links/:idDelete a link (logical delete — active is set to false).
GET/api/v1/links/:id/statsGet scan stats for a link, optionally bounded by day.

Pagination

GET /links accepts `limit` (1-100, default 20) and `cursor` (an opaque value from the previous response's `next_cursor`). `next_cursor` is null on the last page.

curl "https://qrmaple.com/api/v1/links?limit=50" \
  -H "Authorization: Bearer qrm_live_xxxxxxxxxxxxxxxx"

# {"data": [...], "next_cursor": "eyJjcmVhdGVkX2F0Ijoi..."} -- pass next_cursor back as ?cursor= for the next page

Errors

Every error response has the shape `{"error": {"code": "...", "message": "..."}}`. The HTTP status always matches the code:

{
  "error": {
    "code": "not_found",
    "message": "link not found"
  }
}
code
unauthorized401 — missing, invalid, or revoked Bearer token.
forbidden_scope403 — this token isn't an API key (it's a different kind of access token).
tier_required403 — the workspace's plan no longer includes API access (e.g. after a refund/downgrade).
limit_reached402 — a plan limit (e.g. dynamic links) was reached.
rate_limited429 — too many requests; see Rate limits below.
validation_error400 — the request body or query parameters failed validation.
not_found404 — the resource doesn't exist, or belongs to a different workspace.
conflict409 — the requested code is reserved or already in use.

Rate limits

60 requests per minute, per API key. A 429 response includes a `Retry-After` header (seconds) plus `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers.

OpenAPI

Machine-readable spec: openapi.json

limit: 1-100, default 20.