Completing PDFs with JSON

The /api/v1/fill REST endpoint allows you to fill a templatized PDF using JSON data you provide.


First you will need an API key. You can find your API key on the Organization Settings -> API Settings page. We provide a node API client that wraps authentication.

For more information on generating an API key and handling API authentication, check out the API basics.

Creating a PDF Template

Next you will need a PDF Template. A PDF template holds the configuration for a single PDF file. It defines the location of fields on PDF pages, and the type of each field (e.g. date, phone number, etc.). The API allows you to fill the template as many times as you need with different data each time.

On your dashboard, click the + to upload a new PDF Template.

Uploading a PDF Form

Once uploaded, Anvil attempts to find the fields for you. Use the field editor to draw new fields, change field types and names, or modify field locations. Picking the correct types makes filling the PDF easier; compound types (e.g. addresses, names, multiline fields) allow you to fill multiple fields with a single value, and many types (e.g. phone number, date, dollar, SSN) provide formatting for you.

Editing a PDF Template

When you finish editing fields, click over to the API Info tab. It shows everything you need to fill the template with the API, including an example payload to quickly get started.

Viewing PDF Template API Info

Filling a PDF Template

POST to your new PDF template's URL with the data you want to embed in the PDF. Anvil will respond with the raw binary data for the filled PDF.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
  // Optional - set the title encoded into the PDF document
  "title": "Some Title",

  // Optional - default is 10
  "fontSize": 8,

  // Optional - default is dark blue
  "textColor": "#333333",

  // Required - the data to actually fill the PDF
  "data": {
    "someName": "Bobby",
    "someDate": "2018-10-31",
    "anAddress": {
      "street1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94106"
// => Raw PDF bytes
  • Keys in data correspond to a field id in the PDF template.
  • Values in data must be in a format corresponding to their field type. See all field types for more info.
  • Any fields not specified or specified with a null or undefined will be ignored.
  • The response body will be raw binary data for the filled PDF.
  • Anvil does not save the data sent to this endpoint.
curl \
  -X POST \
  -u YOUR_API_KEY: \
  -H 'Content-Type: application/json' \
  -d '{ "data": { "someKey": "some data" } }' \
  https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf > test.pdf

Using the Node Client

For convenience, we provide a node API client. It handles authentication, filling the PDF, and handling errors to help with debugging.

import fs from 'fs'
import Anvil from '@anvilco/anvil'

const pdfTemplateID = 'kA6Da9CuGqUtc6QiBDRR'
const apiKey = '7j2JuUWmN4fGjBxsCltWaybHOEy3UEtt'

const exampleData = {
  "title": "My PDF Title",
  "fontSize": 10,
  "textColor": "#CC0000",
  "data": {
    "someFieldId": "Hello World!"
const anvilClient = new Anvil({ apiKey })
const {
} = await anvilClient.fillPDF(pdfTemplateID, exampleData)

console.log(statusCode) // => 200

// Data will be the filled PDF raw bytes
fs.writeFileSync('output.pdf', data, { encoding: null })

Handling Errors

Should you run into any errors, the response status will be >= 400, and the body will be a JSON payload.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
  // empty request body!
// => 400

Rate Limits

This API enforces separate rate limits for Development and Production API keys.

  • Production: 200 requests over 5 seconds
  • Development: 10 requests over 5 seconds

Each response will contain a few headers that keep track of usage:

HTTP Status: 200
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 196
X-RateLimit-Reset: 148086765

When exceeded, the API will respond with a 429 status code plus a Retry-After header indicating how many seconds to wait:

HTTP Status: 429
Retry-After: 5

Field Types

Each field has a type (e.g. phone, date, address, etc.). When you pass data to fill a PDF, the data will be formatted based on the type's rules before being inserted onto the PDF.

For many types, formatting rules can be chosen in the PDF template editor (e.g. dates, dollar, number, etc.).

Specifying a field's format

Finding Field Types

The PDF template API Info tab shows field information for each field including the id, type, and anything else pertinent to filling the PDF.

Field type information on API info tab

All Field Types

This shows all field types and the format they need to be specified in the payload's data parameter.

// Strings
shortText: String
longText: String
email: String
ssn: String // in format '123121234'
ein: String // in format '921234567'
date: String // in format 'YYYY-MM-DD'

radioGroup: String // ID of the child field that should be selected

// Image fields accept a publicly accessible URL of your image or a
// 'data:' URL. Max size is 10MB. If image is too large or URL is not
// a valid image, the image field will not be filled.
imageFile: String

// Bools
checkbox: Boolean

// Numbers
number: Number
dollar: Number
integer: Number
percent: Number // 0 to 100

// Complex types

// "Boxes Per Letter" in the UI. This will render each character
// in a separate field. e.g. [][][][] with 'Unicorn' input will
// render [U][n][i][c]
charList: String

// "Boxes Per Line" in the UI. It represents several boxes stacked
// into lines. Each newline or element in the array will be rendered
// into the subsequent box.
textWrap: String or Array of lines

// A fullName represents a group of boxes for
// firstName, middle initial, and lastName
fullName: Object {
  firstName: 'Bobby',
  mi: 'W',
  lastName: 'Jones'

usAddress: Object {
  street1: '123 Main St',
  city: 'San Francisco',
  state: 'CA',
  zip: '94106',

phone: Object {
  num: '555113333',

  // Region information is optional, default is US. Currently
  // supported regions are US, MX, CA, GB and any related regions
  // using the +1 (US, CA, GU, etc.), +44 (GB, IM, GG, JE), or +52
  // country codes.
  // If `baseRegion` and `region` match, it will display the number
  // as a local number, without country code information.
  // e.g.
  // { num: '4355345345', region: 'US', baseRegion: 'US' } => '(435) 534-5345'
  // { num: '4355345345', region: 'US', baseRegion: 'MX' } => '+1 (435) 534-5345'
  // { num: '4355345345', region: 'MX', baseRegion: 'MX' } => '435 534 5345'
  // { num: '4355345345', region: 'MX', baseRegion: 'US' } => '+52 435 534 5345'
  region: 'US',
  baseRegion: 'US'