logo
APIAutomations

Create links and QR codes in bulk from a spreadsheet

One short link or dynamic QR code per spreadsheet row — CSV import, the bulk API endpoints, or a Make, Zapier or n8n scenario — without duplicates.

Avatar for undefined
CodeQR Team
Content Team

You have a sheet — products, tables, invoices, event seats — and want one short link or one dynamic QR code per row, with the short URL back in the sheet. This guide compares the three routes CodeQR offers, then walks through the two that run unattended: the bulk endpoints and a per-row automation.

Availability

  • Plan: every plan. Each created link or QR code counts against the workspace's monthly quota, and API calls count against the key's per-minute limit (Free 60, Starter 100, Pro 500, Business 1,000, Scale 10,000). Bulk imports of 50 rows or more in a short window need a verified workspace (a paid plan with a payment method, or an established history).
  • Where: CSV import in Links and QR Codes; POST https://api.codeqr.io/links/bulk and POST https://api.codeqr.io/qrcodes/bulk; Make, Zapier, n8n and IFTTT from SettingsIntegrations.

Before you start

  • Clean the sheet: one row per item, a column with the destination URL (starting with https://), and a column with a stable identifier (SKU, seat number, row id). Rows without a valid URL are rejected one by one — the rest still go through.
  • Decide the domain and whether you want a custom key per row (go.example.com/table-12) or a random one.
  • Add an empty column for the result (shortLink) and, if you use QR codes, one for the image URL.
  • Keys must be unique per domain: a second row with the same key fails with Duplicate key: this short link already exists. — that is what stops accidental re-runs from creating twins.

Pick a route

  • CSV import in the dashboard — best for a one-off batch, no key or tool needed. Upload the file, map columns, receive an e-mail when the queue finishes: How to Import Links from a CSV File and How to Import QR Codes from a CSV File.
  • Bulk endpoints — best when a script or backend owns the sheet: up to 100 items per request, one response listing what was created and what failed.
  • Per-row automation (Make, Zapier, n8n, IFTTT) — best when the sheet keeps growing and a person adds rows: a "new row" trigger runs a Create a Link or Create a QR Code step for each row and writes the result back.

Steps: the bulk endpoints

  1. Build an array of link objects, at most 100 per request. url is required; domain, key, externalId, tagIds, comments are optional and per item.
curl -X POST https://api.codeqr.io/links/bulk \
  -H "Authorization: Bearer codeqr_••••••••••••••••••••••••" \
  -H "Content-Type: application/json" \
  -d '[
    {"url":"https://example.com/products/1","domain":"go.example.com","key":"docs-bulk-1"},
    {"url":"https://example.com/products/2","domain":"go.example.com","key":"docs-bulk-2"},
    {"url":"https://example.com/products/3","domain":"go.example.com","key":"docs-summer"},
    {"url":"nope","domain":"go.example.com","key":"docs-bulk-4"}
  ]'
  1. Read the response. It is 200 even when some rows fail: a flat array in the same order, where each element is either the created link (with id and shortLink) or an object with your input under link plus error and code:
[
  { "id": "cmsxh5cex000iwu8tnt5u72o3", "domain": "go.example.com", "key": "docs-bulk-1", "shortLink": "https://go.example.com/docs-bulk-1", "url": "https://example.com/products/1", "clicks": 0 },
  { "id": "cmsxh5ci5000kwu8t6qn2pgam", "domain": "go.example.com", "key": "docs-bulk-2", "shortLink": "https://go.example.com/docs-bulk-2", "url": "https://example.com/products/2", "clicks": 0 },
  { "link": { "url": "https://example.com/products/3", "domain": "go.example.com", "key": "docs-summer" }, "error": "Duplicate key: this short link already exists.", "code": "conflict" },
  { "link": { "url": "nope", "domain": "go.example.com", "key": "docs-bulk-4" }, "error": "Invalid destination URL", "code": "unprocessable_entity" }
]

Write shortLink into the result column of the rows that have an id; log the error of the others next to their row and fix the sheet.

  1. QR codes use the same shape on POST /qrcodes/bulk — each item needs type (url for a link-style code) and url, plus optional title, colors and size:
curl -X POST https://api.codeqr.io/qrcodes/bulk \
  -H "Authorization: Bearer codeqr_••••••••••••••••••••••••" \
  -H "Content-Type: application/json" \
  -d '[{"type":"url","url":"https://example.com/table/1","title":"Table 1"}]'

The response is an array of QR code objects (id, domain, key, shortLink, title, image). Codes created in bulk have no stored thumbnail — image is null and stays so — but the picture is always available at https://api.codeqr.io/qr?url=<shortLink>?qr=1 (a 1024 px PNG). Field details are in Create QR codes with the API, MCP, and automations.

  1. Pace the requests. With 100 items per call, 1,000 rows is 10 calls — well inside any plan's per-minute limit. If you also create tags or read links back per row, count those calls too, and sleep to the next minute when you get 429.
  2. Make re-runs safe. Put your row id in externalId (or a deterministic key): the second run answers 409 for rows that already exist and creates only the new ones. Look a row up any time with GET /links/info?externalId=ext_<your id>.

Steps: one row, one link, in Make, Zapier or n8n

  1. Enable the integration in SettingsIntegrations (Make and Zapier authorize with one click; n8n uses an API key). Details per platform in Connect CodeQR to Zapier, Make, n8n and IFTTT.
  2. Start the scenario with your sheet's "new row" trigger (Google Sheets, Excel, Airtable, Notion — whatever holds the rows).
  3. Add the CodeQR step: Create a Link (map the URL column to URL, and optionally Key, Domain, External ID, Tag IDs, Title) or Create a QR Code (map URL; choose the QR type and colors once).
  4. Add an "update row" step that writes the returned short link (shortLink) — and for QR codes the image URL — into the result column.
  5. Run once with a single new row, check the link in Links or QR Codes, then turn the scenario on.

Verify it works

  • After a bulk request, GET /links/count grew by the number of created items, and the new rows show in Links with the keys you sent.
  • For an automation, add one row and watch the scenario history: one execution, one link, one written-back short URL. Two executions for one row means the trigger is firing on your own write-back — see Troubleshooting.
  • Open one short link: two to three minutes later it shows one click in Analytics.

Troubleshooting

Every row was created twice

The "update row" step changed the row that the "new row" trigger watches, so the scenario ran again. Write the result to a separate sheet or column the trigger ignores, or filter the trigger on "result column is empty". In n8n and Make, one item per input row is expected — count the input items before assuming duplication.

Some rows came back with conflict

That key already exists on that domain (or the externalId was used before). Either the row was processed in an earlier run — nothing to do — or two rows share the same key: make keys unique or let CodeQR generate them by omitting key.

Invalid destination URL

The cell does not contain a full URL. Add https:// and remove spaces or line breaks; the bulk endpoint validates each row independently, so the rest of the batch still went through.

The API says "Creating N links at once requires a verified workspace"

Batches of 50 rows or more within a short window are held for workspaces without a paid plan and payment method or an established history. The message tells you what unlocks it (upgrade with a payment method, or complete checkout), or send fewer than 50 at a time.

429 halfway through the batch

You hit the per-minute limit of the key (see Availability). Wait for the next minute and resend only the rows that have no id yet — with externalId on each row, resending everything is also safe.

The QR code images are null

Codes created through POST /qrcodes/bulk do not get a stored thumbnail. Use https://api.codeqr.io/qr?url=<shortLink>?qr=1 in your sheet, or create the codes one by one with POST /qrcodes when you need the image field filled.

Related articles