Every "free" QR API I tried wanted a signup, an API key, or quietly turned my QR into a tracked short link that could expire. So I built one that doesn't. Here's how it works — and how it stays free.
The itch
I needed to drop a QR code into a web page. Simple, right? But every option had a catch:
- Sign up, get an API key, paste it into a
.env. - Free tier, but the QR is actually a short link that redirects through their domain — so they can track every scan, and the code stops working if you stop paying.
- Or a heavy client-side library when all I wanted was
<img src="...">.
For a static piece of data — a URL, some text, a Wi-Fi credential — none of that should be necessary. A QR code is just a bitmap of your data. It shouldn't need an account, and it shouldn't phone home.
So I built QRKit: a free QR + barcode tool with a public API that needs no key.
The one-line version
<img src="https://qrkit.tools/api/qr?data=https://example.com&size=300" alt="QR code" />
That's it. It returns an image directly, CORS is open, and it works from any page or backend. No key, no SDK.
# SVG, custom colors, higher error correction
curl "https://qrkit.tools/api/qr?data=Hello%20World&size=400&format=svg&ecc=H&dark=175CD3" -o qr.svg
Params: data (required), size, format (png|svg), ecc (L/M/Q/H), dark, light, margin, download. Full docs: qrkit.tools/api.
Why "static" matters
The content is encoded directly into the QR matrix — there's no redirect link in the middle. Practically, that means:
- It never expires. The data lives in the image, not on my server.
- No scan tracking. There's nothing to track — the phone reads the data and acts on it.
- It can't be locked. Even if my site went down tomorrow, every code already printed keeps working.
This is the honest default a QR code should have, and it's the whole reason the project exists.
The stack
The entire thing runs on Cloudflare Pages + Pages Functions (free tier). A few decisions made that possible:
1. Matrix-only QR rendering. Most QR libraries pull in Node canvas/renderers that don't bundle for Workers. I use qrcode's core to compute just the module matrix, then render SVG/PNG myself. No native deps, so it bundles clean for the edge runtime.
2. A zero-dependency PNG encoder. For PNG output I hand-roll the encoder (matrix → pixels → PNG chunks) instead of pulling a library. It keeps the bundle tiny and stays well under the Workers CPU limit. (For very large sizes I recommend SVG — it's resolution-independent and cheaper to generate.)
3. Coalesced KV writes for the "codes created" counter. I show a live counter of codes generated. Naively that's one KV write per request — which would blow through the free tier's 1,000 writes/day instantly. Instead each isolate buffers increments in memory and flushes to KV at most once per minute, while reads add the un-flushed buffer so the number still moves in real time. It's best-effort/vanity, but it's honest-enough and free.
4. Per-IP rate limiting via a WAF rule. Pages Functions don't support the native Workers ratelimit binding, so abuse protection is a single free Cloudflare WAF Rate Limiting rule at the edge — no code, no cost.
On keeping it free
There are no ads on the API and no tracking anywhere. It's maintained by one person. If it's useful to you, there's a "buy me a coffee" link — that's the whole business model. I'd rather keep it genuinely free than bolt on the exact dark patterns that made me build this in the first place.
Try it
- Web generator (QR, Wi-Fi, barcode, logo): qrkit.tools
- API docs: qrkit.tools/api
- Also a Telegram bot: @QRKitToolsBot
I'd love feedback on the API design — parameters, formats, anything missing for your use case. What would make you actually reach for this over a client-side lib?
Top comments (0)