mrkeyoor.com_
Sat 08 Aug 20:59 UTC
PyPIUtilsupdated 08 Aug 2026

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.

Verdict

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.

API stability5/5qrcode.make, the QRCode class and the image_factory contract have been unchanged since the 7.x line, and 8.0 was a Python support drop rather than an API break. The one behavioural change worth knowing is 8.0 restricting embedded images to ERROR_CORRECT_H, and 8.2 fixed typos in the StyledPilImage embedded image parameter names while keeping the misspelled ones working.
Docs3/5The README is the whole documentation and it does cover the real ground: parameters, all four error correction levels, every image factory, styled images, colour masks and the CLI. What it lacks is a hosted reference, any discussion of capacity limits per version and mode, and it still describes a standard install as including pypng even though packaging moved that to an extra.
Maintenance3/5Version 8.2 was released on 2025-05-01 and the repository was pushed on 2026-03-25, so it is alive but slow: three releases in the last two years, all small, with 53 open issues and pull requests and no GitHub releases published. For an encoder implementing a frozen specification that is a defensible pace, but there is nobody moving quickly if something breaks.
Ecosystem4/5Roughly 6,469,636 weekly downloads and 4,925 stars, and it is the package that Django and Flask tutorials, TOTP setup flows and admin tooling reach for by default. The extension points are internal (image factories, module drawers, colour masks) rather than a third-party plugin ecosystem, so almost everything you need is in the one package.

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
Skip it if

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.txt

Use --output rather than shell redirection on Windows, since PowerShell corrupts the binary stream. Factory names are svg, svg-path, svg-fragment and png.

Alternatives

PackageRegistryPick it when
segnoPyPIYou need Micro QR or rMQR, want zero dependencies for PNG and SVG output, or care about strict standards conformance
pyzbarPyPIThe job is reading codes out of images rather than generating them
python-barcodePyPIYou actually need 1D barcodes such as EAN, Code128 or UPC rather than QR