ScreenshotNeo

BlogGuides

Document Generation APIs: Create, List, and Manage Documents

Learn how to create, find, edit, copy, and poll generated documents with Google Docs, Drive, PDFMonkey, and Ema APIs.

By the ScreenshotNeo team1 October 20269 min read

Document generation APIs fall into two groups: low-level document editors and higher-level generation services. Google Docs gives you document creation and atomic edits, with Google Drive handling discovery and copying. PDFMonkey uses an asynchronous template-rendering workflow. Ema provides higher-level outline, section, template, rendering, and review operations.

The right workflow depends on whether you need an immediately editable document, a generated file from a template, or a structured long-form document with review features.

Choose the right document API

Need Best fit Why
Create and edit native collaborative documents Google Docs API Creates documents and applies structured edits through batchUpdate.
Find documents or copy them into a workflow Google Drive API files.list discovers files and files.copy duplicates them.
Render a template into a downloadable file PDFMonkey Uses a queued generation lifecycle with status polling and a download URL.
Generate proposals, RFP responses, or other structured long documents Ema Provides outline generation, bulk sections, templates, rendering, extraction, profiles, and threaded comments.

Compare services on five axes:

  • Abstraction: Google exposes document primitives; PDFMonkey and Ema expose template or section workflows.
  • Execution: Google creates a blank document immediately, while PDFMonkey queues generation.
  • Template control: Google requires you to compose content and formatting requests; specialized services center templates and payloads.
  • Lifecycle: Google combines Docs and Drive operations. Generation services expose status, downloads, templates, and related management methods.
  • Review: Ema documents comments and threaded review operations.

Google Docs API: create a document

The Google Docs API exposes documents.create, documents.get, and documents.batchUpdate. The create method returns the new document object and its documentId.Google Docs create reference

Prerequisites

  1. Create a Google Cloud project.
  2. Enable the Google Docs API and, when using file discovery or copying, the Google Drive API.
  3. Create OAuth credentials or use a service account appropriate for your deployment.
  4. Request the narrowest scopes your application needs.
  5. Store the access token outside source control.

cURL: create a blank document

curl -X POST \
  'https://docs.googleapis.com/v1/documents' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Quarterly report"}'

Save the returned documentId. Every subsequent Docs request uses it.

Python: create and read the document

import requests

TOKEN = "ACCESS_TOKEN"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json",
}

created = requests.post(
    "https://docs.googleapis.com/v1/documents",
    headers=headers,
    json={"title": "Quarterly report"},
    timeout=30,
)
created.raise_for_status()
document = created.json()
document_id = document["documentId"]

current = requests.get(
    f"https://docs.googleapis.com/v1/documents/{document_id}",
    headers=headers,
    timeout=30,
)
current.raise_for_status()
print(current.json()["title"], document_id)

Node.js: create and read the document

const token = process.env.GOOGLE_ACCESS_TOKEN;
const headers = {
  Authorization: `Bearer ${token}`,
  'Content-Type': 'application/json'
};

const createdRes = await fetch('https://docs.googleapis.com/v1/documents', {
  method: 'POST',
  headers,
  body: JSON.stringify({ title: 'Quarterly report' })
});
if (!createdRes.ok) throw new Error(await createdRes.text());
const created = await createdRes.json();

const documentRes = await fetch(
  `https://docs.googleapis.com/v1/documents/${created.documentId}`,
  { headers }
);
if (!documentRes.ok) throw new Error(await documentRes.text());
console.log(await documentRes.json());

Edit documents with batchUpdate

batchUpdate is the Docs editing primitive. It accepts one or more requests and applies them atomically, allowing insertion, formatting, list creation, and other structural changes in one operation.Google Docs batchUpdate reference

Insert text and format a heading

curl -X POST \
  'https://docs.googleapis.com/v1/documents/DOCUMENT_ID:batchUpdate' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "requests": [
      {
        "insertText": {
          "location": {"index": 1},
          "text": "Quarterly report\nSummary of results\n"
        }
      },
      {
        "updateParagraphStyle": {
          "range": {"startIndex": 1, "endIndex": 18},
          "paragraphStyle": {"namedStyleType": "TITLE"},
          "fields": "namedStyleType"
        }
      },
      {
        "updateParagraphStyle": {
          "range": {"startIndex": 19, "endIndex": 39},
          "paragraphStyle": {"namedStyleType": "HEADING_1"},
          "fields": "namedStyleType"
        }
      }
    ]
  }'

