5 min de lecturaFunciones

Bulk QR Code Generation: From a CSV to Print-Ready Files

Generate QR codes in bulk from a spreadsheet: create a short link per row, render SVG and PNG through an API, and verify a sample before anything goes to print.

Ana Kowalska
Marketing solutions engineering
Bulk QR code generation: a CSV of destinations turned into short links and rendered as print-ready SVG and PNG QR codes through an API

Bulk QR code generation is two API calls per row and a folder at the end. Create a short link for each destination, render a QR code for that short link, write the SVG and PNG to disk. A spreadsheet of 5,000 products becomes 5,000 print-ready files in a few minutes.

The tooling is not the decision. What the code encodes is. Encode the destination directly and the code is frozen the moment it goes to print, so a single wrong URL in a run of 5,000 stickers means the whole run is scrap; encode a short link you control and the destination stays editable forever, while each code quietly reports its own scans.

Making one code rather than thousands? How to create a QR code is the simpler walkthrough, and QR code campaign from scratch covers placement and measurement.

The Pipeline in Four Stages

A four-stage bulk QR code pipeline: read a CSV of destinations, create a short link per row, render the QR code as SVG and PNG, and verify a printed sample by scanning

Keep an id column in the CSV, even if it feels redundant. It becomes the filename, the idempotency key, and the thing you match on when the design team sends back "code 412 is wrong". Without it you are matching on URLs, and URLs are long and easy to mistype.

import csv, base64, hashlib, pathlib, os, requests

API = "https://api.elido.app"
HEAD = {"Authorization": f"Bearer {os.environ['ELIDO_API_KEY']}"}
out = pathlib.Path("qr-out")
out.mkdir(exist_ok=True)

with open("products.csv") as f:
    for row in csv.DictReader(f):        # columns: id, destination
        key = hashlib.sha256(row["destination"].encode()).hexdigest()

        link = requests.post(
            f"{API}/v1/links",
            headers={**HEAD, "Idempotency-Key": key},
            json={"destination_url": row["destination"]},
            timeout=10,
        )
        link.raise_for_status()
        short_url = link.json()["short_url"]

        qr = requests.post(
            f"{API}/v1/qr/generate",
            headers=HEAD,
            json={
                "content": short_url,          # the short link, never the destination
                "formats": ["svg", "png"],
                "size": 1024,
                "error_correction": "M",
            },
            timeout=20,
        )
        qr.raise_for_status()
        payload = qr.json()

        (out / f"{row['id']}.svg").write_text(payload["svg"])
        (out / f"{row['id']}.png").write_bytes(base64.b64decode(payload["png"]))
        print(row["id"], short_url)

The idempotency key is derived from the destination, so re-running the file after fixing three rows creates nothing twice and costs nothing. That property is what makes a bulk job safe to repeat, and it is worth understanding properly before you point this at 5,000 rows: rate limits and idempotency covers the mechanics.

The loop above is sequential, which is fine up to a few hundred rows. Past that, run about eight in parallel; the Python and JavaScript guides both have a bounded concurrency pattern you can lift directly.

Want to run it? Create a key on the free plan, export ELIDO_API_KEY, and point the script at a five-row CSV before you point it at the real one.

Static or Dynamic, at Scale

Static QR codes encoding the destination compared with dynamic QR codes encoding a short link, for a bulk print run where destinations may change

A static code has exactly one advantage: it depends on nothing. No domain to keep alive, no service to keep paying for. That matters for a code etched into a machine that will outlive your vendor contract, and it matters almost nowhere else.

Everything else favours dynamic. Scan counts per code, which is the only way to compare shelf placements or store locations. A destination you can correct when the campaign page moves. And smaller, sparser codes, because a short link is a fraction of the characters of a tagged product URL. Dynamic versus static QR codes has the full comparison.

Three parameters decide whether the batch scans in the real world.

Error correction. Level M recovers from roughly 15 percent damage and keeps the code sparse. Move to Q when a logo sits in the middle or the code will be handled, scuffed, or printed on something curved. H sounds safer and usually is not: the extra redundancy packs in more modules, and denser codes are harder to scan at small sizes. The QR standard documents all four levels.

