Generating one QR code per attendee badge, warehouse bin, or ticket sounds like a simple loop until you're doing it for a few thousand items and either your own rate limiter or the API's kicks in halfway through a batch job.
A few things that make batch QR generation less painful:
Concurrency, not sequential loops. A single QR code generation call is fast (no external network dependency if the service computes the matrix in-process rather than calling out to render it), so the bottleneck is almost always your own request-issuing pattern, not the API. A small worker pool (5-10 concurrent requests) clears a few thousand codes in well under a minute instead of one-at-a-time sequential awaiting.
async function generateBatch(items, concurrency = 8) {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
const batch = items.slice(i, i + concurrency);
const responses = await Promise.all(
batch.map((item) =>
fetch(`https://qr-api.p.rapidapi.com/v1/qr?data=${encodeURIComponent(item.url)}`, {
headers: { "X-RapidAPI-Key": process.env.RAPIDAPI_KEY, "X-RapidAPI-Host": "qr-api.p.rapidapi.com" },
}).then((r) => r.text())
)
);
results.push(...responses);
}
return results;
}
Respect the tier's rate limit rather than discovering it via 429s. If you know you're generating a few thousand codes in one job, check your plan's requests-per-month ceiling ahead of time rather than mid-batch — free tiers are usually sized for "generate codes as users request them," not "bulk-generate an entire event's worth in one run."
Cache the output if the input doesn't change. A QR code for a fixed URL/payload is deterministic — regenerating the same code repeatedly across job re-runs (a common failure mode when a batch job is re-triggered after a partial failure) wastes calls for zero benefit. Key your cache on the exact data+ecc+size params.
Stateless generation (no per-request server state, no database) is what makes this kind of batch job cheap to run in the first place — that's the whole design of QR API: every call is a pure function of its query params, nothing persisted, nothing logged beyond the request path. Same account also runs Validate (IBAN/email/phone/etc. format checks) and Currency API (exchange rates) if useful for the rest of an onboarding/checkout pipeline.
Top comments (0)