Most teams generating documents at scale are still calling an API once per record. A hundred contracts means a hundred requests, a hundred round trips, a hundred places for a script to fail silently at 2am. PDF4me's Generate Documents (Multiple) endpoint collapses that into one call: one template, one array of records, one response with every generated file inside it.
The endpoint
POST /api/v2/GenerateDocumentMultiple. Singular Document, plural nowhere in the path. Get that wrong (GenerateDocumentsMultiple reads more natural to type) and you get a 404, which is one of those small traps worth knowing about before you're debugging it at speed. The docs page states this outright as a common mistake, so it is not just a hunch.
You send a template and an array of data. The engine merges the template against every element in the array and hands back one output document per record, in a single response. That is the entire mental model: one template, many records in, many documents out, no loop required on your side.
Template input: three modes
The template side of the request supports three input modes. You can Base64-encode the template file directly into templateFileData, point at it with a public HTTPS URL, or, if you are working from an HTML template, Base64-encode the raw HTML string and set templateFileType to HTML. All three require templateFileName alongside them so the engine knows what it is parsing. templateFileType itself is limited to Docx, HTML, or PDF for this endpoint, narrower than Generate Document (Single), which also accepts Mail Merge and Google Docs templates. That is a deliberate trade, not an oversight: Multiple is built for volume, not for every template format Single supports.
Data input: three modes, and what does not carry over
The data side has its own three modes. documentDataText takes a JSON array or an XML block directly in the request body, for when your records are small enough to inline. documentDataFile takes either a Base64-encoded data file or a URL to one, for when the record set is large enough that inlining it would bloat the payload. You use one or the other, never both. documentDataType is set to Json or XML, exact casing, and that is the full extent of what this endpoint accepts on the data side. Single also accepts CSV; Multiple does not. If your source system exports CSV, you convert it to JSON or XML before this call, not after.
A minimal JSON array looks like this:
[
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" }
]
Each object becomes one merged document, matched against whatever placeholders the template defines for those field names.
Output types, and where they stop matching the template
outputType accepts PDF, Docx, or xlsx when your template is a Word document or a PDF, and HTML when your template is HTML. That xlsx option is worth pausing on, since it does not exist on the Single endpoint at all: it means you can take a Word or PDF template and mail-merge it out to a spreadsheet, one row of structured output per input record, a genuinely different use case from generating a batch of PDFs.
KeepPdfEditable is the one optional field worth knowing about, and it only does anything when outputType is PDF. Set it to keep the generated PDF's form fields and layers editable rather than flattened. Set outputType to Docx, xlsx, or HTML and the API just ignores it, which is a small thing to remember before you spend time debugging why a flag "is not working."
One structural rule that trips people up: you cannot mix templates in a single call. One call, one template, applied to every record in the array. If you need the same data set rendered against three different templates, that is three separate calls, not three items in one request.
A live-verified Python example
The endpoint's official Python sample reads a Word template and a JSON data file from disk, Base64-encodes the template, and posts both in one request:
import base64
import requests
def read_and_encode_file(file_path):
with open(file_path, 'rb') as file:
return base64.b64encode(file.read()).decode('utf-8')
def generate_documents_multiple(api_key, base_url, template_file_path, json_data_path):
template_base64 = read_and_encode_file(template_file_path)
with open(json_data_path, 'r', encoding='utf-8') as f:
json_data = f.read()
url = f"{base_url}api/v2/GenerateDocumentMultiple"
headers = {
'Authorization': f'Basic {api_key}',
'Content-Type': 'application/json'
}
payload = {
"templateFileType": "Docx",
"templateFileName": "sample.docx",
"templateFileData": template_base64,
"documentDataType": "Json",
"outputType": "Docx",
"documentDataText": json_data,
"async": True # not IsAsync, see the note below
}
return requests.post(url, headers=headers, json=payload)
That is trimmed down from the full sample, which also handles the 202-plus-polling path and decoding outputDocuments[].streamFile back into files on disk. The trim is structural only, every field name above is copied verbatim from the real sample, not invented for this article.
A field name worth double-checking: async, not IsAsync
The main API reference page documents the async flag as IsAsync in its body-fields table. The official Python sample above, and the field's own interactive API Tester page, both use async instead, lowercase, no Is prefix. Both were live-checked while writing this piece and both genuinely say async. If a request built strictly off the reference table's field name is not behaving the way you expect, this is worth checking first before assuming something else is wrong.
Handling the response, sync or async
On the synchronous path, a 200 typically returns JSON with an outputDocuments array, one entry per generated document, each with a fileName and a Base64 streamFile you decode locally to write the file to disk. Some response variants use fileContent, content, or data instead of streamFile as the field name, so check what actually came back rather than assuming. On the asynchronous path, a 202 returns a Location header you poll until the job finishes, which is what kicks in automatically once a batch gets large enough. PDF4me's own FAQ on the endpoint does not quote a fixed record-count ceiling for when that switch happens. If you are pushing genuinely large batches through this, the practical move is splitting into calls of a few hundred records each rather than testing exactly where the async threshold sits.
Where this actually gets used
The pattern shows up anywhere one document shape needs to go out to many recipients with different data in each copy: personalized contracts pulled from CRM records, invoices generated straight from order data, certificates with a name and a date changed per recipient, offer letters where only the candidate details differ, welcome packets for a batch of new hires onboarding on the same day. In every one of those cases the alternative is either a loop hitting Generate Document (Single) once per record, or a human doing a mail merge by hand and hoping nobody's name gets swapped with somebody else's data. Neither scales past a few dozen documents without becoming its own maintenance problem.
If you want to see the mechanics end to end without writing a line of code first, PDF4me's interactive API Tester for this endpoint lets you upload a template and a data array and watch the batch come back. If you are already living inside a no-code automation tool rather than writing REST calls by hand, the same batch-generation behavior is available as a native step: Power Automate, Make, Zapier, and n8n all expose this as its own action or node, template and data array in, one generated file per record out, without you touching templateFileData Base64 encoding by hand. Zapier's own version additionally accepts Mail Merge and Google Docs templates on top of Docx, HTML, and PDF, matching Single rather than the narrower REST list above, worth knowing if you are picking a template format inside a Zap specifically.
Before any of this, your app needs to authenticate against the API in the first place. That is covered in Connect to the PDF4me V2 API, which walks through the base URL, the Basic auth header, and the request and response format the whole V2 API shares.
What this isn't
It is worth being precise about what Generate Documents (Multiple) does not do, since the naming invites confusion. It does not accept CSV data the way Single does. It does not support Mail Merge or Google Docs template types at the raw REST level (Zapier's own action layers that support back in, see above). It does not bundle every output into one archive, it returns each document individually in the response array so your code decides what to do with each one. And it will not let you swap templates mid-batch. Those are not missing features so much as the shape of the trade PDF4me made building an endpoint specifically for one-template-many-records batch generation rather than trying to make Single do double duty.
If your real need is one document built from one record, with a template format wider than Docx, HTML, or PDF, Generate Document (Single) is the right endpoint instead. If your real need is a thousand personalized documents from one array in one call, this is what it is built for.
Website: pdf4me.com
Documentation: docs.pdf4me.com
Developer portal: dev.pdf4me.com
Top comments (0)