DEV Community

Cover image for I built a free QR code API with no API key — here's the stack (Cloudflare, zero-dep PNG)
Thong Dang
Thong Dang

Posted on

I built a free QR code API with no API key — here's the stack (Cloudflare, zero-dep PNG)

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" />
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

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)