🐍 PythonQR / Security
QR Code Validation in Python
Validate QR code content in Python — detect content types (URL, vCard, Wi-Fi credentials, email), block dangerous payloads such as javascript: URIs, and safely redirect users after scanning.
Also available in Node.js
Contents
1. QR content types
QR codes can encode many different data types. The IsValid API detects the type automatically and returns structured fields for each.
| type | Example content | Extra fields |
|---|---|---|
| url | https://example.com | url |
| vcard | BEGIN:VCARD…END:VCARD | name, email, phone |
| wifi | WIFI:S:MyNet;T:WPA;P:pass;; | ssid, security, password |
| mailto:user@example.com | email, subject | |
| phone | tel:+48123456789 | phone |
| sms | smsto:+48123456789:Hello | phone, message |
| text | Plain text content | — |
2. API response structure
Endpoint: GET /v0/qr-code?value=…
{ "valid": true, "type": "url", "content": "https://isvalid.dev/docs", "url": "https://isvalid.dev/docs" }
{ "valid": true, "type": "wifi", "content": "WIFI:S:OfficeNet;T:WPA2;P:hunter2;;", "ssid": "OfficeNet", "security": "WPA2" }
3. Safe URL redirect pattern
⚠️Never redirect directly to QR code content without validation. A QR code can encode
javascript:, data:, or other dangerous URI schemes.async def safe_redirect_url(raw_content: str) -> str: result = await iv.qr_code(raw_content) if not result.valid: raise ValueError("Invalid QR code") if result.type != "url": raise ValueError(f"Expected URL QR, got: {result.type}") url = result.url or result.content # Only allow HTTPS if not url.startswith("https://"): raise ValueError("Only HTTPS URLs permitted") # Block dangerous schemes blocked = ("data:", "javascript:", "vbscript:", "file:") if any(url.lower().startswith(b) for b in blocked): raise ValueError(f"Blocked URL scheme") return url # Safe to redirect
4. Blocking dangerous payloads
DANGEROUS_TYPES = {"text"} # Unstructured text — inspect before acting async def handle_qr_scan(content: str) -> dict: result = await iv.qr_code(content) if not result.valid: return {"action": "reject", "reason": "invalid_qr"} match result.type: case "url": url = result.url or result.content if not url.startswith("https://"): return {"action": "warn", "reason": "non_https_url", "url": url} return {"action": "redirect", "url": url} case "wifi": return {"action": "connect_wifi", "ssid": result.ssid} case "vcard": return {"action": "save_contact", "content": result.content} case _ if result.type in DANGEROUS_TYPES: return {"action": "display_only", "content": result.content[:500]} case _: return {"action": "display", "type": result.type, "content": result.content}
5. Batch validation with asyncio.gather
import asyncio from isvalid_sdk import IsValidConfig, create_client iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"])) async def validate_qr(content: str) -> dict: result = await iv.qr_code(content) if not result.valid: raise ValueError(f"Invalid QR code content: {content[:80]}") return result async def safe_redirect_url(raw_content: str) -> str: """Validate QR content and return a safe redirect URL.""" result = await validate_qr(raw_content) if result.type != "url": raise ValueError(f"Expected URL QR code, got: {result.type}") url = result.url or result.content if not url.startswith("https://"): raise ValueError("Only HTTPS URLs are allowed for redirects") # Block data: URIs and javascript: schemes blocked = ("data:", "javascript:", "vbscript:", "file:") if any(url.lower().startswith(b) for b in blocked): raise ValueError(f"Blocked URL scheme: {url[:40]}") return url # Batch scan — validate multiple QR codes from a bulk upload async def validate_batch(contents: list[str]) -> list[dict]: tasks = [iv.qr_code(c) for c in contents] results = await asyncio.gather(*tasks, return_exceptions=True) return [ { "content": c, "result": r if not isinstance(r, Exception) else None, "error": str(r) if isinstance(r, Exception) else None, } for c, r in zip(contents, results) ] # Example async def main(): url = await safe_redirect_url("https://isvalid.dev/docs") print(url) # "https://isvalid.dev/docs" asyncio.run(main())
6. Summary checklist
✓Always validate QR content before acting on it
✓Check result.type before routing (url / wifi / vcard)
✓Only allow HTTPS URLs for redirects
✓Block data:, javascript:, file: URI schemes
✓Do not auto-connect to Wi-Fi without user confirmation
✓Do not auto-save vCard without user confirmation
✓Use asyncio.gather for batch QR validation
✓Return 422 with reason on invalid QR input