Format. SVG for print, because it scales to a poster without softening the module edges. PNG for slides, marketplaces, and anything that rejects vectors. Ask for both in one request and you skip the round trip when someone needs the other one.

Quiet zone. The blank margin around the code is part of the code. Four modules is the specified minimum, and designers reliably crop it because it looks like wasted space. A code with a tight border scans badly on cheap phone cameras and nobody ever connects the two.

One more thing worth knowing before you fix a size for the whole batch: the amount of data decides the version, and the version decides how many modules the code has. Denso Wave, which invented the format, publishes the version and capacity table. A 30-character short link sits near the bottom of it; a tagged product URL of 180 characters does not, and the difference shows up as a visibly denser code at the same printed size.

Size in the file matters less than size on the object. How big should a QR code be has the scanning-distance rule that decides the physical dimension.

Verify Before Anything Goes to Print

Generate a batch, then check five codes at random. Not on your desk at full screen brightness: print them at the size they will actually be, tape one to the shelf or the packaging, and scan from where a customer would stand.

Then check the redirect itself. curl -sI on three of the short links confirms they return a redirect to the right destination, which catches the classic off-by-one where column two of the CSV was read as column three and every code points at the neighbouring product. That one is silent, survives every visual check, and is only visible in the destination.

Keep the CSV that produced the batch, with the id, destination, short link, and filename in it. When someone asks in six months which code is on which pallet, that file is the answer, and regenerating it from the dashboard is far more work than keeping it.

Where the Scans Show Up

Once the codes are live, each one reports separately. That is the whole reason for one code per item rather than one shared code: a single code tells you the campaign worked, and per-item codes tell you which store, shelf, or SKU worked.

How to track QR code scans covers reading that data, and webhooks for link events covers pushing scans into your own systems rather than checking a dashboard. For a physical-product context specifically, digital product passport QR codes covers the regulatory version of the same pipeline.

Read the Cornerstone Series

This sits in the features cluster. Start with how to create a QR code for the single-code version, then QR code campaign from scratch for the campaign design. The QR codes feature page covers the styling options this script skips.

Preguntas frecuentes

How do I generate QR codes in bulk?

Loop over a CSV, create a short link for each row, then render a QR code for each short link through the API and write the files to disk. Two calls per row and a folder at the end. Encoding the short link rather than the destination is what keeps every code editable after printing.

Should bulk QR codes be static or dynamic?

Dynamic, in almost every bulk case. A static code encodes the destination directly, so one wrong URL in a print run of 5,000 means reprinting. A dynamic code encodes a short link you control, which means the destination is fixable afterwards and each code reports its own scans.

Which error correction level should I use for printed QR codes?

Level M is the sensible default: it recovers from roughly 15 percent damage and keeps the code sparse enough to scan quickly. Go to Q when a logo covers the middle or the code will live somewhere that gets scuffed. H is rarely worth it, because the extra density makes the code harder to scan at small sizes.

What file format should bulk QR codes be in?

SVG for anything that gets printed, because it scales to any size without softening the module edges. PNG for screens, slides, and marketplace uploads that reject vectors. Generating both at once costs one request and saves the round trip when the design team asks for the other one.

How many QR codes can I generate at once?

As many as the rate limit allows, which is a throughput question rather than a hard cap. Run about eight requests in parallel, send an idempotency key derived from each row so a re-run creates nothing twice, and a batch of several thousand finishes in minutes.

Do I need a separate QR code for each product or location?

Yes, if you want to know which one was scanned. One shared code tells you the campaign worked; one code per item tells you which shelf, store, or SKU worked. The per-item version costs nothing extra to generate and is the only way to compare placements.

Prueba Elido

Pega una URL, obtén un enlace corto

Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.

Gratis, sin registro · 2 por día

Prueba Elido

Acortador de URL alojado en la UE: dominios personalizados, análisis profundo y API abierta. Plan gratuito - sin tarjeta de crédito.

Etiquetas
bulk qr code generation
generate qr codes from csv
bulk qr code generator api
qr codes at scale
dynamic qr codes bulk
qr code print files

Seguir leyendo