How to Generate a QR Code with Python

Developer girl coding on Python
September 20, 2023
Written by
Reviewed by

Quick Response (QR) codes are two-dimensional barcodes that can store data, such as text or a web link. They are widely used for various applications like mobile payments, advertising, and product labeling.

In this tutorial, we will build a QR code generator using FastAPI, a modern, fast (high-performance) web framework for building APIs with Python. Our QR code generator will have the option to add a custom watermark to the QR code, and we will also add validation to ensure that a URL is provided in the request.

Prerequisites

Build the QR code generator app

First, let's create the core web app using FastAPI and the qrcode library. Start with creating a directory named python-qr-code, where you store your Python code.

mkdir python-qr-code

Then, create two additional folders in the new directory, named data and qrcodes.

  • data will hold custom watermarks that we will use later to create custom QR codes
  • qrcodes will hold the generated QR Codes

Let’s also change into the top-level project directory.

mkdir -p python-qr-code/{data,qrcodes}
cd python-qr-code

Now, create a virtual environment and activate it:

python3 -m venv .venv
source .venv/bin/activate

After that, install the required packages (fastapi, qrcode[pil], and uvicorn) using pip:

pip install fastapi uvicorn "qrcode[pil]"

Then, create a new Python file, main.py, and add the following code to it:

# Imports
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
from PIL import Image
import qrcode

app = FastAPI()

class Url(BaseModel):
    url: str = Field(..., example="https://example.com")

@app.post("/generate")
async def generate_qr_code(url: Url):
    try:
        img = qrcode.make(url.url)
        filename = "qrcode.png"
        img.save("./qrcodes/" + filename)
        return {"message": "QR code generated successfully!", "qrcode": filename}
    except Exception as e:
        raise HTTPException(status_code=500, detail="Internal Server Error")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

In the main.py file, we have:

  • Imported the required modules.
  • Created an instance of the FastAPI class.
  • Defined a URL model with a single field, url, of type str.
  • Created a route, /generate, that accepts a POST request with a JSON body containing a parameter named url.
  • The generate_qr_code() function creates a QR code from the provided URL and saves it in qrcodes directory locally, as an image file named qrcode.png. It then returns a JSON response with a success message and the filename of the QR code image.

Now, let’s run the application.

uvicorn main:app --reload

Terminal Output, when you run the command "uvicorn main:app --reload"

Your FastAPI application will be running on http://127.0.0.1:8000.

Test if it works

Use an API client like Postman or cURL (in a new tab or session) to send a POST request to http://127.0.0.1:8000/generate with a JSON body that includes the url field, such as in the cURL example, below.

bash
curl --request POST \
  --url http://127.0.0.1:8000/generate \
  --header 'Content-Type: application/json' \
  --data '{
        "url": "https://rishabkumar.com"
}'

 

The API will generate a QR code, similar to the one below, with the provided URL and save it as qrcode.png in the qrcodes directory.

 

The QR Code that the API generated for URL: https://twilio.com

Customize the QR Code

Now, let's modify our QR code generator to add a custom watermark to the QR code. We will also add validation to ensure that the url field is provided in the request.

Modify the Url model in main.py to include an optional watermark field by updating it to match the following code.

python
class Url(BaseModel):
    url: str = Field(..., example="https://example.com")
    watermark: Optional[str] = Field(None, example="path/to/watermark.png")

Then, modify the generate_qr_code() function in main.py to add the watermark to the QR code, by updating it to match the following code.

python
@app.post("/generate")
async def generate_qr_code(url: Url):
    try:
        img = qrcode.make(url.url)
        img = img.convert("RGB")
        if url.watermark:
            # Open the watermark image
            watermark = Image.open(url.watermark)
            # Get the size of the QR code image
            qr_width, qr_height = img.size
            # Resize the watermark to be smaller than the QR code image
            max_size = min(qr_width, qr_height) // 5
            watermark = watermark.resize((max_size, max_size))
            # Get the size of the resized watermark image
            watermark_width, watermark_height = watermark.size
            # Calculate the position to place the watermark at the center of the QR code
            position = ((qr_width - watermark_width) // 2, (qr_height - watermark_height) // 2)
            # Paste the watermark on the QR code image
            img.paste(watermark, position)
        
        filename = "qrcode-" + url.url.replace("/", "-").replace(":", "").replace(".", "-") + ".png"
        img.save("./qrcodes/" + filename)
        
        return {"message": "QR code generated successfully!", "qrcode": filename}
    except Exception as e:
        raise HTTPException(status_code=500, detail="Internal Server Error")

In this modified version of the generate_qr_code() function, we first create a QR code image from the provided URL. Then, if a watermark parameter is provided, we open the watermark image and we resize the watermark image to be 1/5th (one-fifth) the size of the smaller dimension of the QR code image, before pasting it onto the QR code image. This ensures that the watermark will always fit within the QR code image.

Also, the filename of the saved QR code image is modified to include the URL, by replacing the slashes and dots in the URL with dashes. This will save the QR code image with a filename like "qrcode-url-com.png".

Test the code changes

Now, let’s test our code changes. I have an image named twilio-tile-logo.png in the data directory that we will be using as the watermark, you can download it here or use your own watermark. This time I will use the Postman extension in Visual Studio Code, as in the screenshot below, to send a POST request to http://127.0.0.1:8000/generate with a JSON body that includes the url and watermark fields.

Visual Studio Code with Postman Extension: example of a POST request to the FastAPI endpoint to generate QR Code for a URL

If you want to use cURL, here is the equivalent command.

bash
curl --request POST \
  --url http://127.0.0.1:8000/generate \
  --header 'Content-Type: application/json' \
  --data '{
        "url": "https://twilio.com",
        "watermark": "./data/twilio-tile-logo.png"
}'

The API will generate a QR code for the provided URL, add the provided watermark to the center of the QR code, and save it with a filename that includes the URL.

QR Code generated by the FastAPI with a custom watermark in the middle for the provided URL: https://twilio.com

That's it! You have successfully created a QR code generator using FastAPI that can add a custom watermark to the QR code and validates that the url field is provided in the request.

If you are interested in how to do this in Go, check out “How to Generate a QR Code with Go

Rishab Kumar is a Developer Evangelist at Twilio and a cloud enthusiast. Get in touch with Rishab on Twitter @rishabk7 and follow his personal blog on cloud, DevOps, and DevRel adventures at blog.rishabkumar.com