Indexes refer to the document’s structural content. When composing multiple requests, account for inserted text and preserve the trailing newline required by paragraph operations.

Python: one atomic edit request

import requests

TOKEN = "ACCESS_TOKEN"
DOCUMENT_ID = "DOCUMENT_ID"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json",
}

requests_body = {
    "requests": [
        {
            "insertText": {
                "location": {"index": 1},
                "text": "Summary\nRevenue increased 12%.\n",
            }
        },
        {
            "updateTextStyle": {
                "range": {"startIndex": 1, "endIndex": 8},
                "textStyle": {"bold": True},
                "fields": "bold",
            }
        },
    ]
}

response = requests.post(
    f"https://docs.googleapis.com/v1/documents/{DOCUMENT_ID}:batchUpdate",
    headers=headers,
    json=requests_body,
    timeout=30,
)
response.raise_for_status()
print(response.json())

Node.js: batch updates

const body = {
  requests: [
    {
      insertText: {
        location: { index: 1 },
        text: 'Summary\nRevenue increased 12%.\n'
      }
    },
    {
      updateTextStyle: {
        range: { startIndex: 1, endIndex: 8 },
        textStyle: { bold: true },
        fields: 'bold'
      }
    }
  ]
};

const res = await fetch(
  `https://docs.googleapis.com/v1/documents/${process.env.DOCUMENT_ID}:batchUpdate`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.GOOGLE_ACCESS_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  }
);
if (!res.ok) throw new Error(await res.text());
console.log(await res.json());

Common batchUpdate operations

Operation Use Implementation note
insertText Add text at an index Insert paragraphs with newline characters.
updateTextStyle Bold, italicize, underline, or change text appearance Specify only the fields you want to change.
updateParagraphStyle Apply heading, alignment, spacing, or indentation Use a range covering complete paragraphs.
createParagraphBullets Turn paragraphs into a list Apply it after inserting the list text.
insertTable Create a table Follow with cell-level insertion and formatting requests.

Get, list, and find documents

Use Docs documents.get when you already know the document ID and need its current structure. Use Drive files.list to discover documents by name, MIME type, folder, owner, or other file metadata. Google documents the Drive API as the file-discovery layer for this workflow.Drive files.list reference

List Google Docs files with cURL

curl -G 'https://www.googleapis.com/drive/v3/files' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  --data-urlencode "q=mimeType='application/vnd.google-apps.document' and trashed=false" \
  --data-urlencode 'fields=files(id,name,modifiedTime,webViewLink),nextPageToken' \
  --data-urlencode 'pageSize=100'

For a name search, add a condition such as name contains 'Quarterly'. Escape apostrophes in search values and always filter out trashed files when appropriate.

Read a known document

curl -X GET \
  'https://docs.googleapis.com/v1/documents/DOCUMENT_ID' \
  -H 'Authorization: Bearer ACCESS_TOKEN'

Copy a document with Drive

Google Docs does not provide an option to create a document directly inside a specified Drive folder. Create or copy the file, then use Drive operations to organize it.Drive files.copy reference

curl -X POST \
  'https://www.googleapis.com/drive/v3/files/SOURCE_FILE_ID/copy' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "April report",
    "parents": ["TARGET_FOLDER_ID"]
  }'

Asynchronous document generation

Template-rendering APIs often do not return a finished file in the create response. PDFMonkey documents this lifecycle: create a document with a template ID and payload, receive a pending document without a download URL, poll its status, and download the file after the status becomes success.PDFMonkey Documents API documentation

Reliable polling pattern

  1. Submit the generation request with an idempotency key if the provider supports one.
  2. Persist the provider’s document ID immediately.
  3. Poll with exponential backoff, such as 1, 2, 4, 8, and 16 seconds.
  4. Stop on a terminal success or failure state.
  5. Apply a deadline so a stuck job does not consume a worker forever.
  6. Download the result only after success, then verify the content type and size.
async function waitForDocument(getStatus, id, deadlineMs = 120000) {
  const started = Date.now();
  let delay = 1000;

  while (Date.now() - started < deadlineMs) {
    const document = await getStatus(id);
    if (document.status === 'success') return document;
    if (document.status === 'failure' || document.status === 'error') {
      throw new Error(`Generation failed for ${id}`);
    }
    await new Promise(resolve => setTimeout(resolve, delay));
    delay = Math.min(delay * 2, 16000);
  }

  throw new Error(`Generation timed out for ${id}`);
}

