Google Form to PDF and Email: Auto-Generate and Send a PDF on Each Submission
"Google form to pdf and email" is an automation request, not a conversion request: every time someone submits, you want a PDF of that submission built and emailed — to the respondent as a receipt, to a teacher, or to an archive inbox. Google Forms can send a text confirmation and can collect email addresses, but it never attaches a PDF. This guide gives a complete installable Apps Script trigger that does it, plus the quotas, idempotency and privacy details that break naive implementations.
What Google Forms can and cannot do natively
| Native feature | What it actually does |
|---|---|
| Settings → Responses → "Send responders a copy" | Emails a plain-text/text summary of their answers; no PDF attachment |
| "Collect email addresses" | Stores the address in the response sheet; sends nothing on its own |
| Confirmation message | One line of text shown/emailed after submit; no file |
| Response receipt with a PDF | Not available — needs a script or an add-on |
So any "google form to pdf automation" is a Workflow: form submit → read the answers → render a document → attach and send. Once you see it that way, the implementation is small.
Three ways to build it (and which to pick)
| Approach | Setup | Cost / limits | Best for |
|---|---|---|---|
| No-code add-on (e.g. form-to-PDF mail merge tools) | Click through a wizard | Free tier caps, per-form pricing, data goes through a third party | One form, non-technical owner |
| Google Apps Script (below) | ~40 lines + one trigger | Free, runs in your account, Gmail daily quota applies | Schools, internal data, full control |
| Zapier / Make / Power Automate | Visual flow | Paid per task at volume | Cross-system workflows (CRM, Drive, Slack) |
The complete Apps Script
Open the form (More → Script editor) or a standalone Apps Script project bound to the same form. Paste the following, set FORM_ID and run install() once to authorise and create the installable trigger.
const FORM_ID = "YOUR_FORM_ID";
const RECIPIENT_MODE = "respondent"; // "respondent" | "fixed"
const FIXED_TO = "records@example.com";
const SENDER_NAME = "Exam Office";
function install() {
const form = FormApp.openById(FORM_ID);
const existing = ScriptApp.getProjectTriggers()
.filter(t => t.getHandlerFunction() === "onFormSubmit");
if (existing.length === 0) {
ScriptApp.newTrigger("onFormSubmit")
.forForm(form).onFormSubmit().create();
}
}
function esc(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&").replace(/</g, "<")
.replace(/>/g, ">").replace(/"/g, """);
}
function onFormSubmit(e) {
const res = e.response;
const items = res.getItemResponses();
const stamp = res.getTimestamp();
const id = res.getId();
// --- idempotency: never email the same response twice ---
const props = PropertiesService.getScriptProperties();
if (id && props.getProperty("sent_" + id)) return;
let rows = "";
let email = res.getRespondentEmail(); // requires collect-email or domain
items.forEach(function (it, i) {
const q = esc(it.getItem().getTitle());
const a = esc(it.getResponse());
rows += "<tr><th align=left>" + (i+1) + ". " + q +
"</th><td>" + a + "</td></tr>";
if (!email && /email/i.test(q)) { const m = String(it.getResponse()).match(/\S+@\S+/); if (m) email = m[0]; }
});
const html = "<h1>Submission receipt</h1>" +
"<p>Submitted: " + esc(stamp) + "</p>" +
"<table border=1 cellpadding=6 style=border-collapse:collapse;width:100%>" + rows +
"</table>";
const pdf = HtmlService.createHtmlOutput(html)
.setTitle("Submission").getAs("application/pdf")
.setName("submission-" + Utilities.formatDate(stamp, Session.getScriptTimeZone(), "yyyyMMdd-HHmm") + ".pdf");
const to = RECIPIENT_MODE === "fixed" ? FIXED_TO : email;
if (!to) { console.warn("No recipient for response " + id); return; }
GmailApp.sendEmail(to, "Your submission (PDF attached)",
"A PDF copy of your submission is attached.",
{ attachments: [pdf], name: SENDER_NAME });
if (id) props.setProperty("sent_" + id, "1");
}Three details that matter. First, esc() is not optional: a respondent typing <b> into a short-answer field would otherwise inject markup into your PDF. Second, the sent_ property guard prevents a double email when a trigger fires twice or you re-run it manually. Third, getRespondentEmail() only returns a value when "Collect email addresses" is enabled or the form is inside a Workspace domain; the fallback regex picks up an explicit email question.
Google Form to PDF and email in 6 steps
Quotas and limits you must plan for
- Gmail daily sending quota. Consumer accounts are limited to roughly 100 recipients/day, Google Workspace accounts around 1,500. A form that receives 500 submissions in a day will silently fail partway through on a free account — check
MailApp.getRemainingDailyQuota()before sending. - Attachment size. Gmail caps total attachments at about 25 MB. A text PDF is tiny; embedding respondent-uploaded images is not.
- Trigger execution time. About 6 minutes per execution on consumer accounts. Keep the handler small; do not loop over all historical responses inside it.
- Installable vs simple trigger. A simple
onFormSubmit()in a sheet-bound script cannot call GmailApp because it runs without authorisation. Use the installable trigger created byinstall(). - OAuth scopes. The script needs forms, Gmail send, and script-property scopes. Authorise once by running
install()manually in the editor. - Trigger duplication. Running
install()repeatedly creates multiple triggers and duplicate emails. The guard in the code checks for an existing handler before creating one.
Troubleshooting
- No email arrives. Check the Apps Script Executions log. Most cases are a missing recipient (collect-email off) or an unauthorised trigger.
- "Exception: Authorization is required". Run
install()once manually from the editor and accept the consent screen. - Emails arrive twice. Two installable triggers exist. Delete extras in the Triggers (clock) panel.
- The PDF is empty or malformed.
HtmlService.createHtmlOutput()needs a complete HTML string; check thatrowsis not empty and that long answers are escaped, not raw. - Special characters show as boxes. Set a UTF-8 meta tag in the HTML and use a font available to the renderer.
- It stopped after ~100 emails. Gmail daily quota exhausted. Switch to a Workspace account, batch to a single archive address, or queue with a time-driven trigger.
- Timestamp looks wrong. Use
Session.getScriptTimeZone()(as above) rather than assuming UTC.
Privacy and consent
A submission receipt often contains names, emails and free-text personal answers. Only send when the respondent has clearly opted in, keep the PDF to the minimum necessary fields, avoid BCC-ing a public group, and set a retention period for the archive inbox. In a school context, check your local data-protection rules before emailing student answers off-domain.
For a batch of existing responses rather than a per-submission trigger, use the batch generator in Google Form Responses to PDF.
Decide the recipient
The respondent, a fixed archive inbox, or a value from an email question.
Enable collect-email (if needed)
Settings → Responses → Collect email addresses; otherwise add an explicit email question.
Paste the script
Form → More → Script editor; set FORM_ID, RECIPIENT_MODE and SENDER_NAME.
Run install() once
Authorise the Gmail/Forms scopes and create the single installable trigger.
Submit a test response
Verify the PDF attachment, escaping of a typed tag, and the timestamp.
Monitor
Watch the Executions log and remaining daily quota after launch.
Frequently asked questions
Can a Google Form automatically email a PDF of each response?
Not natively. Forms can email a plain-text copy of answers and collect email addresses, but never attaches a PDF. You need an installable Apps Script trigger that renders the answers to PDF and calls GmailApp.sendEmail, or a third-party automation tool.
How do I automate Google Form to PDF and email with Apps Script?
Create an installable onFormSubmit trigger, read e.response.getItemResponses(), build an HTML table with escaped values, convert it with HtmlService.createHtmlOutput(...).getAs('application/pdf'), and send it as an attachment with GmailApp.sendEmail. Store response IDs in script properties to avoid duplicate sends.
Why does my script not send email?
The most common cause is a simple trigger instead of an installable one — simple triggers cannot call GmailApp. Run an install() function manually to authorise. Also check that a recipient exists: getRespondentEmail() is empty unless email collection is enabled.
Is there a limit to how many PDF emails I can send?
Yes. Gmail daily recipient quotas are roughly 100 for consumer accounts and about 1,500 for Workspace, and each execution has a time cap near 6 minutes. Check MailApp.getRemainingDailyQuota() and avoid looping over historical responses inside the trigger.
Do I need to escape the answers before generating the PDF?
Yes. Free-text answers are inserted into HTML; without escaping, a respondent typing or & changes the layout or breaks the document. Escape &, <, > and quotes before building the HTML string.
Google Form converters
ExamParser has a dedicated page for each export format. Word for an editable document, PDF for a paginated archive copy, CSV for raw data, Excel for a working spreadsheet, and Google Sheets for a live linked sheet.
Related guides
- Google Form Responses to PDF (batch archive)
- Google Form to PDF (all three PDF needs)
- Save Google Form as PDF (desktop & iPad)
- Google Form to Excel Automatically
- Link Google Form to Google Sheets
Start from clean, structured data
Automation is only as good as the data feeding it. Export the question structure and answers as structured, deterministic data — questions, options and answer key intact — then build any PDF or email workflow on top. No AI rewriting.
Open the Google Form to Word converter →