A single QR code tops out at about 2,953 bytes — roughly a dense paragraph — and that's the absolute ceiling, at the largest size and the weakest error correction. That's why almost everything you scan is a short URL: the code holds a pointer, and the real payload lives at the other end. But the squares themselves don't care what's inside. They encode bytes, and text, Wi-Fi credentials, calendar events, and payment strings all ride the same container as any link.
The bytes-not-links distinction matters the moment you push capacity. Two things fight for room: how much data you encode, and how much error correction you bake in. Error correction is redundancy — the code can lose part of itself to a coffee ring or a printed logo and still decode — but every recovery byte is a byte you can't spend on payload.
What error correction actually costs you
There are four levels, and the trade is direct: more damage tolerance, less capacity. Same numeric payload, four levels, one version-40 code:
| EC level | Recovers up to | Max numeric capacity | Use it for |
|---|---|---|---|
| L (Low) | ~7% | 7,089 digits | Clean screens, short-lived codes |
| M (Medium) | ~15% | 5,596 digits | The sensible default for print |
| Q (Quartile) | ~25% | 3,993 digits | Codes with a center logo |
| H (High) | ~30% | 3,057 digits | Harsh environments, logo overlays |
The takeaway: jumping from L to H costs you more than half your capacity. That's the actual reason a URL-shortener link scans more reliably than a full tracking URL crammed with UTM parameters — the short string leaves headroom to raise error correction, which shrinks the payload's module count, which makes every module bigger and easier for a camera to resolve. Short link, higher EC, bigger squares. It compounds in your favor.
Where QR codes go wrong in the field
These are the mistakes that survive "works on my 4K monitor" and die on a printed poster:
- A logo dropped on an L or M code. The overlay eats modules the error correction can't spare. If you want a center logo, encode at Q or H so the redundancy covers it — otherwise you're corrupting real data.
- No quiet zone. The spec wants a 4-module light border around the code. Butt it against a colored background or another graphic and many scanners never lock on.
- Inverted colors. Light modules on a dark background breaks a lot of scanners that assume dark-on-light. Keep it boring: dark code, light background, high contrast.
- Tiny modules at distance. A code sized for arm's length fails on a wall poster read from three meters. Test at the real print size and the real scanning distance, not on your monitor.
- A dynamic redirect you don't control. Route through a URL shortener or a "dynamic QR" service and the destination lives on someone else's server. When that service sunsets — or gets bought — the printed codes are dead and you can't repoint them.
- Too much data. Cram a vCard or a long signed payload in and the module count explodes into a dense grid that cheap cameras can't resolve. Encode a link to the data, not the data.
A QR code hides its destination until scan time — which is exactly what makes it a phishing primitive. Don't rely on the opacity; when trust matters, print the readable domain next to the code so a person can see where it goes before their camera commits.
The code is never the product. The action after the scan is, and a code that fails to scan is worse than no code at all — it's a dead end someone chose to trust. If you're placing one somewhere permanent, generate it, then walk to the far side of the room and scan the actual print before it goes live. A QR code generator lets you dial EC level and test a payload before it's ink.
Cover photo by Pixabay on Pexels.
