Auto Generate PDF from Google Sheets with Apps Script
Quick Answer: To create a PDF from Google Sheets once, use File > Download > PDF, or File > Print to pick the sheet, margins and orientation first. To generate it automatically, add an Apps Script function that converts the file with
getAs('application/pdf'), saves it to Drive or emails it, and schedule that function with a time-driven trigger such asonMonthDay(1).
Google Sheets can produce a PDF in two ways: by hand from the File menu, where you control page layout, or on a schedule with Apps Script, where a short function exports the spreadsheet and files or emails it. The scheduled route suits recurring reports, like a monthly expense summary built from that month's bank and card transactions.
Drop your bank statement here
PDF or OFX ยท Max 4.4 MB
Uploaded over an encrypted connection. We never ask for your bank login. Privacy
Preview free with no account. Free account to download.
Making a PDF by hand
Google's help article Create, view, or download a file gives the plain route: at the top, click File > Download and choose a file type, PDF included.
When the layout matters, start from print instead. Google's Print from Google Sheets article (read on 27 October 2026) lists the settings:
- Select the cells or sheet you want, if it's only part of the file.
- Click File > Print.
- Choose Current sheet, Workbook or Selected cells.
- Set margins (Normal, or Custom numbers), orientation (Landscape or Portrait) and paper size.
- Under Headers & footers, add page numbers or the date, and choose Repeat frozen rows so the header row appears on every page.
- Use Set custom page breaks if a table splits awkwardly, then Confirm breaks.
- Click Next. Google notes that Chrome opens a print window, while Firefox and Safari download a PDF file.
For a one-off report, that's all you need. Automation is worth it when the same report goes out on a schedule.
The automatic version, step by step
Open the spreadsheet, go to Extensions > Apps Script, and replace the sample code with this:
const FOLDER_ID = 'paste-your-drive-folder-id';
const RECIPIENT = 'you@example.com';
function exportReportPdf() {
SpreadsheetApp.flush();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const month = Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'yyyy-MM');
const pdf = ss.getAs('application/pdf').setName('Expense report ' + month + '.pdf');
DriveApp.getFolderById(FOLDER_ID).createFile(pdf);
MailApp.sendEmail(RECIPIENT, 'Expense report ' + month, 'The monthly report is attached.', {
attachments: [pdf],
});
}What each piece does, according to Google's reference pages:
SpreadsheetApp.flush()"applies all pending Spreadsheet changes," so values written earlier in the run are in place before export.getAsreturns the spreadsheet as a blob in the requested type; for most blobs, "'application/pdf' is the only valid option." It also adds the .pdf extension.DriveApp.getFolderById(...).createFile(blob)saves the PDF in a folder. The folder ID is the long string after/folders/in the folder's address.MailApp.sendEmail(recipient, subject, body, options)sends it, with the PDF in theattachmentsarray.
Click Run once. Google asks you to authorize access to Drive, Sheets and email; the trigger later runs under the account that approved it.
How do you schedule it to run every month?
Add a second function and run it once:
function createMonthlyTrigger() {
ScriptApp.newTrigger('exportReportPdf')
.timeBased()
.onMonthDay(1)
.atHour(7)
.inTimezone('America/Chicago')
.create();
}The ClockTriggerBuilder also offers everyDays, everyWeeks, onWeekDay and everyHours if monthly is the wrong rhythm. Two details from Google's installable triggers guide: time-driven triggers run "as frequently as every minute or as infrequently as once per month," and the time "might be slightly randomized," so a 7 AM trigger fires somewhere between 7 and 8. Running the setup function twice creates two triggers and two PDFs; open Triggers in the left bar of the script editor and delete extras. The same page has Add Trigger if you'd rather set the schedule by clicking than by code.
Exporting a single tab instead of the whole file
getAs converts the entire spreadsheet, not just the tab you are looking at. For a report that lives on one tab, copy it into a temporary spreadsheet and export that:
function exportOneTab(tabName) {
SpreadsheetApp.flush();
const source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabName);
const temp = SpreadsheetApp.create('tmp-' + tabName);
const copy = source.copyTo(temp);
// Replace formulas with values so cross-tab references don't break.
const range = source.getDataRange();
copy.getRange(1, 1, range.getNumRows(), range.getNumColumns()).setValues(range.getValues());
temp.deleteSheet(temp.getSheets()[0]);
SpreadsheetApp.flush();
const pdf = temp.getAs('application/pdf').setName(tabName + '.pdf');
DriveApp.getFileById(temp.getId()).setTrashed(true);
return pdf;
}The value-copy line matters. A summary tab usually totals another tab with SUMIFS, and once copied alone those references would point at nothing. Writing the values keeps the numbers while the copy keeps the formatting.
You'll also see scripts online that fetch a special export address with page-size, orientation and gridline options in the query string. That approach can give finer layout control, but Google's Apps Script reference doesn't document those parameters, so treat it as unsupported behavior that could change.
Why doesn't the automatic PDF match the print preview?
The print dialog and getAs are two separate paths. Settings you choose under File > Print, such as custom margins, landscape orientation or repeated header rows, apply to that print job; nothing in the getAs reference says the script reads them. Expect the scripted PDF to use a default page layout, and design the report tab so it reads well regardless:
- Keep the report narrow. Six to eight columns fit a portrait page; a twelve-month table usually needs landscape, which the script can't request.
- Put the title, period and totals in the first rows, so page one carries the answer even if the table runs on.
- Format numbers on the sheet itself (thousands separators, two decimals, negatives in brackets if your bookkeeper prefers them). The PDF shows what the cells display.
- Hide helper columns or move them to another tab before relying on the single-tab export, since everything on the copied tab comes across.
If the layout has to be exact every time, a reasonable compromise is to let the script build and email a reminder, then do the final File > Print yourself with the settings you want.
When the script fails
When triggered runs fail, Google's triggers guide shows Apps Script sending a "Summary of failures" email to the account that owns the trigger, with the error message. The usual causes are mundane:
- Authorization. If you add DriveApp or MailApp after the first run, run the function by hand again so Google can ask for the new permission; the trigger can't approve it for you.
- Wrong folder ID. Copy the ID from the folder's address again, without the
?usp=part some links carry. - Empty or stale figures. Data imported by another script or an IMPORTRANGE formula may not be ready at 7 AM. Move the trigger later, or call
SpreadsheetApp.flush()after any writes the export depends on. - Owner left the team. Triggers run as the person who created them. When that account loses access, create the trigger again from an account that will stay.
- Runtime. A single run is capped at six minutes, so build very large reports in a separate step instead of inside the export function.
What limits apply to a scheduled export?
Google's quotas page lists, for consumer (gmail.com) accounts:
| Limit | Consumer account |
|---|---|
| Email recipients per day (MailApp) | 100 |
| Triggers total runtime | 90 min per day |
| Script runtime | 6 min per execution |
| Email attachments | 250 per message, 25 MB total |
Workspace accounts get higher daily limits, and Google says all limits can change without notice. A monthly report to a few people is nowhere near them; a script mailing a PDF to each of 150 clients would hit the recipient cap.
A worked use case: the monthly expense report
A common setup is a transactions tab, a summary tab and the script above. Each month you add that month's rows: export a CSV from your bank where it offers one, or, if you only get PDF statements, use QuickBankConvert to turn them into rows you paste in; our page on PDF to Google Sheets shows the steps. The summary tab totals by category with SUMIFS, the trigger fires on the 1st, and the PDF lands in a shared folder and your bookkeeper's inbox.
If you haven't built the tracker yet, our expense tracker for Google Sheets guide covers the categories and formulas, and auto-importing bank transactions into a spreadsheet covers keeping the transactions tab current. Going the other direction, from a PDF into Sheets, is covered in importing a PDF into Google Sheets.
Related guides
Frequently Asked Questions
How do I save a Google Sheet as a PDF?
Open the spreadsheet, go to File > Download and choose PDF. To control what prints first, use File > Print, pick Current sheet, Workbook or Selected cells, set margins and orientation, then continue.
Can Google Sheets create a PDF automatically every month?
Yes, with Apps Script. A function converts the spreadsheet with getAs('application/pdf'), saves the file to Drive or emails it, and a time-driven trigger built with onMonthDay runs it on a set day.
Why does my automatic PDF include every tab?
Spreadsheet.getAs exports the whole spreadsheet. Copy the one tab you want into a temporary spreadsheet, export that, then move the temporary file to the trash.
Why did my scheduled export run at 9:40 instead of 9:00?
Google says time-driven triggers can be slightly randomized: a 9 AM trigger runs at a time between 9 and 10 AM and then keeps that timing from day to day.
How many PDFs can the script email per day?
Google's quota page lists 100 email recipients per day for consumer accounts using MailApp and higher limits for Google Workspace accounts. Limits can change without notice, so check the page.
Ready to convert your bank statement?
Free to preview. No bank login. Results in about a minute.
Convert a statement