# HTML to PDF in Google Apps Script HTML to PDF in Google Apps Script with the native getAs() call or UrlFetchApp to a Chromium API. Save the PDF to Drive or send it with Gmail. Apps Script is the glue behind a lot of Google Workspace automation, like a Sheet of customers, a Drive folder of invoices or a Gmail notification. Turning HTML into a PDF is a common step, and there are two ways to do it. **Quick answer.** Send your HTML to the html-pdf.com API with one `UrlFetchApp.fetch` call and save the returned blob. Store your key as the script property `HTML_PDF_KEY` first. ```js const pdf = UrlFetchApp.fetch('https://html-pdf.com/v1/pdf', { method: 'post', contentType: 'application/json', headers: { Authorization: 'Bearer ' + PropertiesService.getScriptProperties().getProperty('HTML_PDF_KEY') }, payload: JSON.stringify({ html: '

Hello

' }) }).getBlob().setName('hello.pdf'); DriveApp.createFile(pdf); ``` ## The native way: getAs('application/pdf') This section shows the built-in converter and where it falls short. Apps Script can turn an HTML blob into a PDF without any external service. ```js function nativeHtmlToPdf() { const html = '

Invoice #042

Total: $120.00

'; const pdf = Utilities.newBlob(html, 'text/html', 'invoice.html') .getAs('application/pdf') .setName('invoice-042.pdf'); DriveApp.createFile(pdf); } ``` `HtmlService.createHtmlOutput(html).getBlob().getAs('application/pdf')` works the same way, and you can also fill a Google Docs template with `DocumentApp` and export the document as PDF. This is fine for plain documents, but the converter is not a browser. Here is what you lose. - **Limited CSS.** Flexbox, grid, web fonts, `@page` rules and many modern properties are ignored or rendered differently than in Chrome. - **No JavaScript.** Charts, client-side templating and anything rendered by a script never appear. - **Little control.** No reliable page size, margins, headers, footers or page numbers. - **The DocumentApp route** gives you Google Docs layout, not your HTML and CSS. If your HTML looks right in Chrome and you want the PDF to match, send it to a headless Chromium renderer instead. ## The API way: UrlFetchApp to a Chromium renderer This section gives you a reusable function that renders real Chrome output. html-pdf.com renders HTML or a public URL with headless Chromium and returns the PDF. From Apps Script, that is one `UrlFetchApp.fetch` call. ### 1. Store your API key in script properties Get a key at https://html-pdf.com/login (100 free credits, 1 credit = 1 PDF page). In the Apps Script editor go to **Project Settings → Script properties** and add `HTML_PDF_KEY`. Don't hard-code it in the script. ```js function getKey_() { return PropertiesService.getScriptProperties().getProperty('HTML_PDF_KEY'); } ``` ### 2. Render HTML to a PDF blob ```js function htmlToPdf(html, filename) { const res = UrlFetchApp.fetch('https://html-pdf.com/v1/pdf', { method: 'post', contentType: 'application/json', headers: { Authorization: 'Bearer ' + getKey_() }, payload: JSON.stringify({ html: html, format: 'A4', margin: '15mm', print_background: true, filename: filename }), muteHttpExceptions: true }); if (res.getResponseCode() !== 200) { const err = JSON.parse(res.getContentText()).error; throw new Error(err.code + ': ' + err.message); } return res.getBlob().setName(filename); } ``` `muteHttpExceptions: true` lets you read the JSON error body (`{"error": {"code", "message", "hint"}}`) instead of getting a generic exception. A `402 insufficient_credits` means it's time to top up. The response headers `X-Pages` and `X-Credits-Remaining` are available via `res.getHeaders()`. ### 3. Save to Drive or email it ```js function saveAndSend() { const pdf = htmlToPdf('

Hello from Apps Script

', 'hello.pdf'); // Save to a Drive folder DriveApp.getFolderById('YOUR_FOLDER_ID').createFile(pdf); // Or email it as an attachment GmailApp.sendEmail('client@example.com', 'Your document', 'PDF attached.', { attachments: [pdf] }); } ``` To render a live page instead of a string, send `{"url": "https://..."}` instead of `html`. The page must be publicly reachable. ## Example: one invoice PDF per row in Google Sheets This example turns a Sheet into a batch of invoice PDFs. Say your sheet has a header row and the columns `Number | Customer | Email | Amount | PDF`. The script builds an invoice for every row without a PDF link, saves it to Drive and writes the link back. ```js function escape_(s) { return String(s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); } function invoicesFromSheet() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Invoices'); const rows = sheet.getDataRange().getValues(); const folder = DriveApp.getFolderById('YOUR_FOLDER_ID'); for (let i = 1; i < rows.length; i++) { const [number, customer, email, amount, link] = rows[i]; if (!number || link) continue; const html = `

Invoice ${escape_(number)}

Your Company

Bill to: ${escape_(customer)}

Total: $${Number(amount).toFixed(2)}

`; const pdf = htmlToPdf(html, `invoice-${number}.pdf`); const file = folder.createFile(pdf); sheet.getRange(i + 1, 5).setValue(file.getUrl()); // GmailApp.sendEmail(email, `Invoice ${number}`, 'Attached.', { attachments: [pdf] }); } } ``` Run it from the editor, a custom menu, or a time-driven trigger. Apps Script has its own execution time and daily URL Fetch quotas, so process very large sheets in batches. For a fuller invoice layout (line items, repeated table headers, clean page breaks) see https://html-pdf.com/generate-invoice-pdf and the ready-made https://html-pdf.com/templates. ## Useful options These are the settings you will reach for most often. - `format`: A3 to A6, Letter, Legal, Tabloid. `landscape: true` for wide tables. - `header_html` / `footer_html` with `pageNumber` and `totalPages` for "Page 1 of 3". - `prefer_css_page_size: true` to let your CSS `@page` rule decide. - `wait_delay` or `wait_for_expression` when a URL draws charts with JavaScript. - `response: "url"` returns JSON with a download link valid for 24 hours, handy if you only need to put a link in a cell or email. Requests can be up to 20 MB and each render has 30 seconds. The full reference is at https://html-pdf.com/docs. If you prefer a no-code flow instead of a script, see https://html-pdf.com/guides/n8n. ## FAQ ### How do I convert HTML to PDF in Google Apps Script? For simple pages, use `Utilities.newBlob(html, 'text/html').getAs('application/pdf')`. For output that matches Chrome, POST the HTML to `https://html-pdf.com/v1/pdf` with `UrlFetchApp.fetch` and save the returned blob. ### Why does getAs('application/pdf') ignore my CSS? The built-in converter is not a browser. It skips flexbox, grid, web fonts, `@page` rules and JavaScript, so a headless Chromium renderer is the fix when layout matters. ### Is there a free way to try it? Yes. You get 100 free credits once, with no card, and 1 credit = 1 PDF page. After that, packs start at $10 for 1,000 credits, and purchased credits never expire. ### Can I save the PDF to Drive and email it? Yes. The API returns a blob, so pass it to `DriveApp.createFile` or attach it in `GmailApp.sendEmail`. The HTML is not stored, and `response: "url"` download links last 24 hours.