5 production patterns for personalized PDFs from spreadsheets
Anyone can generate 10 PDFs. Generating 10,000 reliably, repeatedly, without quality drift — that's a different problem. Here are 5 patterns from teams doing it at scale.
Pattern 1: One sheet, one source of truth
Don't spread data across multiple sheets ("clients" sheet + "invoices" sheet + "payments" sheet). At generation time, joining them is brittle.
Do have one master sheet where each row is one PDF. Use formulas to pull data from other sheets if needed. VLOOKUP + QUERY are your friends.
Pattern 2: Validation column
Before generating, validate each row. Add a column:
=IF(AND(B2<>"", C2<>"", D2>0), "✓", "missing fields")
Filter your sheet to only "✓" rows before generation. Catches typos and missing data before you spam 500 clients with broken invoices.
Pattern 3: Idempotent generation
Add a GeneratedAt column. Use a smart template that only generates new rows:
{% if GeneratedAt == "" %}
... full template content ...
{% endif %}
Wait, that doesn't work — the template still runs. Better pattern: filter your sheet to rows where GeneratedAt is empty, generate them, then fill in the timestamp. Next run only picks up new rows.
Pattern 4: Versioned templates
Save templates with version numbers in the name:
Invoice_v1.gdocInvoice_v2.gdoc(added legal disclaimer)Invoice_v3.gdoc(new VAT rate)
In your sheet, add a TemplateVersion column referencing which version each row was generated with. When auditors come asking, you can reproduce the exact PDF.
Pattern 5: Output naming convention
Don't just use {{Client}}.pdf — duplicates collide. Use a unique suffix:
{{Client}}_{{InvoiceNumber}}_{{Date}}.pdf
→ Acme_INV-1001_2026-05-30.pdf
→ Acme_INV-1002_2026-06-30.pdf
Or include the row number for absolute uniqueness:
{{Client}}_{{rowNumber}}.pdf
→ Acme_2.pdf
Bonus: error handling
For each row, Sheet to PDF returns success or failure individually. Capture failed rows in a separate sheet by copy-pasting the result list — rerun just those after fixing the data issue.
Scale checklist
- Sheet with >10k rows? Split into monthly sub-sheets for performance.
- Template >5 pages? Optimize images (Insert → Image → Compress).
- Generation takes >6 minutes? Split into 100-row batches.
- Need to send via email? Use Pro plan, watch the bounce rate.
- Want share links? Use Pro+, recipients don't need Google accounts.