{"id":1942,"date":"2026-08-05T09:00:00","date_gmt":"2026-08-05T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1942"},"modified":"2026-03-07T03:37:50","modified_gmt":"2026-03-07T08:37:50","slug":"bulk-qr-code-generation","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/bulk-qr-code-generation\/","title":{"rendered":"Bulk QR Code Generation: Creating Codes at Scale"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When you need QR codes for 500 products, 100 event badges, or 50 store locations, generating them one at a time is not practical. Bulk QR code generation automates the process: define the data source, configure the QR code parameters, and generate all codes programmatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For short link APIs, see <a href=\"https:\/\/tolinku.com\/blog\/short-link-api\/\">short link APIs: programmatic link creation<\/a>. For dynamic QR codes, see <a href=\"https:\/\/tolinku.com\/blog\/dynamic-qr-codes\/\">dynamic QR codes: change destinations without reprinting<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/screenshot-routes-1772819856524.png\" alt=\"Tolinku dashboard showing route configuration for deep links\">\n<em>Tolinku route configuration with QR code generation for each deep link.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When You Need Bulk Generation<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Use Case<\/th>\n<th>Volume<\/th>\n<th>Data Source<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Product packaging<\/td>\n<td>Hundreds to thousands per SKU<\/td>\n<td>Product database<\/td>\n<\/tr>\n<tr>\n<td>Store locations<\/td>\n<td>Dozens to hundreds<\/td>\n<td>Location database<\/td>\n<\/tr>\n<tr>\n<td>Event badges<\/td>\n<td>Hundreds to thousands<\/td>\n<td>Registration list<\/td>\n<\/tr>\n<tr>\n<td>Inventory\/asset tags<\/td>\n<td>Thousands<\/td>\n<td>Asset management system<\/td>\n<\/tr>\n<tr>\n<td>Direct mail campaigns<\/td>\n<td>Thousands<\/td>\n<td>CRM\/mailing list<\/td>\n<\/tr>\n<tr>\n<td>Menu items<\/td>\n<td>Dozens<\/td>\n<td>Menu database<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Approach 1: Script-Based Generation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For one-off batch generation, use a script with a QR code library.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Node.js with <code>qrcode<\/code><\/h3>\n\n\n\n<pre><code class=\"language-bash\">npm install qrcode\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-typescript\">import QRCode from &#39;qrcode&#39;;\nimport { readFileSync, writeFileSync, mkdirSync } from &#39;fs&#39;;\n\ninterface Product {\n  sku: string;\n  name: string;\n  url: string;\n}\n\nasync function generateBatch(products: Product[], outputDir: string) {\n  mkdirSync(outputDir, { recursive: true });\n\n  for (const product of products) {\n    const filename = `${outputDir}\/${product.sku}.png`;\n\n    await QRCode.toFile(filename, product.url, {\n      errorCorrectionLevel: &#39;M&#39;,\n      width: 400,\n      margin: 4,\n      color: {\n        dark: &#39;#000000&#39;,\n        light: &#39;#FFFFFF&#39;\n      }\n    });\n\n    console.log(`Generated: ${filename}`);\n  }\n}\n\n\/\/ Example: generate from a JSON file\nconst products: Product[] = JSON.parse(\n  readFileSync(&#39;products.json&#39;, &#39;utf8&#39;)\n);\n\ngenerateBatch(products, &#39;.\/qr-codes&#39;);\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Python with <code>qrcode<\/code><\/h3>\n\n\n\n<pre><code class=\"language-bash\">pip install qrcode[pil]\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-python\">import qrcode\nimport json\nimport os\n\ndef generate_batch(products, output_dir):\n    os.makedirs(output_dir, exist_ok=True)\n\n    for product in products:\n        qr = qrcode.QRCode(\n            version=None,  # Auto-detect\n            error_correction=qrcode.constants.ERROR_CORRECT_M,\n            box_size=10,\n            border=4\n        )\n        qr.add_data(product[&#39;url&#39;])\n        qr.make(fit=True)\n\n        img = qr.make_image(fill_color=&#39;black&#39;, back_color=&#39;white&#39;)\n        filename = f&quot;{output_dir}\/{product[&#39;sku&#39;]}.png&quot;\n        img.save(filename)\n        print(f&quot;Generated: {filename}&quot;)\n\n# Load products\nwith open(&#39;products.json&#39;) as f:\n    products = json.load(f)\n\ngenerate_batch(products, &#39;.\/qr-codes&#39;)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Input Data Format<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prepare a CSV or JSON with the data for each QR code:<\/p>\n\n\n\n<pre><code class=\"language-json\">[\n  {\n    &quot;sku&quot;: &quot;PROD-001&quot;,\n    &quot;name&quot;: &quot;Widget A&quot;,\n    &quot;url&quot;: &quot;https:\/\/go.yourcompany.com\/products\/PROD-001?source=packaging&quot;\n  },\n  {\n    &quot;sku&quot;: &quot;PROD-002&quot;,\n    &quot;name&quot;: &quot;Widget B&quot;,\n    &quot;url&quot;: &quot;https:\/\/go.yourcompany.com\/products\/PROD-002?source=packaging&quot;\n  }\n]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or CSV:<\/p>\n\n\n\n<pre><code class=\"language-csv\">sku,name,url\nPROD-001,Widget A,https:\/\/go.yourcompany.com\/products\/PROD-001?source=packaging\nPROD-002,Widget B,https:\/\/go.yourcompany.com\/products\/PROD-002?source=packaging\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Approach 2: API-Based Generation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For ongoing QR code generation integrated into your workflow, use a QR code API or your link management platform&#39;s API.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a short link for each item via the API.<\/li>\n<li>Generate a QR code for each short link URL.<\/li>\n<li>Download or store the QR code images.<\/li>\n<\/ol>\n\n\n\n<pre><code class=\"language-typescript\">async function createLinkAndQR(item: Item): Promise&lt;QRResult&gt; {\n  \/\/ Step 1: Create a short link\n  const link = await createShortLink({\n    destination: `https:\/\/yoursite.com\/products\/${item.sku}`,\n    slug: item.sku.toLowerCase(),\n    tags: [&#39;packaging&#39;, item.category]\n  });\n\n  \/\/ Step 2: Generate QR code for the short link\n  const qrBuffer = await generateQRCode(link.shortUrl, {\n    size: 400,\n    errorCorrection: &#39;M&#39;\n  });\n\n  \/\/ Step 3: Save the QR code\n  writeFileSync(`.\/qr-codes\/${item.sku}.png`, qrBuffer);\n\n  return {\n    sku: item.sku,\n    shortUrl: link.shortUrl,\n    qrPath: `.\/qr-codes\/${item.sku}.png`\n  };\n}\n\n\/\/ Process all items\nconst results = [];\nfor (const item of items) {\n  const result = await createLinkAndQR(item);\n  results.push(result);\n}\n\n\/\/ Save manifest\nwriteFileSync(&#39;qr-manifest.json&#39;, JSON.stringify(results, null, 2));\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Approach 3: Spreadsheet-Based<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For marketing teams who prefer spreadsheets:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Prepare a spreadsheet with one row per QR code (columns: ID, URL, label).<\/li>\n<li>Use a Google Sheets add-on or Excel macro to generate QR codes.<\/li>\n<li>Export as a ZIP of images.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Google Sheets Formula (Simple)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Google Sheets can generate QR codes using the Google Charts API:<\/p>\n\n\n\n<pre><code>=IMAGE(&quot;https:\/\/chart.googleapis.com\/chart?chs=200x200&amp;cht=qr&amp;chl=&quot; &amp; ENCODEURL(B2))\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This generates a preview in the spreadsheet. For print-quality images, use a dedicated tool.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Output Formats<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Format<\/th>\n<th>Use Case<\/th>\n<th>File Size<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>PNG<\/td>\n<td>Print and digital; most compatible<\/td>\n<td>Medium<\/td>\n<\/tr>\n<tr>\n<td>SVG<\/td>\n<td>Print (vector, scales perfectly)<\/td>\n<td>Small<\/td>\n<\/tr>\n<tr>\n<td>PDF<\/td>\n<td>Print-ready with bleed marks<\/td>\n<td>Medium<\/td>\n<\/tr>\n<tr>\n<td>EPS<\/td>\n<td>Professional print workflows<\/td>\n<td>Medium<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For print, SVG or PDF is preferred because vector formats maintain sharp edges at any size.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Naming and Organization<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">File Naming<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use a consistent naming convention:<\/p>\n\n\n\n<pre><code>{sku}-qr.png          \u2192 PROD-001-qr.png\n{location-id}-qr.png  \u2192 store-downtown-qr.png\n{badge-id}-qr.png     \u2192 attendee-12345-qr.png\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Manifest File<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Generate a manifest that maps each QR code to its metadata:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;generated&quot;: &quot;2026-08-05T10:00:00Z&quot;,\n  &quot;count&quot;: 500,\n  &quot;codes&quot;: [\n    {\n      &quot;id&quot;: &quot;PROD-001&quot;,\n      &quot;file&quot;: &quot;PROD-001-qr.png&quot;,\n      &quot;url&quot;: &quot;https:\/\/go.yourcompany.com\/products\/PROD-001&quot;,\n      &quot;errorCorrection&quot;: &quot;M&quot;,\n      &quot;size&quot;: 400\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This manifest is useful for print production, audit trails, and regeneration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quality Assurance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Automated Scanning Tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After generating a batch, verify scannability programmatically:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">import { readQRCode } from &#39;qr-scanner-library&#39;;\n\nasync function verifyBatch(qrDir: string, manifest: Manifest) {\n  const failures = [];\n\n  for (const code of manifest.codes) {\n    const imagePath = `${qrDir}\/${code.file}`;\n    const decoded = await readQRCode(imagePath);\n\n    if (decoded !== code.url) {\n      failures.push({\n        id: code.id,\n        expected: code.url,\n        decoded: decoded || &#39;SCAN_FAILED&#39;\n      });\n    }\n  }\n\n  if (failures.length &gt; 0) {\n    console.error(`${failures.length} QR codes failed verification`);\n    console.error(JSON.stringify(failures, null, 2));\n    process.exit(1);\n  }\n\n  console.log(`All ${manifest.codes.length} QR codes verified`);\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Print Proofs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before a full print run:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Print 5-10 sample QR codes at actual size.<\/li>\n<li>Scan each with at least 3 different phones.<\/li>\n<li>Verify the destination URL is correct.<\/li>\n<li>Check contrast and quiet zone on the printed material.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Performance at Scale<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Volume<\/th>\n<th>Script Time (Node.js)<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>100<\/td>\n<td>~5 seconds<\/td>\n<td>Simple batch<\/td>\n<\/tr>\n<tr>\n<td>1,000<\/td>\n<td>~30 seconds<\/td>\n<td>Add parallel generation<\/td>\n<\/tr>\n<tr>\n<td>10,000<\/td>\n<td>~3 minutes<\/td>\n<td>Stream output, parallel processing<\/td>\n<\/tr>\n<tr>\n<td>100,000<\/td>\n<td>~30 minutes<\/td>\n<td>Consider worker threads<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For very large batches, use parallel generation:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">import { Worker } from &#39;worker_threads&#39;;\n\n\/\/ Split items into chunks and process in parallel\nconst chunkSize = 100;\nconst chunks = [];\nfor (let i = 0; i &lt; items.length; i += chunkSize) {\n  chunks.push(items.slice(i, i + chunkSize));\n}\n\nawait Promise.all(\n  chunks.map(chunk =&gt; generateInWorker(chunk))\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku for Bulk QR Codes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/short-links-qr\">Tolinku<\/a> supports <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/routes\/creating-routes\/\">route creation<\/a> via the <a href=\"https:\/\/tolinku.com\/docs\/developer\/api-reference\/deep-links\/\">API<\/a>, enabling programmatic link and <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/routes\/qr-codes\/\">QR code<\/a> generation at scale. Each route includes built-in analytics, so you can track scans per QR code without additional setup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For short link APIs, see <a href=\"https:\/\/tolinku.com\/blog\/short-link-api\/\">short link APIs: programmatic link creation<\/a>. For the complete guide, see <a href=\"https:\/\/tolinku.com\/blog\/qr-codes-short-links-mobile-apps\/\">QR codes and short links for mobile apps<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Generate hundreds of QR codes programmatically. Automate batch creation for products, locations, events, and marketing campaigns.<\/p>\n","protected":false},"author":2,"featured_media":1941,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Bulk QR Code Generation: Creating Codes at Scale","rank_math_description":"Generate hundreds of QR codes programmatically. Automate batch creation for products, locations, events, and marketing campaigns.","rank_math_focus_keyword":"bulk QR code generation","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-bulk-qr-code-generation.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-bulk-qr-code-generation.png","footnotes":""},"categories":[16],"tags":[62,165,612,141,265,29,162,603,48,49],"class_list":["post-1942","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-marketing","tag-api","tag-automation","tag-bulk-generation","tag-developer","tag-events","tag-mobile-marketing","tag-print-marketing","tag-product-packaging","tag-qr-codes","tag-short-links"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1942","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1942"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1942\/revisions"}],"predecessor-version":[{"id":2443,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1942\/revisions\/2443"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1941"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1942"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1942"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1942"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}