The exact create and status URLs, authentication headers, and payload shape are provider-specific; use the selected provider’s reference rather than assuming Google Docs semantics.

Ema’s higher-level generation workflow

Ema’s v2 reference targets structured documents such as proposals and RFP responses. Its documented capabilities include:

  • Generating an outline from HTML or uploaded-document markdown.
  • Creating many sections in bulk.
  • Uploading, updating, duplicating, and managing templates.
  • Rendering HTML and thumbnails.
  • Setting a default template.
  • Rerunning extraction and applying section profiles.
  • Classifying content.
  • Adding threaded comments for review.

This is a different abstraction from Google Docs. Instead of assembling every paragraph and style request yourself, you manage a structured document model with templates, sections, extraction, rendering, and review operations.

Production design checklist

  • Persist document IDs and provider job IDs before returning from a request.
  • Use correlation IDs in logs so creation, edits, polling, and downloads can be traced.
  • Make retries safe. Retry transient network and rate-limit failures, but avoid duplicating non-idempotent edits.
  • Batch related Google Docs edits into one batchUpdate where possible.
  • Paginate Drive list calls and follow nextPageToken.
  • Validate template variables before submitting generation jobs.
  • Set polling deadlines and alert on jobs that remain pending unusually long.
  • Verify downloaded files before marking a job complete.
  • Keep OAuth tokens and service credentials in a secret manager.
  • Request only the scopes required by your workflow.

Troubleshooting

Symptom Likely cause Fix
401 Unauthorized Expired, missing, or malformed access token Refresh the token and send Authorization: Bearer ....
403 Forbidden API disabled, insufficient scope, or no access to the file Enable the API, request the required scope, and share the document with the calling identity.
404 on a document Wrong ID or the caller cannot see the file Confirm the ID from the create or Drive response and verify permissions.
400 invalid requests in batchUpdate Bad indexes, unsupported fields, or a range that crosses structural boundaries Fetch the document, recalculate indexes, and submit a smaller batch to isolate the invalid request.
Text appears in the wrong place Indexes shifted after an earlier insertion Order insertions carefully and calculate ranges against the resulting document structure.
Drive list returns no files Query excludes the file, file is trashed, or the account lacks access Test a broader query, include trashed=false deliberately, and check the caller’s Drive permissions.
Generation remains pending Queue delay, invalid payload, or provider-side failure Continue bounded polling, inspect the job’s error state, and stop after a deadline.
Download URL is absent Asynchronous job has not reached success Poll status and download only after the provider reports success.
Duplicate documents after retry Create request was retried without idempotency protection Store the first response and use provider-supported idempotency keys or a client request table.

Performance, reliability, and cost considerations

Performance

  • Use one Google Docs batchUpdate for related edits instead of many sequential requests.
  • Cache document IDs after discovery instead of listing Drive files on every request.
  • Keep polling intervals bounded and increase them gradually.
  • Generate independent documents concurrently within provider rate limits.

Reliability

  • Treat document creation, editing, rendering, and downloading as separate states.
  • Persist state before acknowledging work to an upstream queue.
  • Retry only transient failures and log the provider response body.
  • Use a dead-letter path for jobs that exceed their deadline or fail validation.

Cost

Google Docs and Drive usage is governed by Google’s account, API, and quota policies. PDFMonkey and Ema costs depend on their current plans and usage terms. Measure documents created, pages rendered, polling calls, and retries so you can attribute spend to a workflow.

Or skip the browser setup

If your document workflow also needs screenshots of generated pages, reports, or public previews, ScreenshotNeo provides a single website screenshot API call. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for all options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Can Google Docs create a document directly in a Drive folder?

No. Create the document with Docs, then use Drive operations to organize or copy it into a folder.

When should I use batchUpdate?

Use it whenever you need to insert or format content. Group related requests so they apply atomically.

Do all document-generation APIs return a download immediately?

No. Google creates an editable document immediately, while PDFMonkey documents a pending, polling-based generation flow.

Which API fits collaborative editing?

Google Docs is the natural fit when the result must remain a native, collaboratively editable document.

Which API fits proposal or RFP automation?

Ema is designed for structured long-form workflows with outlines, sections, templates, rendering, extraction, and threaded comments.