Get started

How to generate a PDF from Google Sheets (4 methods)

Export Google Sheets data to PDF automatically: built-in export, Apps Script automation, Zapier/Make no-code workflows, and API-based generation for production use.

benoitdedMarch 30, 20268 min read

Google Sheets is where a lot of data lives. Invoices, reports, inventory lists, expense summaries: if the data is already in a spreadsheet, the next question is usually "how do I get a clean PDF out of this?"

The answer depends on what you need:

MethodEffortAutomationDesign control
Built-in exportNoneManual onlyLimited
Apps ScriptLowFully automatedMedium
Zapier / MakeLowFully automatedMedium
PDF API (PDF4.dev)MediumFully automatedFull

Method 1: Built-in export (manual)

The simplest option. No code, no setup.

  1. Open your spreadsheet in Google Sheets
  2. Go to File → Download → PDF Document (.pdf)
  3. Adjust page size, margins, and range in the dialog
  4. Click Export

This works for one-off exports. The PDF looks like the spreadsheet — grid lines, cell formatting, whatever is visible on screen. You can't fully control headers, footers, or typography.

Limitations: Manual only, no automation, limited design control.


Method 2: Apps Script (automated export)

Apps Script runs JavaScript inside Google Workspace. It can export any sheet to PDF and email or save it automatically.

Basic export to email

Open your spreadsheet, then go to Extensions → Apps Script and paste:

function exportSheetAsPDF() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetId = ss.getActiveSheet().getSheetId();
  const ssId = ss.getId();
 
  // Build the export URL
  const url = `https://docs.google.com/spreadsheets/d/${ssId}/export` +
    `?format=pdf` +
    `&size=A4` +
    `&portrait=true` +
    `&fitw=true` +          // fit to page width
    `&gridlines=false` +    // hide grid lines
    `&printtitle=false` +
    `&sheetnames=false` +
    `&gid=${sheetId}`;
 
  const token = ScriptApp.getOAuthToken();
  const response = UrlFetchApp.fetch(url, {
    headers: { Authorization: `Bearer ${token}` }
  });
 
  const pdfBlob = response.getBlob().setName('report.pdf');
 
  // Email it
  GmailApp.sendEmail(
    '[email protected]',
    'Monthly report',
    'Your report is attached.',
    { attachments: [pdfBlob] }
  );
}

Save to Google Drive instead

Replace the GmailApp call with:

const folder = DriveApp.getFolderById('YOUR_FOLDER_ID');
folder.createFile(pdfBlob);

Export a specific range

Add the range parameter to the URL:

`&range=A1:F50`  // only rows 1-50, columns A-F

Schedule it automatically

In Apps Script, click Triggers (clock icon) → Add Trigger:

  • Function: exportSheetAsPDF
  • Event source: Time-driven
  • Type: Day timer, Week timer, or Month timer

This runs your export at whatever cadence you need — daily reports, weekly summaries, monthly invoices — without touching anything manually.

Key URL parameters

ParameterValuesWhat it does
formatpdfOutput format
sizeA4, letter, A3Page size
portraittrue / falseOrientation
fitwtrue / falseFit to page width
gridlinestrue / falseShow/hide grid lines
gidSheet ID (number)Which sheet to export
rangeA1:G30Export a specific range
top_margin0.5Top margin in inches

Method 3: Zapier or Make (no-code)

If your data flows through other tools — a CRM, a form, a database — Zapier and Make can bridge Google Sheets to a PDF generator without any code.

With Zapier

  1. Trigger: "New or Updated Row in Google Sheets"
  2. Action: Use the Webhooks by Zapier step to POST your row data to the PDF4.dev API
  3. Action: Save the PDF to Google Drive or email it

With Make (formerly Integromat)

Make has a visual canvas where you connect modules:

  1. Google Sheets module — watch for new rows
  2. HTTP module — POST to PDF4.dev API with the row data as JSON
  3. Google Drive module — save the returned PDF
  4. Gmail module — email it (optional)

This approach works well when the PDF is one step in a larger workflow — for example, when a form submission creates a row in Sheets and should immediately trigger a confirmation PDF.


Method 4: PDF API (for styled reports)

The built-in export and Apps Script give you a spreadsheet-looking PDF. If you need a branded report — custom fonts, logos, charts, non-grid layouts — the cleanest approach is:

  1. Fetch the sheet data via the Google Sheets API
  2. Inject it into an HTML template
  3. Render it with a PDF API

This is what PDF4.dev is built for.

Step 1: Fetch sheet data

// Using the Sheets API v4
const SHEET_ID = 'your_spreadsheet_id';
const RANGE = 'Sheet1!A1:E20';
const API_KEY = 'your_google_api_key';
 
const response = await fetch(
  `https://sheets.googleapis.com/v4/spreadsheets/${SHEET_ID}/values/${RANGE}?key=${API_KEY}`
);
const data = await response.json();
 
