Skip to content

Recipe: event badges in bulk

Goal: turn the attendee list exported from your ticketing tool into one print-ready badge per person.

You’ll use: a badge template, a CSV file, and a Node.js script that calls the export API with limited concurrency.

  1. Create a template named Badge.

  2. Add a colored band at the top with the event logo, and a band at the bottom whose color reflects the ticket type (you can design one template per ticket type, see below).

  3. Add the variables, large and centered:

    Variable Required Default Style
    firstName ✅ Anton, 96 px
    lastName ✅ Open Sans, 48 px
    company Open Sans, 32 px, grey
    ticket Attendee Courier Prime, 28 px, bold
attendees.csv
firstName,lastName,company,ticket
Ada,Lovelace,Analytical Engines,Speaker
Grace,Hopper,US Navy,Attendee
Alan,Turing,,Staff

Column names match variable names exactly, so each row can be sent as-is.

  • Directorybadges/
    • attendees.csv
    • generate.mjs
    • Directoryout/
      • …
generate.mjs
import { readFile, writeFile, mkdir } from 'node:fs/promises';
const TEMPLATE_ID = process.env.BADGE_TEMPLATE_ID;
const API_KEY = process.env.IMPRIME_API_KEY;
const CONCURRENCY = 4;
// Minimal CSV parsing — use a real CSV library if fields may contain commas or quotes
const [header, ...lines] = (await readFile('attendees.csv', 'utf8')).trim().split(/\r?\n/);
const keys = header.split(',');
const attendees = lines.map((line) =>
Object.fromEntries(line.split(',').map((value, i) => [keys[i], value])),
);
await mkdir('out', { recursive: true });
async function renderBadge(attendee, index) {
const res = await fetch(`https://app.imprime.io/api/export/${TEMPLATE_ID}/pdf`, {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(attendee),
});
if (!res.ok) throw new Error(`${attendee.firstName} ${attendee.lastName}: ${await res.text()}`);
const file = `out/${String(index + 1).padStart(3, '0')}-${attendee.lastName}.pdf`;
await writeFile(file, Buffer.from(await res.arrayBuffer()));
console.log('✓', file);
}
// Simple worker pool: at most CONCURRENCY requests in flight
let next = 0;
const failures = [];
await Promise.all(
Array.from({ length: CONCURRENCY }, async () => {
while (next < attendees.length) {
const i = next++;
await renderBadge(attendees[i], i).catch((err) => failures.push(err.message));
}
}),
);
if (failures.length) {
console.error(`\n${failures.length} badge(s) failed:\n` + failures.join('\n'));
process.exit(1);
}
Terminal window
BADGE_TEMPLATE_ID=<templateId> IMPRIME_API_KEY=<key> node generate.mjs
  • One template per ticket type — map ticket to a template ID in the script ({ Speaker: '…', Staff: '…' }) to change colors per audience.
  • Print sheet — merge the generated PDFs with a tool like pdfunite or qpdf to send a single file to the printer.