Skip to content
API key

Resources

Rate limits, pagination and versioning

The rules that apply to every endpoint, in one place.

Rate limits

Each API key may send a fixed number of requests per minute. The number comes from the account’s plan, and every key gets its own allowance; keys do not share one.

Requests per minute by plan
PlanLineREST APIRequests per minute, per key
FreeQR plansNot included30
LaunchQR plansNot included60
GrowthQR plansNot included60
ProQR plansIncluded120
Free CardCard plansNot included30
TeamCard plansNot included60
BusinessCard plansIncluded120

Only the plans marked “Included” can call the REST API, so in practice a REST key has 120 requests a minute. The other rows apply to the same keys used with AI assistants. GET /me reports the figure for the key you are using, in rateLimit.requestsPerMinute.

Headers

Rate limit response headers
HeaderMeaning
RateLimit-LimitThe key’s allowance per window.
RateLimit-RemainingRequests left in the current window.
RateLimit-ResetSeconds until the window resets.

Going over

The request is refused with 429. Wait RateLimit-Reset seconds and send it again; nothing was done, so repeating it is safe.

429 response
{
  "success": false,
  "message": "API rate limit exceeded. Retry after the window resets — see the RateLimit-Reset header."
}

There are no separate daily or monthly request quotas on the API. Plans do limit how many cards and QR codes an account may hold, which shows up as 403 limit_exhausted_cards or 403 limit_exhausted_products, not as a 429.

Pagination

List endpoints return one page at a time, newest first, in this envelope:

List envelope
{
  "object": "list",
  "data": [
    "…"
  ],
  "pagination": {
    "total": 240,
    "limit": 25,
    "offset": 0,
    "hasMore": true
  }
}
Pagination parameters and fields
NameWhereMeaning
limitQueryItems per page, 1 to 100. Default 25 (20 for /campaigns).
offsetQueryHow many items to skip. Default 0.
pagination.totalResponseHow many items match in all.
pagination.hasMoreResponsetrue when there is another page after this one.

To read everything, raise offset by limit until hasMore is false:

list-all.mjs
// Read every card, one page at a time.
const cards = [];
for (let offset = 0; ; offset += 100) {
  const res = await fetch(`https://api.qrbold.com/api/public/v1/cards?limit=100&offset=${offset}`, {
    headers: { Authorization: `Bearer ${process.env.QRBOLD_API_KEY}` },
  });
  if (!res.ok) throw new Error(`QRBold API ${res.status}`);
  const page = await res.json();
  cards.push(...page.data);
  if (!page.pagination.hasMore) break;
}
console.log(`${cards.length} cards`);
  • A limit over 100 answers 422; it is not quietly reduced.
  • There are no cursors. Paging is by offset only.
  • Because lists are newest first, a record created while you are paging shifts later pages by one. For a full export, de-duplicate by id.
  • Archived items are left out of lists unless you ask for status=archived.

The first page of cards:

curl "https://api.qrbold.com/api/public/v1/cards?limit=25&offset=0" \
  -H "Authorization: Bearer $QRBOLD_API_KEY"

Idempotency

An idempotency key lets you repeat a write safely. Send any unique string in the Idempotency-Key header of a POST, PATCH or DELETE:

Header
Idempotency-Key: 6f4e9d3c-7e3c-4b91-a8ab-123456789abc
How idempotency keys behave
SituationResult
First request with a keyRuns normally. The response is remembered for 24 hours.
Same key, same request, within 24 hoursThe original response is returned again, with the header Idempotent-Replay: true. Nothing runs twice.
Same key, different body, path or query409 idempotency_key_reused
Same key while the first request is still running409 idempotency_key_in_flight. Wait a few seconds and repeat.
The first request failed with a 5xxNot remembered. The retry runs for real.
A key longer than 255 characters400 invalid_idempotency_key
No key at allThe request runs every time it is sent.
  • The header is optional, and recommended on every write.
  • Keys belong to the API key that sent them. Two API keys can use the same value without clashing.
  • A UUID is the usual choice.

Versioning and compatibility

The version is part of the path: /api/public/v1. v1 is the current and only version. There is no version header and no dated versions.

What can change inside v1

Changes that may happen without a new version
May be added at any timeSo your code should
New endpointsNot need to do anything.
New fields in a response objectIgnore fields it does not recognise.
New optional request fields and query parametersNot need to do anything.
New values for error.codeFall back to the HTTP status for a code it does not know.
New values in sets such as link types or QR typesNot fail on an unknown value.
Changed wording of error.messageNever match on message text.

Removing a field, renaming one, changing a type or making an optional field required would be a breaking change and belongs in a new version path.

QRBold does not currently publish an API changelog or a deprecation schedule. These pages were last checked against the API on 2026-10-09. Where the API’s behaviour is irregular today, it is documented as it is; see responses that break the pattern.

Formats

Data formats
ThingFormat
Request and response bodiesJSON, UTF-8. Send Content-Type: application/json.
Dates and timesISO 8601 in UTC, e.g. 2026-10-09T09:30:00.000Z.
Analytics daysYYYY-MM-DD, in the time zone you asked for.
IdsOpaque strings. Do not parse them or assume a length.
Empty valuesnull, not an absent key, for fields such as templateId and externalId.
Booleans in a query stringtrue or false.
Request sizeCampaign bodies up to 1 MB. Keep other bodies well under that.
TransportHTTPS only.