# Create a PDF for each row in Google Sheets

Google Sheets to PDF, one document per row. Turn each row into an invoice, certificate or letter PDF with Apps Script, Zapier or n8n.

**Quick answer.** Each row in your Google Sheet holds the details for one document. A script or a Zapier step puts those details into a template, html-pdf.com turns it into a PDF, and the PDF is saved to Google Drive or emailed. The PDF link can be written back into the row.

## What you'll build

Say your sheet lists 40 workshop attendees with a name, email and date. Click one menu item and each person gets a certificate PDF by email, and a new column fills up with links to every file.

It works the same for monthly invoices to clients, offer letters, rent receipts or price quotes. If the information fits in a row, it can become a PDF.

## What you need

- **A Google Sheet** with a header row, for example `Name`, `Email`, `Course`, `Date`.
- **A design.** Pick the [certificate](https://html-pdf.com/templates/certificate), [invoice](https://html-pdf.com/templates/invoice) or [receipt](https://html-pdf.com/templates/receipt) template. The template is a web page design that we turn into a PDF, so you only change the words and colors.
- **An API key** from [html-pdf.com](https://html-pdf.com/login). You get 100 free pages once, with no card.
- **Optional.** Zapier or n8n if you want new rows handled automatically without a script.

## Option 1 with Apps Script (a menu button in your sheet)

1. In the sheet, open **Extensions → Apps Script**.
2. Paste the code below and save.
3. In **Project Settings → Script properties**, add `HTML_PDF_KEY` with your key.
4. Reload the sheet. A **PDF** menu appears. Choose **Create PDFs** and approve the permissions.

```js
function onOpen() {
  SpreadsheetApp.getUi().createMenu("PDF").addItem("Create PDFs", "createPdfs").addToUi();
}

function createPdfs() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const rows = sheet.getDataRange().getValues();
  const head = rows.shift();
  const col = name => head.indexOf(name);
  const key = PropertiesService.getScriptProperties().getProperty("HTML_PDF_KEY");
  if (col("PDF") === -1) sheet.getRange(1, head.length + 1).setValue("PDF");
  const pdfCol = (col("PDF") === -1 ? head.length : col("PDF")) + 1;

  rows.forEach((r, i) => {
    if (r[pdfCol - 1]) return; // already done
    const html = `<h1>Certificate</h1><h2>${r[col("Name")]}</h2><p>${r[col("Course")]}, ${r[col("Date")]}</p>`;
    const res = UrlFetchApp.fetch("https://html-pdf.com/v1/pdf", {
      method: "post",
      contentType: "application/json",
      headers: { Authorization: "Bearer " + key },
      payload: JSON.stringify({ html: html, landscape: true }),
    });
    const file = DriveApp.createFile(res.getBlob().setName(r[col("Name")] + ".pdf"));
    GmailApp.sendEmail(r[col("Email")], "Your certificate", "It's attached.", { attachments: [file.getBlob()] });
    sheet.getRange(i + 2, pdfCol).setValue(file.getUrl());
  });
}
```

Rows that already have a link are skipped, so you can run it again safely. Apps Script has daily limits on emails and run time, so split very large sheets into batches. More patterns are in the [Apps Script guide](https://html-pdf.com/google-apps-script-html-to-pdf).

## Option 2 with Zapier (each new row, automatically)

1. Create a Zap with **Google Sheets** and the event **New Spreadsheet Row**. Pick the spreadsheet and worksheet.
2. Add **Webhooks by Zapier → Custom Request**. Method `POST`, URL `https://html-pdf.com/v1/pdf`, headers `Authorization` (`Bearer hp_live_...`) and `Content-Type` (`application/json`).
3. Paste the JSON below in **Data** and map the row's columns into it.
4. Add **Google Drive → Upload File** with the `url` from step 2 in the **File** field, then **Google Sheets → Update Spreadsheet Row** to save the Drive link.

```json
{
  "html": "<h1>Certificate</h1><h2>Priya Shah</h2><p>Excel Basics, 10 Oct 2026</p>",
  "landscape": true,
  "filename": "priya-shah.pdf",
  "response": "url"
}
```

The link from html-pdf.com lasts 24 hours, which is why the Drive step keeps the permanent copy. See the [Zapier guide](https://html-pdf.com/guides/zapier). In n8n, use the **Google Sheets Trigger** node and an **HTTP Request** node, as in the [n8n guide](https://html-pdf.com/guides/n8n). Make users can follow the [Make guide](https://html-pdf.com/guides/make).

## Ask your AI agent

With the [html-pdf.com MCP server](https://html-pdf.com/pdf-mcp-server) in Claude or ChatGPT, paste a few rows and let the agent design the first PDF.

> Here are three rows from my sheet (name, course, date). Use html-pdf to make a landscape certificate PDF for the first row with a navy border and a signature line. Then give me the HTML so I can reuse it in Apps Script.

## Printing the whole sheet instead

If you just want the sheet itself as a PDF, use **File → Download → PDF** in Google Sheets. This guide is for when each row needs its own designed document. For a scheduled summary of the whole sheet, see [PDF report automation](https://html-pdf.com/pdf-report-automation).

## FAQ

### Can I convert Google Sheets rows to PDF for free?

Apps Script is free, and html-pdf.com gives you 100 free pages once with no card. After that, packs start at $10 for 1,000 pages.

### How do I stop duplicate PDFs?

Keep a PDF column. The script skips rows that already have a link, and in Zapier the trigger only fires for new rows.

### Can each PDF go to a different person?

Yes. Put each person's email address in a column and use it as the recipient, as the script above does.

### What if my sheet has thousands of rows?

Process them in batches to stay within Apps Script limits. Each one page PDF costs one credit.
