Published · September 24, 2026
Generate QR Codes in Your Own Code: Python and JavaScript, No API Needed
You don't need a hosted API to generate QR codes in your own project. Python's qrcode and segno libraries, and Nayuki's qrcodegen for JavaScript, encode the whole thing on-device — free, offline, and with the destination data never leaving your machine.
Quick answer
You don't need an API to generate a QR code from your own code. In Python, pip install qrcode[pil] or pip install segno gets you a working generator in one function call. In JavaScript, Nayuki's qrcodegen library runs the same encoding logic in the browser or in Node — no server round-trip, no rate limit, and nothing you're encoding ever leaves your machine. Below: a working example in each language, plus the gotchas that trip people up the first time.
Step-by-step: Python
Installing and generating with qrcode
The qrcode package is the default choice for most Python projects. Install it with the Pillow extra so it can actually render an image, not just data:
pip install qrcode[pil]
The one-liner version:
import qrcode
img = qrcode.make("https://example.com")
img.save("code.png")
Need control over size and error correction? Use the QRCode class directly:
import qrcode
qr = qrcode.QRCode(
version=1,
box_size=10,
border=4,
error_correction=qrcode.constants.ERROR_CORRECT_M,
)
qr.add_data("https://example.com")
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save("code.png")
version sets the physical size — 1 to 40, and how much data each version can hold depends on the error-correction level too. fit=True picks the smallest version that fits automatically, so you rarely set version by hand. error_correction accepts ERROR_CORRECT_L, _M, _Q, or _H — roughly 7%, 15%, 25%, and 30% of the code can be damaged and still scan, in that order. box_size is pixels per module; border is the quiet zone.
Zero-dependency alternative: segno
Not a fan of pulling in Pillow? segno has no third-party dependencies and supports more output formats — SVG/PNG data URIs, PDF, terminal ANSI art, LaTeX — plus Micro QR Codes and Structured Append, which qrcode doesn't have. The call is nearly identical:
import segno
qr = segno.make("https://example.com")
qr.save("code.svg")
Stick with qrcode if you're already using Pillow and just want a standard PNG. Go with segno instead for a smaller dependency tree or one of its extra formats.
Step-by-step: JavaScript
For the browser or Node, Nayuki's qrcodegen is a solid default. The same algorithm is ported faithfully across Java, TypeScript/JavaScript, Python, Rust, C++, and C, so output is identical regardless of which language wrote it — MIT-licensed, zero dependencies, roughly 1,000 lines. It ships as a single compiled JS file, no bundler required, and it's also on npm.
const QRC = qrcodegen.QrCode;
const qr = QRC.encodeText("https://example.com", QRC.Ecc.MEDIUM);
const svg = toSvgString(qr, 4); // 4-module quiet zone
Want more control? Build segments manually and pass them to encodeSegments, which also accepts explicit min/max version bounds and a mask override:
const segs = qrcodegen.QrSegment.makeSegments("Hello, world!");
const qr = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false);
// qr.getModule(x, y) is true (dark) or false (light) — paint each module
Everything here runs client-side, so it's also the pick when you want QR generation to keep working even if a user's connection drops — there's no request that can fail.
Common problems and fixes
The code came out bigger than I expected. Almost always the error-correction level, not a bug — higher error correction adds redundancy, which pushes the library to a higher version (more modules) for the same payload. See how error correction affects the code you generate. If size matters more than damage tolerance, drop from ERROR_CORRECT_H to _M or _L.
My code looks different from one generated on a website for the same data. Both are probably correct. qrcode and qrcodegen default to mask pattern 4, while the ISO/IEC 18004:2015 reference implementation uses mask 5 — see why the same data can render as a different-looking code. Any standard scanner reads either one fine.
Should I set the version manually? Usually not — let the library auto-fit (fit=True in qrcode, or an automatic version range in qrcodegen), and only pin a version for a hard size constraint, like a specific print dimension.
Doing this on QRDock specifically
Just need one QR code — a link, a Wi-Fi network, a vCard? Skip the library and use qrdock.app directly; it's faster than writing a script for a single code. The Python/JavaScript approach earns its keep once you're generating codes programmatically: hundreds of unique codes from a database, or building a batch out of a spreadsheet of SKUs or guest names.
Frequently Asked Questions
Do I need an API key to generate QR codes in Python or JavaScript?
No. Libraries like qrcode and segno in Python, and qrcodegen in JavaScript, encode the QR matrix entirely on your machine or in the browser — there's no network call, no rate limit, and no third party ever sees the data you're encoding.
Which Python library should I use: qrcode or segno?
Reach for qrcode if you already depend on Pillow and just need a PNG or SVG out the door — it's the more common default. Reach for segno if you want zero dependencies, need output formats like data URIs or terminal ANSI art, or need Micro QR / Structured Append support that qrcode doesn't have.
Can I generate a QR code in plain JavaScript without a build step?
Yes. Nayuki's qrcodegen compiles down to a single JavaScript file you can drop into a browser page or a Node script — no bundler required — and it's also published on npm if you do want it as a dependency.
Why does a locally generated QR code look different from one made on a website?
The underlying data can be identical while the mask pattern differs — qrcode and qrcodegen default to mask 4, while the ISO/IEC 18004:2015 reference implementation uses mask 5. Both are fully scannable; the visual pattern of black and white modules is just a different, equally valid encoding choice.