PDF API docs

Docweave is a PDF API built for AI agents and developers: one REST or MCP call generates a PDF from HTML, a URL, or a template + JSON — or reads a PDF back to markdown. Use the reference below for auth, endpoints, options, and copy-paste samples.

Merge, batch, or run async with webhooks. Bearer-key auth, idempotency, per-document metering.

Quick start

  1. Create a free account at /signup — 50 PDFs/month, no card.
  2. In your dashboard, mint an API key (shown once — copy it).
  3. POST to /api/v1/pdf with that key (examples below).
  4. Need more volume? Upgrade anytime at /pricing.

Authentication

Pass your key as Authorization: Bearer <key>. Include an idempotencyKey to make retries safe — a repeat returns the stored result instead of re-rendering (or re-billing).

POST /api/v1/pdf

The core endpoint. Send a source object and optional options. Returns JSON by default; add Accept: application/pdf for raw bytes.

Source types

type: "html"stringRaw HTML string to render
type: "url"stringPublic URL to screenshot (SSRF-guarded)
type: "markdown"stringMarkdown rendered with pro typography
type: "template"stringMustache template + data object
cURL
curl https://docweave.dev/api/v1/pdf \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/pdf" \
  -d '{
    "source": { "type": "html", "html": "<h1>Hello, PDF</h1>" },
    "options": { "format": "A4" }
  }' \
  --output hello.pdf
Node.js (ESM)
import { writeFile } from "node:fs/promises";

const res = await fetch("https://docweave.dev/api/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: "Bearer dw_live_your_key",
    "Content-Type": "application/json",
    Accept: "application/pdf",
  },
  body: JSON.stringify({
    source: {
      type: "template",
      template: "<h1>Invoice {{ number }}</h1><p>Total: {{ total }}</p>",
      data: { number: "INV-042", total: "$1,280.00" },
    },
    idempotencyKey: "inv-042",
  }),
});
await writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));
Python
import requests

r = requests.post(
    "https://docweave.dev/api/v1/pdf",
    headers={"Authorization": "Bearer dw_live_your_key", "Accept": "application/pdf"},
    json={"source": {"type": "url", "url": "https://example.com"}},
)
with open("out.pdf", "wb") as f:
    f.write(r.content)
Go
package main

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"source": map[string]string{"type": "html", "html": "<h1>Hello</h1>"},
	})
	req, _ := http.NewRequest("POST", "https://docweave.dev/api/v1/pdf", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer dw_live_your_key")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/pdf")

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
	out, _ := os.Create("hello.pdf")
	io.Copy(out, resp.Body)
}
Ruby
require "net/http"
require "json"
require "uri"

uri = URI("https://docweave.dev/api/v1/pdf")
req = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer dw_live_your_key",
  "Content-Type" => "application/json",
  "Accept" => "application/pdf",
})
req.body = { source: { type: "html", html: "<h1>Hello</h1>" } }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
File.binwrite("hello.pdf", res.body)
PHP
<?php
$ch = curl_init("https://docweave.dev/api/v1/pdf");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer dw_live_your_key",
        "Content-Type: application/json",
        "Accept: application/pdf",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "source" => ["type" => "html", "html" => "<h1>Hello</h1>"],
    ]),
]);
$pdf = curl_exec($ch);
file_put_contents("hello.pdf", $pdf);
Java 11+
import java.net.http.*;
import java.net.URI;
import java.nio.file.*;

var body = """
  {"source": {"type": "html", "html": "<h1>Hello</h1>"}}""";

var req = HttpRequest.newBuilder()
    .uri(URI.create("https://docweave.dev/api/v1/pdf"))
    .header("Authorization", "Bearer dw_live_your_key")
    .header("Content-Type", "application/json")
    .header("Accept", "application/pdf")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

var res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("hello.pdf"), res.body());

Page options

All options are optional. Pass them in the options object.

format"A4" | "A3" | "Letter" | "Legal" | "Tabloid"Page size (default: A4)
landscapebooleanLandscape orientation
marginobject{ top, right, bottom, left } as CSS sizes
scalenumberRender scale 0.1–2.0
printBackgroundbooleanInclude CSS backgrounds/colors
headerstringSimple header text (supports {{page}}, {{pages}})
footerstringSimple footer text (supports {{page}}, {{pages}})
headerHtmlstringRaw Chromium header template HTML
footerHtmlstringRaw Chromium footer template HTML
watermarkstringDiagonal text watermark on all pages
fontstringGoogle Font family name (auto-imported)

Markdown source

Send source.type: "markdown" with a raw markdown string. Tables, code blocks, headings, lists — all rendered with professional typography. Combine with the font option to change the typeface.

