Get your API key
How to generate PDFs from Airtable records

How to generate PDFs from Airtable records

Turn Airtable records into PDFs: a script via the Airtable API, an automation script action, or a no-code n8n flow, each rendering a template to PDF.

12 min read

Airtable stores the data, not the document. To turn an Airtable record into a PDF invoice, contract, or certificate, you read the record through the Airtable API, map its fields into an HTML template, and render that HTML to PDF. The fastest path for most teams is an Airtable automation with a Run script action that calls a hosted render API like PDF4.dev and writes the PDF URL back to the record, no server to host. If you already run a backend or need to batch a whole table on a schedule, a Node script using the official airtable SDK is the better fit.

This guide covers all three real paths: a Node script, an Airtable automation script action, and a no-code n8n flow. Each reads the same record and produces the same PDF.

Which method should you use to generate PDFs from Airtable?

Pick the method that matches where your logic already lives. A Node script suits teams with a backend and batch needs. An Airtable automation suits no-code teams who want generation to fire on a record change. n8n suits teams already running workflow automation. All three render the same template through the same API.

MethodWhere it runsBest forSetup effortTriggers on record change
Node script (airtable SDK)Your server or a cron jobBackends, batch over a whole tableMediumNo (you schedule or call it)
Airtable automation (Run script)Airtable sandboxNo-code teams, per-record on editLowYes (native automation trigger)
n8n flowSelf-hosted or n8n CloudTeams already on n8nLow to mediumYes (Airtable trigger node)

All three paths split the work the same way: Airtable holds the data, an HTML template holds the layout, and a render API turns filled HTML into a PDF. Only the glue code differs.

The rendering layer is the part Airtable cannot do. You either self-host headless Chromium (Playwright or Puppeteer) or call a hosted API. PDF4.dev is the hosted option in every example below: you POST HTML or a template_id plus a data object and get a PDF back, with {{variables}} filled by Handlebars server-side.

What do you need before you start?

You need three things: a personal access token, the base id, and the table name. Airtable deprecated API keys, so authentication is a personal access token (PAT) created at the Airtable developer hub. Scope the token to data.records:read (and data.records:write if you write the PDF URL back) on the specific base.

  • Personal access token: starts with pat, sent as Authorization: Bearer pat.... Treat it as a secret, never commit it.
  • Base id: starts with app, visible in the API docs for your base or in the URL when you open the base.
  • Table name or table id: the human name (for example Invoices) works, or the tbl... id.

The REST endpoint shape is https://api.airtable.com/v0/{baseId}/{tableName}. A single record is https://api.airtable.com/v0/{baseId}/{tableName}/{recordId}. The API returns 5 requests per second per base; over that you get HTTP 429 and must back off for 30 seconds.

Store both the Airtable PAT and your PDF4.dev key (p4_live_...) in environment variables or your platform's secret store. A token in source control is a token you have to rotate.

How do you generate a PDF from Airtable with a Node script?

Read the record with the official airtable npm SDK, map its fields into a Handlebars template, then POST that template plus the data to PDF4.dev. This path fits a backend or a scheduled job that processes one record or a whole view. Install the SDK with npm install airtable.

The flow is three steps: fetch, render, store. The example below reads one invoice record, sends its fields as the data object, and asks PDF4.dev for a signed URL with delivery: "url".

import Airtable from "airtable";
 
const base = new Airtable({ apiKey: process.env.AIRTABLE_PAT }).base(
  process.env.AIRTABLE_BASE_ID, // "app..."
);
 
// 1. Fetch one record from the Invoices table.
const record = await base("Invoices").find("rec0123456789ABCD");
const f = record.fields;
 
// 2. Map Airtable fields to template variables.
const data = {
  customer_name: f["Customer"],
  invoice_number: f["Invoice #"],
  issue_date: f["Issue date"],
  line_items: f["Line items"], // expects an array or JSON string
  total: f["Total"],
};
 
// 3. Render the template to a PDF and get a signed URL.
const res = await fetch("https://pdf4.dev/api/v1/render", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PDF4_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    template_id: "invoice", // a saved template, or send raw "html"
    data,
    delivery: "url",
  }),
});
 
const { url } = await res.json();
console.log("PDF ready at", url);

To batch a whole table, page through records with base("Invoices").select({ view: "To bill" }).eachPage() and throttle to stay under 5 requests per second. Render each record, then write the resulting URL back with base("Invoices").update(recordId, { "PDF": [{ url }] }).

Caveat: a self-hosted alternative (Puppeteer or Playwright) means you also own the Chromium binary, font installation, and serverless cold starts. On AWS Lambda a cold Chromium launch adds 2 to 5 seconds and the binary eats most of the 250 MB unzipped package limit. The hosted call above sidesteps both.

How do you generate a PDF inside an Airtable automation?

Add an automation with a Run script action so the PDF generates the moment a record meets your condition, with no server at all. The Airtable scripting sandbox supports fetch(), so the script reads the triggering record, calls the render API, and writes the PDF URL back to a field. This is the lowest-setup path for no-code teams.

In the automation editor, set a trigger (for example "When a record matches conditions" on a Status of Ready to bill), add an input variable that passes the record id, then add the Run script action with the code below.

// Input variables are configured in the automation UI.
const { recordId } = input.config();
 
const table = base.getTable("Invoices");
const record = await table.selectRecordAsync(recordId);
 
