# Drive2pdf API > Generate PDFs from Google Docs and Sheets templates via REST API. ## Endpoint POST https://api.drive2pdf.com/v1/pdf Headers: - `api_key: ` (required) - `Content-Type: application/json` (required) ## Setup 1. Create a Google Doc or Sheet template in Google Drive. 2. In the Drive2pdf dashboard, add the template and click **Deploy**. 3. Redeploy after any structural changes to the source file. 4. Copy the `template_id` from the dashboard (this is NOT the Google Drive file ID). ## Rules - `template_id` is always required. - For **Docs** templates: use `data` array. Do NOT send `operations`. - For **Sheets** templates: use `operations` array. Do NOT send `data`. - Never send both `data` and `operations` in the same request. - Templates cannot be created, modified, or deployed via API — only via the dashboard. - The API does not accept HTML, raw files, or file uploads. It only fills existing templates. - Unknown fields in the request body are silently ignored. - Missing required fields (`template_id`, `api_key`) return 400/401 errors. ## When to Use Docs vs Sheets - **Docs**: Letters, contracts, invoices, certificates — layout-driven documents with text placeholders, conditional sections, repeating tables, images, and rich text. - **Sheets**: Spreadsheet reports, financial statements, tabular exports — data-driven documents using cell coordinates, Named Ranges, formulas, and structural manipulation (insert/remove/hide rows/columns). --- ## Google Docs Templates Page size/orientation/margins are set in the Google Doc via File → Page setup. ### Data Types All data items go in a `data` array. Each item has `key`, `type`, and type-specific fields. #### type: "text" Replaces `{{key}}` in the template with a string. Use `\n` for line breaks. Key matching is **case-insensitive**: `{{Customer_Name}}` matches key `"customer_name"`. ```json { "key": "name", "value": "John Doe", "type": "text" } ``` #### type: "number" Same as text but accepts numeric values. ```json { "key": "total", "value": 299.99, "type": "number" } ``` #### type: "image" Replaces a placeholder image in the template. The image's **Alt Text Title** (set via right-click → Alt Text → Title) must match the `key`. Matching is **case-insensitive**. Image URLs must be **publicly accessible** (no auth, no short-lived URLs). ```json { "key": "logo", "value": "https://example.com/logo.png", "type": "image" } ``` Optional `link` makes the image clickable: ```json { "key": "logo", "value": "https://example.com/logo.png", "link": "https://example.com", "type": "image" } ``` #### type: "section" Show/hide content between `{{key}}` and `{{/key}}` tags. If omitted from data, section is **shown** by default. ```json { "key": "disclaimer", "show": false, "type": "section" } ``` #### type: "table" Repeats rows containing `{{key.field}}` placeholders for each object in the array. Only one object key per table row — do NOT mix e.g. `{{item.name}}` and `{{member.role}}` in the same table. Supports text, image, richtext, and links inside cells. Does NOT support nested tables or conditional sections inside cells. ```json { "key": "item", "type": "table", "value": [ { "description": "Web Dev", "qty": "40", "rate": "$150", "amount": "$6,000" }, { "description": "Design", "qty": "20", "rate": "$125", "amount": "$2,500" } ] } ``` Static rows (without dotted placeholders) are preserved as headers/footers. Row styling is inherited. #### type: "richtext" Replaces `{{key}}` with styled text segments. ```json { "key": "status", "type": "richtext", "value": [ { "text": "Status: " }, { "text": "APPROVED", "bold": true, "color": "#22c55e", "highlight": "#dcfce7" } ] } ``` Segment properties: `text` (string), `bold` (bool), `italic` (bool), `underline` (bool), `strikethrough` (bool), `color` (hex), `highlight` (hex), `size` (number, pt), `url` (string, makes text a hyperlink). Rich text also works inside table rows — use an array instead of a string for the field value. #### type: "link" Replaces `{{key}}` with a clickable hyperlink. ```json { "key": "website", "type": "link", "value": "https://example.com", "caption": "Visit us", "color": "#0055FF", "underline": true } ``` `caption` (optional): display text, defaults to URL. `color` and `underline` are optional. --- ## Google Sheets Templates Sheets use cell coordinates, Named Ranges, and structural commands — not `{{placeholder}}` syntax. All sheet operations go in an `operations` array. Operations execute **in the exact order provided**. You must manage index shifts when inserting/removing rows/columns. Tip: work bottom-to-top to avoid shifting issues. ### Operation: "data" Insert values into cells or Named Ranges. ```json { "type": "data", "target": "B2", "content": [["Invoice #001"]] } ``` - `target`: Named Range name or A1 notation (e.g. `"B2"`, `"C10"`). - `content`: 2D array of values. - `repeat` (bool, requires Named Range): duplicate the range's rows for each content row, pushing existing content down. Required to preserve row heights/styles. - `gid` (number): sheet tab ID, optional if using Named Range. ### Operation: "visibility" Hide or show rows/columns. ```json { "type": "visibility", "dimension": "row", "start": 10, "quantity": 5, "hide": true, "gid": 0 } ``` - `dimension`: `"row"` | `"rows"` | `"column"` | `"columns"` - `start`: 1-based index - `end` OR `quantity`: range end (1-based) or count - `hide`: true to hide, false to show - `gid`: sheet tab ID (default: 0) ### Operation: "insert" Insert rows or columns. ```json { "type": "insert", "dimension": "row", "start": 20, "quantity": 2, "inherit_from": "after", "gid": 0 } ``` - `inherit_from`: `"before"` (default) | `"after"` | `"none"`. Inherits values, formulas, formatting, merges. Formulas auto-adjust references. ### Operation: "remove" Remove rows or columns. ```json { "type": "remove", "dimension": "col", "start": 5, "quantity": 1, "gid": 0 } ``` ### Export Options (Sheet-level params) - `gid` (number|array): tab(s) to export (default: 0) - `name` (string|array): output filename(s) - `size` (string): A3, A4, A5, B4, B5, Letter, Legal, Tabloid (default: A4) - `portrait` (bool): orientation (default: true) - `margins` (string): inches, single value or "top,right,bottom,left" (default: "0") - `merge` (bool): merge tabs into one PDF (default: true) --- ## Common Request Parameters | Field | Type | Required | Description | |---|---|---|---| | `template_id` | string | Yes | Template ID from Drive2pdf dashboard | | `data` | array | Docs only | Array of data objects (see Docs types above) | | `operations` | array | Sheets only | Array of operation objects | | `name` | string | No | Output filename | | `watermark_image_url` | string | No | Watermark image URL | | `watermark_opacity` | number | No | 0.0–1.0 | | `metadata` | object | No | PDF metadata (see below) | | `base64` | bool | No | Include base64 PDF in response | ### Metadata Object Fields: `title`, `author`, `subject`, `keywords` (string|array), `producer`, `creator`, `creation_date` (ISO 8601), `modification_date` (ISO 8601). --- ## Response ### Success (200) ```json { "urls": ["https://pdf.drive2pdf.com/pdfs/.../document.pdf"] } ``` - `urls` is always an array. For single-PDF output it contains one URL. - URLs expire after **24 hours**. Download or forward them promptly. With `base64: true`: ```json { "urls": ["..."], "base64": ["JVBERi0xLjQK..."] } ``` - `base64` array matches `urls` by index. Each entry is the full PDF as a base64-encoded string. ### Response Headers - `X-Request-Id`: unique request ID (include in support requests) - `X-Documents-Limit`: remaining document quota - `X-RateLimit-Quota`: max requests/min - `X-RateLimit-Remaining`: remaining in current window - `X-RateLimit-Reset`: reset timestamp (ms) --- ## Errors & Retry Logic Format: `{ "code": "ERROR_CODE", "message": "..." }` | Status | Code | Retry? | Description | |---|---|---|---| | 400 | INVALID_REQUEST_PAYLOAD | No | Fix request body | | 400 | TEMPLATE_NOT_DEPLOYED | No | Deploy template in dashboard first | | 401 | API_KEY_INVALID | No | Check API key | | 401 | NO_REFRESH_TOKEN | No | Connect Google account in dashboard | | 401 | TOKEN_REFRESH_FAILED | No | Re-authenticate in dashboard | | 402 | QUOTA_EXCEEDED | No | Upgrade plan or wait for quota reset | | 402 | SUBSCRIPTION_EXPIRED | No | Renew subscription | | 408 | REQUEST_TIMEOUT | Yes | Simplify template or reduce data. Max 60s | | 429 | RATE_LIMIT_CONCURRENT_EXCEEDED | Yes | Too many concurrent requests | | 429 | RATE_LIMIT_PER_MINUTE_EXCEEDED | Yes | Too many requests/min | | 500 | INTERNAL_SERVER_ERROR | Yes | Retry with backoff | | 503 | SERVICE_CAPACITY_EXCEEDED | Yes | Retry with backoff | **Retry strategy**: For retryable errors (408, 429, 500, 503), use exponential backoff: wait 1s, then 2s, 4s, 8s. Check `X-RateLimit-Reset` header for 429s to know exactly when to retry. --- ## Anti-Patterns (Do NOT Do) - Do NOT send HTML or Markdown as values — the API only replaces template placeholders with plain text, images, or richtext segments. - Do NOT attempt to upload files or binary data in the request body. - Do NOT try to create, edit, or deploy templates via the API — templates are managed exclusively in the dashboard. - Do NOT use nested tables (tables inside table cells) — they won't repeat. - Do NOT use conditional sections inside repeating table cells — they are ignored. - Do NOT mix different object keys in the same table row (e.g. `{{item.name}}` and `{{user.email}}`). - Do NOT use the Google Drive file ID as `template_id` — use the ID from the Drive2pdf dashboard. --- ## Example: Docs Request (Invoice) ```bash curl -X POST https://api.drive2pdf.com/v1/pdf \ -H "api_key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "template_id": "tpl-123", "data": [ { "key": "company", "value": "Acme Corp", "type": "text" }, { "key": "invoice_number", "value": "INV-2026-042", "type": "text" }, { "key": "date", "value": "January 22, 2026", "type": "text" }, { "key": "total", "value": 9500.00, "type": "number" }, { "key": "logo", "value": "https://example.com/logo.png", "type": "image" }, { "key": "has_discount", "show": true, "type": "section" }, { "key": "show_bank_details", "show": false, "type": "section" }, { "key": "item", "type": "table", "value": [ { "description": "Web Development", "qty": "40 hrs", "rate": "$150.00", "amount": "$6,000.00" }, { "description": "UI/UX Design", "qty": "20 hrs", "rate": "$125.00", "amount": "$2,500.00" }, { "description": "Hosting (Annual)", "qty": "1", "rate": "$1,000.00", "amount": "$1,000.00" } ] }, { "key": "website", "type": "link", "value": "https://acme.com", "caption": "acme.com", "color": "#0055FF", "underline": true } ], "name": "invoice-042", "metadata": { "title": "Invoice INV-2026-042", "author": "Acme Corp" } }' ``` ## Example: Sheets Request (Report) ```bash curl -X POST https://api.drive2pdf.com/v1/pdf \ -H "api_key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "template_id": "tpl-456", "operations": [ { "type": "data", "target": "B2", "content": [["Q4 2025 Report"]] }, { "type": "data", "target": "B3", "content": [["January 22, 2026"]] }, { "type": "data", "target": "revenue_items", "repeat": true, "content": [ ["Web Development", 40, 150.00, 6000.00], ["UI/UX Design", 20, 125.00, 2500.00], ["Hosting", 1, 1000.00, 1000.00] ] }, { "type": "visibility", "dimension": "row", "start": 50, "end": 100, "hide": true, "gid": 0 } ], "gid": [0, 123456], "name": ["summary", "details"], "size": "A4", "portrait": true, "margins": "0.5,0.25,0.5,0.25", "merge": false }' ```