Markdown → PDF
curl https://docweave.dev/api/v1/pdf \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/pdf" \
  -d '{
    "source": {
      "type": "markdown",
      "markdown": "# Project Report\n\n## Summary\n\nQ3 revenue grew **42%** YoY.\n\n| Metric | Value |\n|--------|-------|\n| Revenue | $1.2M |\n| Users | 15,000 |"
    },
    "options": { "font": "Inter" }
  }' --output report.pdf

Headers & footers

Use options.header and options.footer for simple text with page number placeholders. Use {{page}} for current page and {{pages}} for total. Set margins to give the header/footer room.

Headers + page numbers
{
  "source": { "type": "html", "html": "<h1>Contract</h1><p>Terms...</p>" },
  "options": {
    "header": "Acme Corp — Confidential",
    "footer": "Page {{page}} of {{pages}}",
    "margin": { "top": "80px", "bottom": "80px" }
  }
}

Watermarks

Add options.watermark to stamp a diagonal semi-transparent label across every page. Common values: "DRAFT", "CONFIDENTIAL", "SAMPLE".

Watermark
{
  "source": { "type": "html", "html": "<h1>Draft Proposal</h1><p>...</p>" },
  "options": {
    "watermark": "DRAFT",
    "format": "A4"
  }
}

Google Fonts

Pass options.font with any Google Font family name. It is automatically imported and applied to the document body. No need to add <link> tags yourself.

Custom font
{
  "source": { "type": "html", "html": "<h1>Fancy Report</h1><p>With custom typography</p>" },
  "options": {
    "font": "Playfair Display",
    "format": "A4"
  }
}

POST /api/v1/pdf/batch

Render up to 10 PDFs in a single request. Each item uses the same format as /api/v1/pdf. Returns an array of results (some may fail while others succeed). Each successful render counts as one against your monthly quota.

Batch render
curl https://docweave.dev/api/v1/pdf/batch \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "source": { "type": "html", "html": "<h1>Invoice 1</h1>" } },
      { "source": { "type": "html", "html": "<h1>Invoice 2</h1>" } },
      { "source": { "type": "markdown", "markdown": "# Report" } }
    ]
  }'
# → { "results": [{ "ok": true, ... }, { "ok": true, ... }, ...] }

POST /api/v1/pdf/merge

Render 2–10 sources and combine them into a single multi-page PDF. Each source can be a different type (HTML, markdown, URL, template). The merge counts as one render.

Merge multiple sources
curl https://docweave.dev/api/v1/pdf/merge \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/pdf" \
  -d '{
    "sources": [
      { "source": { "type": "html", "html": "<h1>Cover Page</h1>" } },
      { "source": { "type": "markdown", "markdown": "# Chapter 1\n\nContent..." } },
      { "source": { "type": "html", "html": "<h1>Appendix</h1>" } }
    ],
    "fileName": "full-document.pdf"
  }' --output full-document.pdf

POST /api/v1/pdf/async

For long-running renders or when you don't want to hold a connection open. Returns a job ID immediately (HTTP 202), then POSTs the result to your webhookUrl when the PDF is ready.

Async with webhook
curl https://docweave.dev/api/v1/pdf/async \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "type": "url", "url": "https://example.com/large-report" },
    "webhookUrl": "https://your-app.com/webhooks/pdf-ready"
  }'
# → { "jobId": "job_abc123", "status": "processing" }

# Your webhook receives:
# POST https://your-app.com/webhooks/pdf-ready
# { "jobId": "job_abc123", "status": "completed",
#   "result": { "ok": true, "url": "...", "byteSize": 45231 } }

POST /api/v1/read

The other direction: turn a PDF into text or markdown. Send a source that is a public URL or inline base64 bytes; get back extracted content, page count, and a needsOcr flag. URL rendering is SSRF-guarded, and reads are metered per document.

Read a PDF
curl https://docweave.dev/api/v1/read \
  -H "Authorization: Bearer dw_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "type": "url", "url": "https://example.com/invoice.pdf" },
    "options": { "format": "markdown" }
  }'
# → { "result": { "content": "…markdown…", "pageCount": 3,
#                 "hasTextLayer": true, "needsOcr": false } }

Integrations

Docweave works with any tool that can make HTTP requests. Here are step-by-step guides for popular no-code platforms:

n8n

HTTP Request node → PDF in your workflow

Make (Integromat)

HTTP module → PDF in any scenario

Zapier

Webhooks by Zapier → PDF on any trigger

AI Agents (MCP)

generate_pdf + read_pdf tools via MCP server

Plans & limits

Free includes 50 PDFs/month. Starter (1,000), Pro (12,000), and Business (50,000) raise the monthly limit — see pricing. When you hit your limit the API returns 402. Only successful renders count; the counter resets each calendar month.

Rate limit: 120 requests/minute per API key. Batch items count individually toward quota but as one rate-limit hit. The async endpoint counts one render on completion.