// Map Airtable cell values into the template data.
const data = {
  customer_name: record.getCellValueAsString("Customer"),
  invoice_number: record.getCellValueAsString("Invoice #"),
  issue_date: record.getCellValueAsString("Issue date"),
  total: record.getCellValueAsString("Total"),
};
 
// Call the render API. Use delivery "url" so Airtable can attach the file.
const res = await fetch("https://pdf4.dev/api/v1/render", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_PDF4_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ template_id: "invoice", data, delivery: "url" }),
});
 
const { url } = await res.json();
 
// Write the PDF back as an attachment on the same record.
await table.updateRecordAsync(recordId, {
  PDF: [{ url }],
});

When you set an attachment field to [{ url }], Airtable fetches the file from that URL and stores its own copy. The URL only needs to be reachable at the moment Airtable pulls it, which is why a signed, time-limited render URL works. PDF4.dev render URLs stay valid for 24 hours, well inside that window.

Use getCellValueAsString() for text and numbers, but read linked records and attachments with getCellValue() so you get the raw array. Map those to a {{#each}} block in your template for line items.

Caveat: the Airtable script sandbox has execution-time limits and cannot install npm packages, so keep the logic to fetch and write. Heavy formatting belongs in the HTML template, not the script.

How do you generate a PDF from Airtable with no-code (n8n)?

Chain an Airtable trigger node to an HTTP request that calls PDF4.dev, with zero code. n8n is an open-source workflow tool you self-host or run on n8n Cloud. The flow watches a base for new or updated records, maps fields to template variables, renders the PDF, then writes the URL back to Airtable.

A minimal n8n flow has four nodes:

  1. Airtable Trigger: polls the base and table, emits each new or changed record.
  2. Set (or Edit Fields): maps Airtable columns to template variable names like customer_name and total.
  3. HTTP Request: POST to https://pdf4.dev/api/v1/render with the Authorization: Bearer header and a JSON body of template_id, data, and delivery: "url".
  4. Airtable (Update record): writes the returned url into a URL or attachment field.

The HTTP Request node body looks like this, with n8n expressions pulling values from the trigger:

{
  "template_id": "invoice",
  "data": {
    "customer_name": "={{ $json.fields.Customer }}",
    "invoice_number": "={{ $json.fields['Invoice #'] }}",
    "total": "={{ $json.fields.Total }}"
  },
  "delivery": "url"
}

PDF4.dev ships an official n8n community node (n8n-nodes-pdf4) with a "Render From Template" operation, so you can skip the raw HTTP node and pick fields from a dropdown. Install it in n8n under Settings, Community Nodes. Make or Zapier follow the same shape: Airtable trigger, then an HTTP/render step, then an Airtable update. See automate PDFs with Zapier and Make for those two.

Caveat: the polling Airtable trigger checks on an interval (commonly every minute), so n8n is near-real-time, not instant. For instant firing, use the Airtable automation path above instead.

How do you call the PDF4.dev render API directly?

POST your HTML or a saved template_id with a data object to https://pdf4.dev/api/v1/render and you get a PDF back. The API renders HTML with headless Chromium server-side and fills {{variables}} with Handlebars, so there is no browser to install and no serverless cold start to manage. This is the same endpoint all three Airtable paths above call.

The smallest possible request sends raw HTML and asks for a URL:

curl -X POST https://pdf4.dev/api/v1/render \
  -H "Authorization: Bearer p4_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Hello {{customer_name}}</h1>",
    "data": { "customer_name": "Acme Corp" },
    "delivery": "url"
  }'

The delivery field controls the response shape. Omit it for a raw PDF binary body, set "base64" for an inline JSON payload, or set "url" for a signed link that Airtable can attach. For Airtable, "url" is the right choice because attachment fields take a URL, not bytes.

You can design and save the invoice template visually first, so the script only sends data. Want to test a template before wiring Airtable? Paste HTML into the free HTML to PDFTry it free tool and check the output, then move it into a saved template.

Which option should you choose?

Choose by where your automation logic should live, not by which is "best." All three render the same template through the same API; they differ only in trigger and hosting.

  • Use an Airtable automation Run script if you want zero servers and generation to fire the instant a record changes. Lowest setup, ideal for invoices and contracts created from a single base.
  • Use a Node script with the airtable SDK if you already run a backend, need to batch a whole view on a schedule, or want full control over retries and throttling against the 5 requests per second limit.
  • Use n8n (or Make / Zapier) if your team already runs a workflow automation tool and prefers a visual flow over code. Near-real-time via polling.
ScenarioRecommended path
Per-record PDF on edit, no backendAirtable automation Run script
Batch a whole table nightlyNode script + airtable SDK
Already using n8n / Make / ZapierNo-code flow with a render step
Need instant trigger, zero infraAirtable automation Run script
Complex multi-step logic + backendNode script

For more record-to-document patterns, see generate PDFs from Google Sheets and generate PDF invoices programmatically. The data source changes; the render call stays the same.

Wrapping up

Airtable does not export formatted document PDFs on its own, so you read the record through the API, map fields into an HTML template, and render with a service like PDF4.dev. The Run script automation path needs no server and fires on a record change, the Node script path handles batches and backends, and n8n covers no-code teams. Pick the trigger that fits your stack, keep the layout in the template, and write the signed PDF URL straight back into an Airtable attachment field.

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.