Skip to content
// Sheets · Drive · Apps Script

Export a single sheet as a PDF in Google Sheets.

How to export one tab from a Google Sheets file as a PDF using Apps Script — building the /export URL with the sheet's gid and fetching it with your OAuth token.

I want to export only one specific tab from my Google Sheets file as a PDF, not the entire workbook.

The script

copy · paste · trigger
exportSheetAsPdf.gs
Apps Script
// Export the active sheet as a PDF and save it to Drive root
function exportActiveSheetAsPdf() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var ssId = ss.getId();
  var gid = sheet.getSheetId();

  var url = 'https://docs.google.com/spreadsheets/d/'
    + ssId
    + '/export?format=pdf&gid='
    + gid
    + '&single=true&portrait=true&fitw=true';

  var token = ScriptApp.getOAuthToken();
  var response = UrlFetchApp.fetch(url, {
    headers: { Authorization: 'Bearer ' + token }
  });

  var filename = sheet.getName() + '.pdf';
  DriveApp.createFile(response.getBlob().setName(filename));
  Logger.log('Saved: ' + filename);
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why there is no built-in single-tab export

SpreadsheetApp gives you getBlob() on a Spreadsheet object, but that blob is always the full workbook as a PDF. There is no getSheetBlob() or equivalent. The method Google's own UI uses is an HTTP export endpoint on docs.google.com, and you can call it yourself from Apps Script.

The key parameter is gid, which is the numeric sheet ID (not the tab name, not the index). You get it with sheet.getSheetId(). Pair that with single=true in the query string, and Google's export renderer treats only that tab as the document. Leave out single=true and you get the whole workbook even with a gid set — I've been burned by that more than once.

Building the URL and authenticating the request

The export URL shape is: /spreadsheets/d/{spreadsheetId}/export?format=pdf&gid={sheetId}&single=true. The remaining query params (portrait, fitw, size, gridlines, etc.) are optional print settings — the same ones the Sheets UI writes into the URL when you use File > Download.

Authentication is the part most examples get wrong. UrlFetchApp.fetch() does not automatically carry your session credentials. You need ScriptApp.getOAuthToken() and pass it as an Authorization: Bearer header. The script will prompt for the necessary scopes (https://www.googleapis.com/auth/spreadsheets and https://www.googleapis.com/auth/drive) the first time it runs. If you're deploying this as a bound script, those scopes are picked up automatically from the calls to SpreadsheetApp and DriveApp.

Saving to Drive and naming the file

UrlFetchApp.fetch() returns an HTTPResponse. Calling getBlob() on it gives you a Blob with a generic MIME type and a placeholder name. Call setName() before passing the blob to DriveApp.createFile(), otherwise the Drive file ends up named 'response' or something equally useless.

If you want the file in a specific folder rather than Drive root, replace DriveApp.createFile(...) with DriveApp.getFolderById('your-folder-id').createFile(...). The folder ID is the string after /folders/ in the folder's URL. For automated invoicing or reporting pipelines, I keep that ID in a named PropertiesService entry rather than hardcoding it — makes it easier to swap folders between dev and prod runs.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
The script saves a PDF but it includes all my sheets, not just one. What's wrong?
You're missing single=true in the export URL. Without it, gid controls which tab is shown first but the full workbook is still rendered. Add &single=true immediately after the gid parameter.
I get a 401 Unauthorized error when calling UrlFetchApp.fetch(). What's missing?
The Authorization header is missing or malformed. Confirm you're calling ScriptApp.getOAuthToken() inside the function at runtime, not at the top of the file as a global. The token is session-scoped and must be fetched fresh on each execution.
How do I export a sheet by name instead of by active tab?
Use ss.getSheetByName('YourTabName') instead of ss.getActiveSheet(), then call getSheetId() on the result. If getSheetByName() returns null the tab name doesn't match exactly — it's case-sensitive.
Can I email the PDF directly instead of saving it to Drive?
Yes. Skip DriveApp.createFile() and pass the blob straight to GmailApp.sendEmail('[email protected]', 'Subject', '', {attachments: [response.getBlob().setName(filename)]}). No Drive write required.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.