qrcode
A pure Python QR code generator. qrcode.make('text') gets you an image in one line; the QRCode class underneath exposes the parameters that matter (version, error correction level, box size, border) plus an image_factory hook that decides the output format. Factories ship for Pillow, for pure Python PNG via pypng, and for SVG in several shapes including a single combined path. A styled Pillow factory adds rounded or circular modules, gradient colour masks and a logo in the middle. It also installs a qr command line script and can print codes as ASCII straight to a terminal.
The default Python QR generator, and for one-off codes, terminal output or a styled logo code it is hard to beat on effort. Add the right extra at install time, and look at segno instead if you need Micro QR, strict standards coverage, or a dependency-free install.
Use it if
- You need to generate QR codes from Python and want the common case to be one function call
- You want SVG output without a rasteriser, which the SvgPathImage factory produces as a single path element
- You need styled codes: rounded or circular modules, gradient colour masks, or a logo embedded in the centre
- You want a code printed into a terminal or a text file, which print_ascii does with no image dependency at all
- You need to read QR codes. This library only writes them, and decoding needs something like pyzbar or an OpenCV detector
- You need other symbologies. Micro QR, rMQR, Aztec, DataMatrix and 1D barcodes are all out of scope; segno covers the QR family more completely
- You are generating at high volume in a request path. This is pure Python doing bit-level matrix work with mask pattern evaluation, so it is measurably slower than a C-backed encoder, and best_mask_pattern tries all eight patterns by default
- You want the base install to just work: pip install qrcode brings no image library at all, so the first qrcode.make() call raises ImportError('PyPNG library not installed.') until you add the pil or png extra
- You are relying on the styled output being scannable. The README warns that styled codes are not guaranteed to work with all readers, and since 8.0 an embedded image forces ERROR_CORRECT_H
- You need an actively developed dependency: 8.2 was released on 2025-05-01, the repository was last pushed on 2026-03-25, and the project publishes no GitHub releases, so there is no changelog feed to watch
Setup reality
pip install qrcode installs the encoder and nothing that can produce an image, which is the single most common first stumble: the package imports fine, then qrcode.make() raises ImportError because neither Pillow nor pypng is present. Pick your extra deliberately, either pip install 'qrcode[pil]' for Pillow or pip install 'qrcode[png]' for the pure Python PNG writer, or 'qrcode[all]' for both. The default factory is chosen at call time, PilImage when Pillow imports and PyPNGImage otherwise, so the same code can produce different objects on different machines and the fill_color and back_color arguments only apply to the Pillow path. version is an integer from 1 to 40 that fixes the matrix size, and leaving it None with make(fit=True) is almost always what you want; setting it too small raises DataOverflowError instead of growing. Error correction defaults to M, roughly 15 percent recovery, and raising it to H shrinks the payload capacity considerably. box_size is pixels per module and border is modules of quiet zone with a spec minimum of 4, so shrinking the border to save space is how codes stop scanning. add_data appends rather than replaces, so reusing a QRCode object for a second payload needs clear() first. Embedded logos require ERROR_CORRECT_H since 8.0, and even then you should scan the result on real phones before shipping. On Windows the colorama dependency is pulled in for the CLI, and piping qr output in PowerShell corrupts the binary, which is what the --output flag exists for.
Patterns
Make a code in one callgenerate-basic-qr
import qrcode
img = qrcode.make('https://example.com/invoice/9182')
img.save('invoice.png')Requires the pil or png extra. With a plain pip install qrcode this raises ImportError('PyPNG library not installed.') at the make() call, not at import.
Set version, error correction and sizingcontrol-code-parameters
qr = qrcode.QRCode(
version=None,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=10,
border=4,
)
qr.add_data('https://example.com/ticket/44')
qr.make(fit=True)
img = qr.make_image(fill_color='black', back_color='white')version=None with fit=True picks the smallest matrix that holds the data. Never drop border below 4: that quiet zone is part of the specification and scanners rely on it.
Encode a second payload with the same objectreuse-qrcode-object
qr = qrcode.QRCode()
qr.add_data('first payload')
first = qr.make_image()
qr.clear()
qr.add_data('second payload')
second = qr.make_image()add_data appends to whatever is already queued. Skipping clear() silently encodes both payloads into one code that scans as concatenated text.
Produce an SVG instead of a rasterrender-svg
import qrcode
import qrcode.image.svg
img = qrcode.make(
'https://example.com',
image_factory=qrcode.image.svg.SvgPathImage,
)
print(img.to_string(encoding='unicode'))SvgPathImage combines the modules into one path, which avoids the hairline gaps you get from the plain rect factory when zooming. SvgPathFillImage adds a white background.
Write a PNG with no Pillow dependencyrender-without-pillow
import qrcode
from qrcode.image.pure import PyPNGImage
img = qrcode.make('https://example.com', image_factory=PyPNGImage)
img.save('code.png')Needs the png extra for pypng. This factory ignores fill_color and back_color, so the output is always black on white.
Render a code as textprint-to-terminal
import io
import qrcode
qr = qrcode.QRCode()
qr.add_data('otpauth://totp/Example:user?secret=JBSWY3DPEHPK3PXP')
qr.make(fit=True)
buf = io.StringIO()
qr.print_ascii(out=buf)
print(buf.getvalue())print_ascii needs no image library at all, which makes it the safe choice for CLI enrollment flows. Use invert=True when the terminal has a light background.
Draw rounded or circular modulesstyle-modules
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data('https://example.com')
img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=RoundedModuleDrawer(),
)Styled codes are not guaranteed to scan everywhere. Test on several real phone cameras before printing anything, and keep the error correction level high.
Add a gradient colour maskapply-color-mask
from qrcode.image.styles.colormasks import RadialGradiantColorMask
img = qr.make_image(
image_factory=StyledPilImage,
color_mask=RadialGradiantColorMask(),
)Colour masks only exist on StyledPilImage, so Pillow is required. Low contrast between the light and dark ends is the usual reason a gradient code fails to scan.
Put an image in the centreembed-logo
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data('https://example.com')
img = qr.make_image(
image_factory=StyledPilImage,
embedded_image_path='logo.png',
)Since 8.0 an embedded image is only allowed at ERROR_CORRECT_H, because the logo destroys modules that error correction then has to recover.
Catch data that will not fithandle-capacity-overflow
from qrcode.exceptions import DataOverflowError
qr = qrcode.QRCode(version=4, error_correction=qrcode.constants.ERROR_CORRECT_H)
try:
qr.add_data(long_payload)
qr.make(fit=False)
except DataOverflowError:
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M)
qr.add_data(long_payload)
qr.make(fit=True)A fixed version with fit=False will not grow to fit. Raising error correction from M to H cuts usable capacity substantially, which is the usual cause of an unexpected overflow.
Get the module matrix for custom renderingread-raw-matrix
qr = qrcode.QRCode(border=4)
qr.add_data('https://example.com')
qr.make(fit=True)
matrix = qr.get_matrix() # list of rows of booleans, border included
print(len(matrix), len(matrix[0]))This is the escape hatch when you render into a PDF, a canvas or a label printer yourself. The returned matrix already includes the quiet zone from border.
Generate from the command lineuse-cli
qr --output=code.png "https://example.com"
qr --factory=svg-path "https://example.com" > code.svg
qr --ascii "https://example.com" > code.txtUse --output rather than shell redirection on Windows, since PowerShell corrupts the binary stream. Factory names are svg, svg-path, svg-fragment and png.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| segno | PyPI | You need Micro QR or rMQR, want zero dependencies for PNG and SVG output, or care about strict standards conformance |
| pyzbar | PyPI | The job is reading codes out of images rather than generating them |
| python-barcode | PyPI | You actually need 1D barcodes such as EAN, Code128 or UPC rather than QR |