Generate QR Codes in Python with the qrcode Library
A hands-on guide to creating, styling, and batch-generating QR codes in Python with the qrcode library — from a one-liner to production-ready output.
QR codes are everywhere — on packaging, restaurant tables, event tickets, and Wi-Fi cards. If you have ever wondered what those little black-and-white squares actually encode and how a scanner turns them back into text, the German explainer site Technik-Frage has a friendly, jargon-free walkthrough of the concept: Was ist ein QR-Code und wie funktioniert er?. It covers the data matrix, error correction, and why a code still scans even when part of it is dirty or covered. This article picks up where that conceptual overview leaves off and shows you how to generate your own QR codes in Python.
Installing the library
The de-facto standard for creating QR codes in Python is the aptly named qrcode package, which uses Pillow under the hood for image rendering:
pip install "qrcode[pil]"The [pil] extra pulls in Pillow so you can save real PNG files instead of ASCII art.
The one-liner
For the simplest case, the high-level API is a single call:
import qrcode
img = qrcode.make("https://www.pykit.org")
img.save("pykit.png")That is genuinely all you need for a scannable code. But the moment you want control over size, margins, or error correction, you will want the full builder API.
Taking control with the QRCode class
import qrcode
qr = qrcode.QRCode(
version=None, # let the library pick the smallest fitting size
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10, # pixels per module
border=4, # quiet zone (min 4 per the spec)
)
qr.add_data("https://www.pykit.org")
qr.make(fit=True)
img = qr.make_image(fill_color="#0b3d2e", back_color="white")
img.save("pykit_styled.png")Two parameters matter most. The version (1–40) controls how many modules the grid has, and therefore how much data fits. Leaving it as None with fit=True lets the library choose the smallest grid that holds your payload. The error correction level decides how much of the code can be damaged and still decode — exactly the redundancy that Technik-Frage describes in its overview. Level H recovers about 30 percent of the symbol, which is why it is the safe choice if you plan to drop a logo in the center.
Encoding more than URLs
A QR code is just a text container, and scanners recognise certain text formats and act on them. A few useful payload conventions:
- Wi-Fi:
WIFI:T:WPA;S:MyNetwork;P:supersecret;;joins a network on scan. - Contact (vCard): a full
BEGIN:VCARD ... END:VCARDblock adds a contact. - Email:
mailto:hello@example.com?subject=Hiopens a pre-filled draft.
From Python's point of view these are all just strings you pass to add_data — the magic is in the convention the scanner understands.
Batch generation
Where Python really earns its place is automation. Say you need a unique code for every row in a CSV of event tickets:
import csv, qrcode
from pathlib import Path
out = Path("tickets")
out.mkdir(exist_ok=True)
with open("tickets.csv", newline="") as f:
for row in csv.DictReader(f):
ticket_url = f"https://example.com/t/{row['id']}"
qrcode.make(ticket_url).save(out / f"{row['id']}.png")A few lines turn a spreadsheet into hundreds of ready-to-print codes — the kind of repetitive task scripting was made for.
Reading them back
Generating is only half the story. If you also need to decode QR codes from images or a webcam, we covered that in our companion piece on reading QR codes with OpenCV and pyzbar. Together, generation and decoding give you a complete round trip.
Wrapping up
The qrcode library scales smoothly from a one-line throwaway to styled, error-corrected, batch-produced output. If you want the conceptual foundation first — how the matrix and error correction actually work — start with Technik-Frage's clear explainer, Was ist ein QR-Code und wie funktioniert er?, then come back and generate a few of your own.