// data.values is a 2D array: [[header1, header2], [row1col1, row1col2], ...]
const [headers, ...rows] = data.values;
const items = rows.map(row =>
  Object.fromEntries(headers.map((h, i) => [h, row[i] ?? '']))
);

Step 2: Define an HTML template

Create a template in your PDF4.dev dashboard, or inline it:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: 'Inter', sans-serif; padding: 40px; color: #1a1a1a; }
    h1 { font-size: 24px; margin-bottom: 8px; }
    .meta { color: #666; font-size: 14px; margin-bottom: 32px; }
    table { width: 100%; border-collapse: collapse; }
    th { background: #f5f5f5; padding: 10px 12px; text-align: left; font-size: 13px; }
    td { padding: 10px 12px; border-bottom: 1px solid #eee; font-size: 14px; }
    .total { font-weight: 600; }
  </style>
</head>
<body>
  <h1>{{title}}</h1>
  <p class="meta">Generated {{date}}</p>
  <table>
    <thead>
      <tr>
        {{#each headers}}<th>{{this}}</th>{{/each}}
      </tr>
    </thead>
    <tbody>
      {{#each rows}}
      <tr>
        {{#each this}}<td>{{this}}</td>{{/each}}
      </tr>
      {{/each}}
    </tbody>
  </table>
</body>
</html>

Step 3: Render with PDF4.dev

const pdfResponse = await fetch('https://pdf4.dev/api/v1/generate', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PDF4DEV_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    html: template,
    data: {
      title: 'Q1 Sales Report',
      date: new Date().toLocaleDateString(),
      headers,
      rows: items.map(item => Object.values(item))
    },
    options: {
      format: 'A4',
      margin: { top: '20mm', bottom: '20mm', left: '20mm', right: '20mm' }
    }
  })
});
 
const pdfBuffer = await pdfResponse.arrayBuffer();
// Save to disk, upload to S3, email as attachment...

Full pipeline: Sheets → PDF → email (Node.js)

import { google } from 'googleapis';
import nodemailer from 'nodemailer';
 
async function generateReportFromSheets(spreadsheetId, range, recipientEmail) {
  // 1. Authenticate with Google
  const auth = new google.auth.GoogleAuth({
    keyFile: 'service-account.json',
    scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly']
  });
  const sheets = google.sheets({ version: 'v4', auth });
 
  // 2. Fetch sheet data
  const { data } = await sheets.spreadsheets.values.get({
    spreadsheetId,
    range
  });
  const [headers, ...rows] = data.values;
 
  // 3. Generate PDF
  const pdf = await fetch('https://pdf4.dev/api/v1/generate', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.PDF4DEV_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      html: buildReportTemplate(headers, rows),
      options: { format: 'A4' }
    })
  });
  const pdfBuffer = Buffer.from(await pdf.arrayBuffer());
 
  // 4. Email it
  const transporter = nodemailer.createTransport({ /* your SMTP config */ });
  await transporter.sendMail({
    from: '[email protected]',
    to: recipientEmail,
    subject: 'Weekly report',
    text: 'Your report is attached.',
    attachments: [{ filename: 'report.pdf', content: pdfBuffer }]
  });
}

Choosing the right method

SituationBest method
One-off export, no automation neededBuilt-in export
Scheduled email with no external toolsApps Script
Multi-step workflow with other appsZapier / Make
Branded reports with custom designPDF API (PDF4.dev)
High-volume generation (100s/day)PDF API (PDF4.dev)
Serverless / Next.js / Node.js appPDF API (PDF4.dev)

Apps Script is the right choice for most Google Workspace automation: it's free, runs inside Google, and the URL export gives you a decent-looking PDF fast. The API approach is worth the setup when design control matters — reports that go to customers, invoices that need a logo, documents that represent your brand.


Apps Script permissions

When you run the script for the first time, Google will ask you to authorize:

  • See and manage your spreadsheets — to read the sheet and generate the export URL
  • Send email as you — if you use GmailApp
  • See and manage files in Drive — if you save to Drive

These permissions stay within your Google account. No data leaves Google unless you explicitly call an external API.


Common issues

Export URL returns a login page instead of a PDF The Authorization: Bearer header with ScriptApp.getOAuthToken() is required. Without it, the export URL redirects to the sign-in page.

Large sheets produce partial PDFs The built-in export has a page limit. For very large sheets, use the range parameter to export in sections, or fetch the data and render it with a PDF API that handles pagination via HTML flow.

Charts are not included in the export Charts embedded in Google Sheets are sometimes excluded from the URL-based export. If you need charts in the PDF, screenshot the chart with the Sheets API or render a chart in your HTML template using a library like Chart.js.

Font doesn't match my brand The built-in export uses Google's default rendering. To use your own fonts, fetch the data and render it with an HTML template where you control the CSS.

Free tools mentioned:

Html To PdfTry it free

Start generating PDFs

Build PDF templates with a visual editor. Render them via API from any language in ~300ms.