Skip to main content
Base URL: https://api.formepdf.com All /v1/* endpoints require an API key passed as a Bearer token:
Authorization: Bearer forme_sk_...
Create API keys in the dashboard.

Render PDF (sync)

POST /v1/render/:slug Renders a PDF from a template and returns the file directly. Path params: slug — your template’s URL slug. Body: JSON object passed as template data. All fields are forwarded to your JSX template function. Optional fields:
  • s3 — upload the PDF to your S3 bucket instead of returning bytes (see S3 Upload below)
curl https://api.formepdf.com/v1/render/invoice \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"clientName": "Jane Smith", "date": "2024-01-15", "dueDate": "2024-02-15", "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}]}' \
  --output invoice.pdf
Response: 200 OK with Content-Type: application/pdf body.

Render PDF (async)

POST /v1/render/:slug/async Queues a render job and returns immediately. Use this for large documents or when you don’t need the PDF inline. Body: Same as sync, plus:
  • webhookUrl (optional) — URL to POST the result to when rendering completes
curl -X POST https://api.formepdf.com/v1/render/invoice/async \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"clientName": "Jane Smith", "date": "2024-01-15", "dueDate": "2024-02-15", "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}], "webhookUrl": "https://example.com/webhook"}'
Response: 202 Accepted
{ "jobId": "clxyz...", "status": "pending" }

Webhook payload

When the job completes, Forme POSTs to your webhookUrl:
{
  "jobId": "clxyz...",
  "status": "complete",
  "pdfBase64": "JVBERi0xLjQK..."
}
On failure:
{
  "jobId": "clxyz...",
  "status": "failed",
  "error": "Missing required field: \"items\""
}

Poll Job Status

GET /v1/jobs/:jobId Check the status of an async render job.
curl https://api.formepdf.com/v1/jobs/clxyz... \
  -H "Authorization: Bearer forme_sk_abc123..."
Response:
{
  "id": "clxyz...",
  "status": "complete",
  "pdfBase64": "JVBERi0xLjQK...",
  "completedAt": "2024-01-15T12:00:00.000Z"
}
Possible status values: pending, processing, complete, failed.

S3 Upload

Pass an s3 object in the sync render body to upload the PDF directly to your S3-compatible bucket instead of returning the bytes.
curl -X POST https://api.formepdf.com/v1/render/invoice \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "clientName": "Jane Smith",
    "date": "2024-01-15",
    "dueDate": "2024-02-15",
    "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}],
    "s3": {
      "bucket": "my-pdfs",
      "key": "invoices/inv-001.pdf",
      "region": "us-east-1",
      "accessKeyId": "AKIA...",
      "secretAccessKey": "secret..."
    }
  }'
Response: 200 OK
{ "url": "https://my-pdfs.s3.us-east-1.amazonaws.com/invoices/inv-001.pdf" }
S3 config fields:
FieldRequiredDescription
bucketYesS3 bucket name
keyYesObject key (path) in the bucket
accessKeyIdYesAWS access key ID
secretAccessKeyYesAWS secret access key
region*AWS region (e.g. us-east-1). Required unless endpoint is set.
endpoint*Custom S3-compatible endpoint URL. Required unless region is set.
Works with any S3-compatible service (AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces).

Extract Embedded Data

POST /v1/extract Extract embedded JSON data from a PDF that was rendered with embedData. Send the raw PDF bytes as the request body with Content-Type: application/pdf.
curl -X POST https://api.formepdf.com/v1/extract \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/pdf" \
  --data-binary @invoice.pdf
Response: 200 OK
{ "data": { "clientName": "Jane Smith", "items": [...] } }
Returns 404 if no embedded data is found.

Rate Limits

  • 100 requests per minute per API key
  • Monthly render limits depend on your plan:
PlanMonthly rendersTemplates
Free5003
Pro5,000Unlimited
Team25,000Unlimited
Business100,000Unlimited
When you hit a rate limit, the API returns 429 Too Many Requests. When you exceed your monthly render quota, it returns 429 with a message to upgrade.

Error Format

All errors return JSON:
{ "error": "Human-readable error message" }
Common HTTP status codes:
  • 400 — Invalid request (missing fields, bad S3 config)
  • 401 — Missing or invalid API key
  • 404 — Template or resource not found
  • 429 — Rate limit or usage limit exceeded
  • 502 — S3 upload failed
  • 500 — Internal server error