Advanced Topics
QR Code APIs & Bulk Generation
Making one QR code is a form. Making four thousand — one per product, per property, per table, per delegate — is a different activity with different failure modes. The hard part is rarely generating the codes; it's keeping track of which code ended up on which physical object, and being able to fix them later without touching four thousand records by hand.
The short version
- Past a few dozen codes, use an API. Below that, a UI is faster.
- The reconciliation problem is the real one — which code is on which object.
- Check batch semantics: all-or-nothing versus partial success changes your retry logic entirely.
- Key scope matters — a key usually determines which library you're writing to.
- Rate limits are per-day and per-second; batching is what makes large imports feasible.
- Export early and keep it. A CSV of code-to-destination mappings is your continuity plan.
When an API is actually worth it
Honestly: later than people think. A UI wins up to a surprising volume, because it needs no code, no auth setup and no error handling.
| Situation | Use |
|---|---|
| A handful of codes, made once | The generator UI |
| 20–50 codes, made once | UI, or a bulk import if one exists |
| Hundreds, or regenerated periodically | API |
| One code per item in a catalogue | API, driven from the catalogue |
| Codes created by another system | API |
| Destinations that change on a schedule | API, with a scheduled job |
The tipping point isn't really the count — it's whether the codes have to stay in sync with something else. A thousand codes generated once and never touched again is a one-off job. Fifty codes that must always point at whatever your CMS currently says is an integration, and that's an API even at fifty.
Batch semantics: the thing to check first
Every bulk API takes a list. What differs — and what determines how you write the client — is what happens when one item in the list is invalid.
- All-or-nothing. One bad record means nothing is created. Simpler to reason about: you either have the whole batch or none of it, and a retry is safe. The cost is that a single malformed URL in a batch of 50 blocks the other 49.
- Partial success. Valid records are created, invalid ones reported. More forgiving, but your client must parse per-item results and retry precisely — and a naive retry duplicates everything that already succeeded.
Neither is wrong, but assuming the wrong one produces either duplicate records or a silent gap. Check before writing the import.
Our public API is all-or-nothing on batch creation: a validation error on any item means nothing in that batch was created, and the response identifies the offending index. Batches accept up to 50 records per call. That combination makes retries safe — you fix the bad record and resend the whole batch without worrying about duplicates.
Idempotency
The classic bulk-import failure: a request times out, you retry, and now you have two of everything. Check whether the API offers an idempotency key. If it doesn't, keep your own record of what you've submitted and reconcile before retrying rather than assuming a timeout means failure — a timeout usually means "I don't know."
Keys, scope and rate limits
API keys for QR services typically encode more than identity — they often determine which library you're writing to. Creating a code with the wrong key can put it in a personal library instead of a shared team one, which is annoying to unpick after four thousand of them.
On this site there are two key types, and the key alone decides the target — there's no separate URL prefix for business operations. A personal key writes to that account's own library; a business key writes to the team's shared library, and a code created there enters the same draft → review workflow a human teammate's entry would. Rate limits differ too: 2,000 requests/day and 5/second for personal keys, 10,000/day and 10/second for business ones. Full detail in the API getting-started guide.
On rate limits generally: the per-second limit governs how fast you can go, the per-day limit governs how much you can do at all, and batching is what reconciles them. At 50 records per call, importing 4,000 codes is 80 requests — trivial against any sane daily limit. Importing them one at a time is 4,000 requests and will hit something.
Two habits worth building into any import script: respect 429s with exponential backoff rather than hammering, and log every response. When something goes wrong at record 2,300 you'll want to know exactly what came back.
The reconciliation problem
This is the part that actually bites, and it has nothing to do with the API.
You generate 4,000 codes. They're printed onto 4,000 labels. The labels go onto 4,000 products. Six months later someone asks: "the code on product SKU-8871 goes to the wrong page — which record is that?"
If you didn't keep the mapping between your identifier and the code identifier at generation time, answering that means scanning a physical product to find out. That's recoverable for one item and impossible for a recall.
- Store the mapping as you create. Your SKU or property reference alongside whatever ID the API returns, written at creation rather than reconstructed later.
- Put your own reference in the code's name field if there is one, so it's visible in the UI too.
- Export a CSV immediately after import and keep it in version control or a document store. It's the artefact you'll actually reach for.
- Re-export periodically. Destinations drift as people edit them by hand.
A CSV export route matters more for this than for reporting — it's your continuity plan. If the provider disappears, that file is the only thing standing between you and 4,000 unidentifiable codes in the wild.
Rendering the images
Worth separating two things that get conflated: managing code records and producing image files.
Some APIs return a PNG. Others manage the record — destination, name, scan history — and leave rendering to you. That's a deliberate split, not an omission: rendering is cheap, offline, and something you probably want control over anyway, since the styling, size and format need to match your print process.
Our API is records-only — it creates, lists, updates and exports QR code records, and does not render images server-side. That matches how the generator itself works: every code is rendered client-side in the browser, and nothing you encode is transmitted to a server. For a bulk workflow you take the destination URLs from the API and render locally with whatever library fits your pipeline, at whatever size and error correction level your print job needs.
Practically this is usually what you want for print anyway. Rendering locally means vector output at exact dimensions, consistent styling, and no dependency on a remote service during a print run — see the sizing guide for what your print process needs.
A sane import workflow
- Dry run on ten records. Verify the shape of what comes back before doing four thousand.
- Generate a test code and scan it physically, at final size on final stock, before committing to a print run.
- Import in batches, logging every response with your own reference attached.
- Export immediately and store the CSV somewhere durable.
- Reconcile counts. If you sent 4,000 and the export has 3,987, find the 13 now rather than after printing.
- Spot-check destinations across the range — first, last, and a random middle sample.
Step 5 catches the most expensive class of error, and it takes one line of code.
Frequently asked questions
When should I use a QR code API instead of a generator?
When the codes need to stay in sync with another system, or when there are enough of them that clicking becomes the bottleneck — practically, past a few dozen. A thousand codes generated once and never touched is still a one-off job you could do with a bulk import. Fifty codes that must always reflect what your catalogue currently says is an integration, and that's an API even at fifty.
How do I generate thousands of QR codes at once?
Use a batch endpoint and send records in chunks — at 50 per call, 4,000 codes is 80 requests rather than 4,000. Check whether batches are all-or-nothing or partial-success first, because that determines whether a naive retry is safe or creates duplicates. Log every response with your own reference attached, export a CSV as soon as the import finishes, and reconcile the count before anything goes to print.
How do I know which QR code went on which product?
By storing the mapping at creation time — your SKU or reference alongside the identifier the API returns — rather than trying to reconstruct it later. Put your own reference in the code's name field too, so it's visible in the UI. Then export a CSV and keep it somewhere durable. Without that mapping, answering "which record is the code on SKU-8871?" means physically scanning a product, which doesn't scale to a recall.
Do QR code APIs return the image?
Some do, some manage only the record and leave rendering to you. The records-only split is usually preferable for print work: rendering locally gives you vector output at exact dimensions, consistent styling, and no dependency on a remote service mid-print-run. This site's API is records-only, matching its client-side generation model — you take the destination URLs and render with whatever library suits your pipeline.
What happens if a bulk request fails halfway through?
Depends entirely on the batch semantics, and it is worth separating two different "failures". A validation failure is the well-defined one: with all-or-nothing batches nothing was created, so fixing the bad record and resending is safe, while partial-success batches leave records behind that a blind retry will duplicate.
A failure mid-write — a timeout, a throttle, a dropped connection after the request was accepted — is a different question, and most APIs are quieter about it than they are about validation. Unless a provider states that a batch is written in a single transaction, assume some records may exist. Timeouts are the nastiest case of all, because they mean "unknown" rather than "failed". Our own API is all-or-nothing on validation, and does not currently guarantee it for a mid-write failure — so for large imports, export and reconcile before retrying rather than resending blind.