Python QR Code: Generate with Custom Size and Colors
python qrcode generate qr codes with custom size and colors: Learn how to generate QR codes in Python with custom size and colors using the qrcode library. Control dim...
python qrcode generate qr codes with custom size and colors requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Generating QR codes in Python with custom size and colors is a common requirement for ticketing systems, product labels, and authentication flows. The qrcode library, combined with Pillow, gives you direct control over the module size, quiet zone, and color scheme. This article shows you how to generate QR codes with custom size and colors using the qrcode library, from a minimal example to web-ready output.
Installing the qrcode Library
The qrcode library is not part of the standard library, so you need to install it. It depends on Pillow for image generation, so installing qrcode will pull in Pillow automatically in most environments.
pip install qrcode[pil]
The [pil] extra ensures Pillow is installed, which is required for creating image files. If you only need to generate QR codes as text or in a terminal, you can install without the extra, but for custom size and colors you need Pillow.
Generating a Basic QR Code
Start with a minimal example to understand the core API. The qrcode library provides a make() function that returns an image object directly, but for more control you use the QRCode class.
import qrcode qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data("https://example.com") qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save("basic_qr.png")
Here, version controls the size of the QR code matrix. Version 1 is the smallest (21×21 modules). error_correction determines how much data can be recovered if the code is damaged. box_size is the size of each module in pixels, and border is the width of the quiet zone in modules. The add_data() method takes the string to encode, and make(fit=True) automatically selects the smallest version that fits the data.
The make_image() method accepts fill_color and back_color to set the foreground and background colors. The default is black on white, but you can change these to any RGB value.
Controlling QR Code Size with box_size and border
The physical size of a QR code image is determined by three factors: the version (number of modules), box_size, and border. The total pixel width is (modules + 2 * border) * box_size. For a version 1 code, that's (21 + 2 * 4) * 10 = 290 pixels.
To generate a larger or smaller QR code, adjust box_size. For example, a box_size of 5 produces a 145-pixel image, while 20 produces 580 pixels. The border is the quiet zone, which is required for reliable scanning. The QR code specification recommends a minimum border of 4 modules, but you can increase it if your design needs more whitespace.
qr = qrcode.QRCode(box_size=15, border=6) qr.add_data("https://example.com") qr.make(fit=True) img = qr.make_image(fill_color="navy", back_color="lightyellow") img.save("custom_size.png")
If you need a QR code that fits a specific pixel dimension, you can calculate the required box_size by dividing the target width by (modules + 2 * border). Remember that the version depends on the data length and error correction level, so the module count may vary. Use qr.modules_count after make() to get the actual number of modules.
Customizing QR Code Colors
The fill_color and back_color parameters accept any color that Pillow supports, including named colors, hex strings, and RGB tuples. This gives you the flexibility to match brand guidelines or embed QR codes into colored backgrounds.
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M) qr.add_data("https://example.com") qr.make(fit=True) img = qr.make_image(fill_color="#e74c3c", back_color="#ecf0f1") img.save("branded_qr.png")
When choosing colors, ensure there is sufficient contrast between the foreground and background. QR code scanners rely on the difference between dark and light modules. A dark fill on a light background is the safest choice. Avoid using colors that are too similar, such as dark blue on black, because the scanner may not distinguish the modules.
You can also use RGB tuples for precise color values:
img = qr.make_image(fill_color=(41, 128, 185), back_color=(236, 240, 241))
Choosing the Right Error Correction Level
The error_correction parameter determines how much data can be recovered if the QR code is partially damaged or obscured. There are four levels:
| Level | Recovery Capacity | Typical Use |
|---|---|---|
| L | ~7% | High data density, clean printing |
| M | ~15% | Standard use, moderate damage resistance |
| Q | ~25% | Industrial or outdoor use |
| H | ~30% | Maximum robustness, small data |
Higher error correction levels increase the number of modules needed, which makes the QR code physically larger for the same data. If you are generating QR codes with custom size and colors, the error correction level affects the minimum size required for reliable scanning. For example, a version 1 code with level H can hold only 7 numeric characters, while level L can hold 17. If you need to encode more data, the library will automatically select a higher version when fit=True.
Use ERROR_CORRECT_M as a balanced default for most applications. For labels that may be scratched or partially covered, choose Q or H and accept the larger size.
Saving and Outputting QR Codes
The make_image() method returns a Pillow Image object, which you can save in any format Pillow supports. Common formats are PNG, JPEG, and SVG. SVG is vector-based and scales without losing quality, but not all scanners support it. PNG is the most reliable for raster output.
img.save("qr.png", format="PNG") img.save("qr.jpg", format="JPEG") img.save("qr.svg", format="SVG")
To use the QR code in a web application, you can write the image to an in-memory buffer and send it as an HTTP response.
from io import BytesIO buffer = BytesIO() img.save(buffer, format="PNG") buffer.seek(0)
The BytesIO object behaves like a file, so you can pass it to a web framework's response object.
Performance and Memory Considerations
Generating a single QR code is fast, but if you are creating many codes in a loop, the overhead of Pillow image creation and encoding becomes noticeable. The main cost is the pixel buffer and the PNG compression step. For batch generation, consider reusing the QRCode object when possible, though you must clear its data between uses.
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M) for url in urls: qr.clear() qr.add_data(url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") # save or process img
Memory usage scales with the image dimensions. A 500×500 pixel image uses about 1 MB in memory as an RGBA buffer. If you generate thousands of images, you may want to process and save them one at a time rather than holding them all in memory.
Common Pitfalls and How to Avoid Them
One common mistake is setting box_size too small. If each module is only 1 or 2 pixels, the QR code may be unreadable because the scanner cannot resolve individual modules. Always test the printed or displayed size.
Another issue is ignoring the quiet zone. If you trim the border to zero, scanners may fail to detect the code. Stick to at least 4 modules of border.
Color contrast is also critical. A QR code with a light fill on a dark background can sometimes work, but it's less reliable. If you must invert colors, test with multiple scanners.
Finally, be careful with data encoding. The add_data() method accepts strings, but if you pass non-ASCII characters, the library will use UTF-8 encoding by default. For binary data, use add_data(data, optimize=0) to avoid automatic encoding selection.
Integrating QR Code Generation into a Web Application
In a web application, you typically generate a QR code on demand and return it as an image response. Here is a minimal Flask example that generates a QR code with custom size and colors based on query parameters.
from flask import Flask, send_file, request import qrcode from io import BytesIO app = Flask(__name__) @app.route("/qr") def generate_qr(): data = request.args.get("data", "https://example.com") box_size = int(request.args.get("box_size", 10)) fill = request.args.get("fill", "black") back = request.args.get("back", "white") qr = qrcode.QRCode( version=None, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=box_size, border=4, ) qr.add_data(data) qr.make(fit=True) img = qr.make_image(fill_color=fill, back_color=back) buffer = BytesIO() img.save(buffer, format="PNG") buffer.seek(0) return send_file(buffer, mimetype="image/png")
This endpoint accepts data, box_size, fill, and back as query parameters. The version=None lets the library choose the smallest version that fits the data. The response is a PNG image with the specified dimensions and colors. You can extend this pattern to return SVG or to cache generated codes for repeated requests.
When deploying this in production, consider adding caching headers or a CDN to reduce repeated generation load. Also validate user input to prevent extremely large box_size values that could cause memory exhaustion.