How to make one PDF per row from Google Sheets
Every row becomes its own PDF, named from a column — invoices, letters, statements, one per person.
Sheets exports to PDF happily — as one PDF of the whole sheet. Getting one document per row means either a Docs template driven by Apps Script, or an add-on. Here is the script route, which is what the forum answers describe.
A Docs template plus Apps Script
Two to three hours the first time, then minutes per run
- 1
Build the template in Google Docs
Write the document once, with placeholders where the data goes — {{Name}}, {{Amount}}, {{Date}}. Plain text markers are easier to replace reliably than anything fancier.
- 2
Open Apps Script from the Sheet
Extensions → Apps Script. The script is bound to the spreadsheet, so SpreadsheetApp.getActiveSheet() gives you the data without any connection setup.
- 3
Loop the rows and copy the template
For each row, use DriveApp.getFileById(templateId).makeCopy() to duplicate the Doc, open the copy with DocumentApp, and call body.replaceText('{{Name}}', value) for each placeholder.
- 4
Export each copy as PDF
Get the copy's blob with getAs('application/pdf'), write it into a Drive folder with folder.createFile(blob), and name it from whichever column should drive the filename.
- 5
Delete the intermediate Docs
Each PDF leaves behind the Doc copy it was made from. Without an explicit setTrashed(true), a run of 200 rows leaves 200 stray documents in your Drive.
Where it breaks
- The six-minute execution limit is the wall everyone hits. Each row costs several Drive round trips — copy, open, replace, export, write, delete — so scripts of this shape typically manage somewhere in the low hundreds of rows before timing out mid-run.
- It requires broad OAuth scopes. This script reads and writes your Drive, and the consent screen says so. That is a genuine amount of access to grant a snippet copied from a forum post.
- Drive creation quotas apply, and they are per-day. A large run that fails on quota looks like an unexplained error rather than a limit, which sends people debugging their code instead of waiting.
- Everything lands in Drive, not on your machine, so you finish by selecting hundreds of files in the Drive UI and downloading them as a zip — a step the tutorials do not count.
Or do it here, in two clicks more
This runs in your browser, so it reads a downloaded file rather than connecting to your Google account — no Drive access, no consent screen, no signup. The export is the only extra step:
- 1Choose File → Download → Comma Separated Values (.csv), or .xlsx if you would rather keep the formatting.
- 2Drop it below and write your template with the same {{Column}} placeholders you would have used in Docs.
- 3Run it. Every row becomes its own PDF, named from the column you pick, downloaded straight to your machine — no Drive access, no OAuth screen, no cleanup.
Runs entirely in your browser · No upload · No signup · Open the network tab and check
Questions
Is there an add-on that does this?
Several, and they work. What they need is Drive access and, above their free tiers, a subscription — most meter documents per month. They also process on their servers, which is a different privacy posture from a script you wrote yourself or a tool that runs in your browser.
Can I keep the Sheet as the source and refresh it later?
Not with the browser-based route — it works on the file you drop in, so a changed Sheet means exporting again. Two clicks each time. The Apps Script route is the one that stays connected to the live Sheet, and that is a real advantage of it if you run this daily.
What happens to rows with missing values?
In the script route, replaceText leaves the raw {{Placeholder}} visible in the PDF, which is how those documents reach customers with {{Amount}} printed on them. Check the output of any row where a column might be blank — that is worth doing whichever route you take.
Working in Excel instead?
Mail Merge to PDF covers the same job from an .xlsx or .csv file, with the Excel methods — Power Query, VBA and the rest — written out in full.