Prerequisites & Installation
You need Python 3.9 or newer. Check your version first:
python --version
# Python 3.12.x β anything 3.9+ is fine
The main library is qrcode. The [pil] extra pulls in Pillow for PNG/JPEG image output β you almost always want this:
pip install "qrcode[pil]"
To also export as SVG (ideal for business card print files), add:
pip install "qrcode[pil]" svglib
Always install into a virtual environment: python -m venv venv && source venv/bin/activate (or venv\Scripts\activate on Windows). This keeps your global Python clean.
Step 1 β Your First QR Code (URL)
Before building the full business card version, let us make sure everything works with the simplest possible case β a URL QR code:
import qrcode img = qrcode.make("https://allinoneaicenter.com") img.save("basic_qr.png") print("β basic_qr.png saved")
Run it with python basic_qr.py and a 290 Γ 290 pixel PNG appears in your folder. Scan it β it opens your URL. That is the whole library in two lines.
qrcode.make() does
It is a convenience wrapper that uses default settings: error correction M (15% recovery), box size 10px, border 4 modules, black on white. For business cards we will override all of these.
Step 2 β Encode a vCard (the Business Card Standard)
A URL QR code only opens a webpage. A vCard QR code tells the phone to save a contact β name, title, email, phone, website and address all land directly in the address book. This is what makes a QR code genuinely useful on a business card.
The vCard 3.0 format is plain text with one field per line. Here is the exact format that works on both iOS and Android:
def build_vcard( name: str, title: str = "", email: str = "", url: str = "", phone: str = "", city: str = "", ) -> str: """Return a vCard 3.0 string ready to encode into a QR code.""" parts = name.strip().rsplit(" ", 1) last = parts[-1] if len(parts) > 1 else "" first = parts[0] if len(parts) > 1 else name lines = [ "BEGIN:VCARD", "VERSION:3.0", f"N:{last};{first};;;", f"FN:{name}", ] if title: lines.append(f"TITLE:{title}") if email: lines.append(f"EMAIL:{email}") if url: full_url = url if url.startswith("http") else f"https://{url}" lines.append(f"URL:{full_url}") if phone: lines.append(f"TEL:{phone}") if city: lines.append(f"ADR:;;{city};;;;") lines.append("END:VCARD") return "\n".join(lines) # ββ Your details βββββββββββββββββββββββββββββββββββββββββββββββ vcard = build_vcard( name = "Prabhu Kumar Dasari", title = "AI & XR Architect", email = "allinoneaicenter@gmail.com", url = "allinoneaicenter.com", phone = "+91 00000 00000", city = "Hyderabad, India", ) print(vcard)
Running the snippet prints something like this β the exact text that gets encoded into the QR code:
BEGIN:VCARD VERSION:3.0 N:Dasari;Prabhu Kumar;;; FN:Prabhu Kumar Dasari TITLE:AI & XR Architect EMAIL:allinoneaicenter@gmail.com URL:https://allinoneaicenter.com TEL:+91 00000 00000 ADR:;;Hyderabad, India;;;; END:VCARD
Some old vCard specs say \r\n but Android and iOS scanners both handle \n fine. Avoid Windows-style \r\n β it adds garbage characters that can corrupt the parsed contact.
Step 3 β Generate the QR Code from vCard
Now we feed the vCard string into the QR encoder. The key difference from Step 1 is that we use the QRCode class directly so we can control the error correction level:
import qrcode from qrcode.constants import ERROR_CORRECT_H qr = qrcode.QRCode( error_correction=ERROR_CORRECT_H, # H = 30% recovery β best for print box_size=10, # pixels per module (square dot) border=4, # quiet zone β 4 modules minimum (spec) ) qr.add_data(vcard) # vcard string from Step 2 qr.make(fit=True) # auto-select the smallest QR version that fits img = qr.make_image(fill_color="black", back_color="white") img.save("vcard_qr.png") print(f"β Saved {img.pixel_size}Γ{img.pixel_size}px β vcard_qr.png")
Why ERROR_CORRECT_H?
Business cards get folded, scratched and printed at small sizes. Level H recovers up to 30% of damaged data. Levels L/M/Q recover 7%/15%/25% respectively. The trade-off is a slightly denser (more modules) QR code β use a box_size of 10+ to keep it scannable.
Step 4 β Custom Colours & Size
The default black-on-white works fine, but matching your brand colours makes the card look intentional. Pass hex values to make_image():
from qrcode.image.styledpil import StyledPilImage from qrcode.image.styles.moduledrawers import RoundedModuleDrawer img = qr.make_image( image_factory=StyledPilImage, module_drawer=RoundedModuleDrawer(), # rounded dots β modern look fill_color="#1a2744", # dark navy β your brand colour back_color="white", ) img.save("branded_qr.png")
Available module drawers in qrcode.image.styles.moduledrawers:
| Drawer | Look | Best for |
|---|---|---|
SquareModuleDrawer | Default square dots | Minimal / technical |
RoundedModuleDrawer | Rounded square dots | Business cards β |
CircleModuleDrawer | Circular dots | Creative / playful |
GappedSquareModuleDrawer | Squares with gaps | Airy / open design |
HorizontalBarsDrawer | Horizontal bar style | Minimalist |
VerticalBarsDrawer | Vertical bar style | Minimalist |
Keep at least 4:1 contrast between fill and background. Light grey on white, or yellow on white, will cause scan failures. Dark colour on white or white on very dark background are the safest combinations.
Step 5 β Embed Your Logo in the Centre
This is the part most tutorials skip. A centred logo turns a generic QR into something people actually stop and scan. The trick is to paste the logo after generation β and to use ERROR_CORRECT_H from Step 3 so the covered modules are recoverable:
from PIL import Image # 1. Generate the base QR image (PIL Image object) qr_img = qr.make_image(fill_color="#1a2744", back_color="white").convert("RGBA") # 2. Open logo β PNG with transparency preferred logo = Image.open("logo.png").convert("RGBA") # 3. Resize logo to ~25% of QR width (safe for H-level error correction) qr_w, qr_h = qr_img.size logo_max = qr_w // 4 logo.thumbnail((logo_max, logo_max), Image.LANCZOS) # 4. Compute centred position logo_w, logo_h = logo.size pos = ((qr_w - logo_w) // 2, (qr_h - logo_h) // 2) # 5. Add a white circular background behind the logo (cleaner look) bg_size = logo_max + 20 logo_bg = Image.new("RGBA", (bg_size, bg_size), "white") bg_pos = ((qr_w - bg_size) // 2, (qr_h - bg_size) // 2) qr_img.paste(logo_bg, bg_pos) # 6. Paste the logo (use alpha channel as mask) qr_img.paste(logo, pos, mask=logo) # 7. Save qr_img.convert("RGB").save("logo_qr.png", dpi=(300, 300)) print("β logo_qr.png saved at 300 DPI")
Keep the logo under 30% of QR area
ERROR_CORRECT_H can recover up to 30% of the QR data. Covering more than that with a logo risks making the code unreadable. The 25% width rule above (β 6% of total area) gives comfortable headroom β the white background padding counts too.
Step 6 β Export as SVG (for Print)
PNG at 300 DPI is fine for most printers. But if your designer needs to scale the QR code up for a banner or poster, SVG is the right format β it is infinitely scalable with no pixelation. The qrcode library ships SVG factories out of the box:
import qrcode import qrcode.image.svg from qrcode.constants import ERROR_CORRECT_H qr = qrcode.QRCode(error_correction=ERROR_CORRECT_H, border=4) qr.add_data(vcard) qr.make(fit=True) # SvgFillImage β solid filled squares (best for dark-on-white print) svg_img = qr.make_image(image_factory=qrcode.image.svg.SvgFillImage) with open("vcard_qr.svg", "wb") as f: svg_img.save(f) print("β vcard_qr.svg saved β scalable for print")
| Factory | Output | Use when |
|---|---|---|
SvgImage | SVG with path outlines | Lightweight, minimal SVG |
SvgFillImage | SVG with filled rects | Print / design files β |
SvgPathImage | SVG single compound path | Illustrator / Figma import |
Complete Business Card Script
Here is the full, production-ready script that combines everything above. Edit the YOUR DETAILS section at the top and run it β it outputs both a PNG (for digital use) and an SVG (for print):
""" business_card_qr.py Generates a vCard QR code for a business card. Outputs: business_card_qr.png (300 DPI, with optional logo) business_card_qr.svg (scalable, for print) Requirements: pip install "qrcode[pil]" """ import qrcode import qrcode.image.svg from qrcode.constants import ERROR_CORRECT_H from qrcode.image.styledpil import StyledPilImage from qrcode.image.styles.moduledrawers import RoundedModuleDrawer from pathlib import Path from PIL import Image # ββββββββββββββββββββββββββββββββββββββββ # YOUR DETAILS β edit these # ββββββββββββββββββββββββββββββββββββββββ NAME = "Prabhu Kumar Dasari" TITLE = "AI & XR Developer" EMAIL = "allinoneaicenter@gmail.com" URL = "allinoneaicenter.com" PHONE = "+91 00000 00000" CITY = "Hyderabad, India" # Set to path of your logo PNG, or None to skip LOGO_PATH = None # e.g. Path("logo.png") # Brand colours FG_COLOUR = "#1a2744" # dark navy BG_COLOUR = "#ffffff" # white # ββββββββββββββββββββββββββββββββββββββββ # BUILD vCARD # ββββββββββββββββββββββββββββββββββββββββ def build_vcard(**kwargs) -> str: name = kwargs.get("name", "") parts = name.strip().rsplit(" ", 1) last = parts[-1] if len(parts) > 1 else "" first = parts[0] if len(parts) > 1 else name url = kwargs.get("url", "") lines = [ "BEGIN:VCARD", "VERSION:3.0", f"N:{last};{first};;;", f"FN:{name}", ] if kwargs.get("title"): lines.append(f"TITLE:{kwargs['title']}") if kwargs.get("email"): lines.append(f"EMAIL:{kwargs['email']}") if url: lines.append(f"URL:{url if url.startswith('http') else 'https://'+url}") if kwargs.get("phone"): lines.append(f"TEL:{kwargs['phone']}") if kwargs.get("city"): lines.append(f"ADR:;;{kwargs['city']};;;;") lines.append("END:VCARD") return "\n".join(lines) vcard = build_vcard( name=NAME, title=TITLE, email=EMAIL, url=URL, phone=PHONE, city=CITY ) # ββββββββββββββββββββββββββββββββββββββββ # GENERATE QR # ββββββββββββββββββββββββββββββββββββββββ qr = qrcode.QRCode( error_correction=ERROR_CORRECT_H, box_size=12, border=4, ) qr.add_data(vcard) qr.make(fit=True) # ββ PNG (styled) ββββββββββββββββββββββββββββββββββββββ png_img = qr.make_image( image_factory=StyledPilImage, module_drawer=RoundedModuleDrawer(), fill_color=FG_COLOUR, back_color=BG_COLOUR, ).convert("RGBA") # ββ Embed logo (optional) ββββββββββββββββββββββββββββ if LOGO_PATH and Path(LOGO_PATH).exists(): logo = Image.open(LOGO_PATH).convert("RGBA") qr_w, qr_h = png_img.size logo_max = qr_w // 4 logo.thumbnail((logo_max, logo_max), Image.LANCZOS) logo_w, logo_h = logo.size bg_pad = 16 bg = Image.new("RGBA", (logo_w+bg_pad, logo_h+bg_pad), "white") bg_x = (qr_w - bg.width) // 2 bg_y = (qr_h - bg.height) // 2 png_img.paste(bg, (bg_x, bg_y)) png_img.paste(logo, (bg_x + bg_pad//2, bg_y + bg_pad//2), mask=logo) png_img.convert("RGB").save("business_card_qr.png", dpi=(300, 300)) print("β business_card_qr.png") # ββ SVG (for print / designers) ββββββββββββββββββββββ svg_qr = qrcode.QRCode( error_correction=ERROR_CORRECT_H, border=4 ) svg_qr.add_data(vcard) svg_qr.make(fit=True) svg_img = svg_qr.make_image(image_factory=qrcode.image.svg.SvgFillImage) with open("business_card_qr.svg", "wb") as f: svg_img.save(f) print("β business_card_qr.svg") print("\nπ Done! Scan business_card_qr.png to verify.")
business_card_qr.png β a 300 DPI PNG with rounded dark-navy dots on white, optionally with your logo centred. Send this to a print shop as-is or drop it into your card design in Canva / Figma.
business_card_qr.svg β a scalable vector file. Import into Illustrator, Figma, or Inkscape for print-ready business card layouts.
Try the Live Generator (No Python Needed)
If you just want a QR code right now β without setting up Python β use the interactive business card generator I built directly on this site. Fill in your name, email, website and phone, and the QR updates live. You can download the PNG instantly.
Live QR Business Card Generator
Enter your contact details β QR code updates instantly β download PNG β free, no login
The live tool uses the same vCard 3.0 format and the same field structure as the Python script above, so the output is identical. The Python script is useful when you need to automate batch generation β for example, generating QR codes for every employee in a CSV, or regenerating your card programmatically whenever your details change.
Bonus β Batch Generation from a CSV
Here is how to extend the script to generate a QR code for every person in a spreadsheet. Useful for teams, events or conference badge printing:
""" batch_qr.py Reads contacts.csv and generates one QR PNG per row. CSV columns: name, title, email, url, phone, city """ import csv, qrcode from qrcode.constants import ERROR_CORRECT_H from pathlib import Path OUT_DIR = Path("qr_output") OUT_DIR.mkdir(exist_ok=True) with open("contacts.csv", newline="", encoding="utf-8") as f: reader = csv.DictReader(f) for i, row in enumerate(reader, start=1): vcard = build_vcard(**row) # reuse function from above qr = qrcode.QRCode(error_correction=ERROR_CORRECT_H, box_size=10, border=4) qr.add_data(vcard) qr.make(fit=True) img = qr.make_image(fill_color="#1a2744", back_color="white") fname = row["name"].lower().replace(" ", "-") + ".png" img.save(OUT_DIR / fname) print(f" [{i}] {fname}") print(f"\nπ All QR codes saved to ./{OUT_DIR}/")
Your contacts.csv should look like this:
name,title,email,url,phone,city Prabhu Kumar Dasari,Senior XR Developer,prabhu@example.com,allinoneaicenter.com,+91 00000 00000,Hyderabad Anjali Mehta,Product Manager,anjali@example.com,example.com,+91 11111 11111,Mumbai Rohit Sharma,DevOps Engineer,rohit@example.com,example.com,+91 22222 22222,Bengaluru
Frequently Asked Questions
Which Python library is best for generating QR codes?
The qrcode library (pip install qrcode[pil]) is the most widely used. It supports PNG, SVG, coloured QR codes, rounded modules, and logo embedding. For headless scripts or servers where Pillow is too heavy, segno is a lighter alternative.
What is a vCard QR code?
A vCard QR code encodes contact information in the vCard 3.0 standard β name, title, email, phone, URL, and address. When scanned by a smartphone, it prompts the user to save the contact directly to their address book. No app is required; it works in the default camera app on both iOS and Android.
What size should a QR code be on a printed business card?
A minimum of 2 cm Γ 2 cm (about 0.8 inches square) at the finished print size. Smaller than this and mid-range phone cameras will struggle. 2.5 cm is the safe sweet spot for a standard 3.5 Γ 2 inch business card. Generate at 300 DPI β the PNG script above does this automatically.
How do I add a logo to a QR code without breaking it?
Keep the logo under 25β30% of the QR code's width and use ERROR_CORRECT_H. The error correction level determines how much of the data can be obscured β Level H allows 30% recovery. Add a white padding rectangle behind the logo so the adjacent modules remain clearly readable.
Can I generate QR codes as SVG in Python?
Yes. The qrcode library includes SVG factories built-in β no extra install needed beyond qrcode[pil]. Use SvgFillImage for solid print-ready output or SvgPathImage if your designer is importing into Illustrator or Figma.
What error correction level should I use?
ERROR_CORRECT_H for business cards (30% recovery). Cards get handled, scuffed, and printed small. H-level keeps your code scannable even when partially damaged. The trade-off is a denser QR code β compensate with a larger print size or higher box_size value.