QRTracking REST API

v1 reference

Programmatic access to your QR codes. Authenticate with an API key, list and modify QR codes, and pull scan analytics.

Getting started

  1. Sign in to Settings → API Keys and click Create API Key.
  2. Copy the full secret immediately — it’s shown once. The prefix (qrt_…) is visible later in the dashboard for identification, but the full secret is not stored in plaintext on our side.
  3. Make a hello-world request to verify the key works:
    curl https://api.qrtracking.net/v1/auth/verify \
      -H "Authorization: Bearer qrt_your_key_here"
    A successful response looks like:
    {
      "authenticated": true,
      "organization": { "id": "...", "name": "Acme Co" },
      "apiKey": { "name": "Production", "prefix": "qrt_xxxx" }
    }

Base URL: https://api.qrtracking.net/v1

Authentication

Every request must carry your API key in the Authorization header with the Bearer scheme:

Authorization: Bearer qrt_your_key_here

Plan gating

API access requires a Pro or Business plan. Calls with a free or starter plan key return 403 PLAN_LIMIT.

Key lifecycle

  • Rotation: Create a new key, deploy it to your service, then revoke the old one. There is no built-in rotation primitive — you operate two keys briefly.
  • Revocation: A revoked key returns 401 API_KEY_REVOKED.
  • Expiration: Optional. Expired keys return 401 API_KEY_EXPIRED.
  • Audit trail: Each successful call updates the key’s last-used timestamp; per-key usage rows are visible in the dashboard.

Rate limits

Limits are applied per API key, with a per-minute window and an optional per-day cap. Headers on every response let you build client-side back-off:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute.
X-RateLimit-RemainingRequests remaining in the current minute window.
X-RateLimit-ResetUnix timestamp when the per-minute window resets.
X-Daily-LimitPer-day cap (when configured).
X-Daily-RemainingDaily quota remaining.
Retry-AfterSeconds to wait after a 429 before retrying.

On a 429, back off for the number of seconds in Retry-After before retrying. Don’t hammer.

Conventions

Pagination

Cursor-based on (created_at, id) so inserts during paging don’t drift the window. Pass the next_cursor from each response back as ?cursor= to fetch the next page. Default page size is 50; max is 200 via ?limit=. The cursor is an opaque token — treat it as a black box.

Timestamps

ISO 8601 with timezone, e.g. 2026-05-07T18:42:30.123Z.

IDs

UUID v4.

Error envelope

{
  "error": "human-readable message",
  "code": "MACHINE_READABLE_CODE"
}

Status codes follow standard semantics: 2xx success, 4xx client error, 5xx server error. Always check code rather than the status code alone — multiple errors can map to the same status.

QR codes

Manage your QR codes programmatically. Every endpoint is scoped to the organization that owns the API key — you cannot reach another org’s data.

List QR codes

GET /v1/qr-codes

Cursor-paginated, sorted newest first.

QueryPurpose
workspace_idFilter to a workspace UUID.
folder_idFilter to a folder UUID.
qCase-insensitive name search.
limit1–200, default 50.
cursorFrom a previous response.
curl 'https://api.qrtracking.net/v1/qr-codes?limit=50' \
  -H "Authorization: Bearer qrt_..."

{
  "data": [
    {
      "id": "...",
      "shortCode": "abcd1234",
      "name": "Welcome flyer",
      "destinationUrl": "https://example.com/welcome",
      "createdAt": "2026-05-07T18:42:30.123Z",
      ...
    }
  ],
  "next_cursor": "MjAyNi0wNS0wN1QxODozODoxMy4xMjNafGFiY2QtZWZnaA"
}

Retrieve a QR code

GET /v1/qr-codes/{id}

curl https://api.qrtracking.net/v1/qr-codes/<id> \
  -H "Authorization: Bearer qrt_..."

{
  "data": {
    "id": "...",
    "shortCode": "abcd1234",
    "name": "Welcome flyer",
    "destinationUrl": "https://example.com/welcome",
    "destinationPageId": null,
    "styleConfig": { ... },
    ...
  }
}

Scan stats

GET /v1/qr-codes/{id}/stats

Window defaults to the last 30 days. Pass ?from= and ?to= (ISO 8601) to customize.

{
  "data": {
    "qr_code_id": "...",
    "from": "2026-04-07T00:00:00.000Z",
    "to":   "2026-05-07T23:59:59.999Z",
    "total_scans": 4213,
    "unique_scans": 3104,
    "last_scanned_at": "2026-05-07T18:42:30.123Z",
    "daily_counts": [
      { "date": "2026-04-07", "scans": 12, "unique_scans": 11 },
      ...
    ],
    "top_countries": [
      { "country": "US", "country_name": "United States", "count": 2104 },
      ...
    ],
    "top_devices": [
      { "device_type": "mobile", "count": 3010 },
      ...
    ],
    "top_referrers": [
      { "referrer_domain": "twitter.com", "count": 482 },
      ...
    ]
  }
}

Create a QR code

POST /v1/qr-codes

Body fields: name (required), destinationUrl (required, http or https), and optional description, workspaceId, styleConfig. If workspaceId is omitted the org’s first workspace is used.

curl -X POST https://api.qrtracking.net/v1/qr-codes \
  -H "Authorization: Bearer qrt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome flyer",
    "destinationUrl": "https://example.com/welcome"
  }'

Coming soon: the Idempotency-Key header for safe POST retries (Stripe-style 24h dedupe). Until then, treat POST as non-idempotent and apply your own client-side dedupe if needed.

Update a QR code

PATCH /v1/qr-codes/{id}

Patch any subset of name, description, destinationUrl. The redirect cache is invalidated automatically when the destination changes — clients hit the new URL within a couple of seconds.

Delete a QR code

DELETE /v1/qr-codes/{id}

Soft-delete; the row is preserved for analytics history but the redirect endpoint starts returning 404 immediately. Restoration is not yet available via the API — use the dashboard’s Trash view.

Error reference

StatusCodeWhen
400INVALID_PARAMETERBody or query failed validation.
400INVALID_BODYRequest body wasn’t valid JSON.
400INVALID_CURSORPagination cursor is malformed.
401UNAUTHORIZEDMissing or malformed Authorization header.
401INVALID_API_KEYKey not recognized.
401API_KEY_REVOKEDKey was revoked from the dashboard.
401API_KEY_EXPIREDKey’s expiresAt is in the past.
403PLAN_LIMITOrg plan doesn’t include API access.
404NOT_FOUNDResource doesn’t exist or isn’t in this org.
409NO_WORKSPACEPOST without workspace_id, and the org has no workspaces.
429RATE_LIMITEDToo many requests; back off using Retry-After.

Webhooks, idempotency keys, and a downloadable OpenAPI spec are on the roadmap. For support, email support@qrtracking.net or open an issue from the support page.