How to Fill PDF Forms Using the PDF.co Web API in JavaScript

This tutorial demonstrates how to fill an existing PDF form with JavaScript and the PDF.co Web API. The example uses Node.js to submit form-field values, download the completed PDF, and save it locally.

Use this workflow in a server-side application. Do not expose your PDF.co API key in browser-based JavaScript.

What You’ll Need

Before starting, prepare:

  • Node.js 18 or later
  • A PDF.co account and API key
  • A fillable PDF form
  • The internal names of the PDF fields you want to populate

This tutorial uses the following sample form:

Download the sample IRS Form 1040

How the Workflow Works

The application will:

  1. Define the source PDF and form-field values.
  2. Send the data to the PDF.co PDF Add endpoint.
  3. Receive a temporary URL for the completed PDF.
  4. Download the result.
  5. Save it as result.pdf.

The endpoint used in this tutorial is:

POST https://api.pdf.co/v1/pdf/edit/add

The endpoint can fill existing fields, add text annotations, place images, and create new form controls. See the PDF Add API documentation for its complete request schema.

Create the Node.js Project

Create a new project directory and initialize it:

mkdir pdf-form-filler
cd pdf-form-filler
npm init -y

The example uses APIs included with Node.js 18, so no additional package is required.

Configure the PDF.co API Key

Copy your API key from the PDF.co dashboard.

Store it in the PDFCO_API_KEY environment variable.

On Windows PowerShell:

$env:PDFCO_API_KEY="YOUR_PDFCO_API_KEY"

On macOS or Linux:

export PDFCO_API_KEY="YOUR_PDFCO_API_KEY"

The application will send this value in the x-api-key request header.

Identify the PDF Form Fields

PDF form fields have internal names that may differ from their visible labels. You must use these internal names when constructing the request.

You can inspect a PDF form with the PDF.co PDF Inspector. Upload the form and review its field names, page information, and field types.

For automated inspection, use the /v1/pdf/info/fields endpoint. PDF.co recommends this endpoint for retrieving information about text fields, checkboxes, radio buttons, and other fillable controls.

For the sample form, a field name looks like this:

topmostSubform[0].Page1[0].f1_02[0]

Copy field names exactly, including capitalization, brackets, and indexes.

Define the Form-Field Values

Each object in the fields array can contain:

  • fieldName: The internal PDF field name.
  • pages: The page associated with the field.
  • text: The value to place in the field.
  • fontName: An optional font name.
  • size: An optional font size.
  • Font styling options such as fontBold, fontItalic, fontUnderline, and fontStrikeout.

For example:

const fields = [
    {
        fieldName: "topmostSubform[0].Page1[0].f1_02[0]",
        pages: "1",
        text: "John A."
    },
    {
        fieldName: "topmostSubform[0].Page1[0].f1_03[0]",
        pages: "1",
        text: "Doe"
    }
];

Use the page value returned by PDF Inspector or the fields-information endpoint.

For checkboxes and radio buttons, use the value required by that field. The sample form accepts "True" for the selected checkbox:

{
    fieldName: "topmostSubform[0].Page1[0].FilingStatus[0].c1_01[1]",
    pages: "1",
    text: "True"
}

When setting a custom font size, also specify fontName:

{
    fieldName:
        "topmostSubform[0].Page1[0].YourSocial_ReadOrderControl[0].f1_05[0]",
    pages: "1",
    text: "Joan B.",
    fontName: "Arial",
    size: 8
}

Create the JavaScript Application

Create a file named app.js and add the following code:

const { writeFile } = require("node:fs/promises");
const path = require("node:path");

const API_KEY = process.env.PDFCO_API_KEY;

const SOURCE_FILE_URL =
    "https://pdfco-test-files.s3.us-west-2.amazonaws.com/pdf-form/f1040.pdf";

const DESTINATION_FILE = "./result.pdf";

const fields = [
    {
        fieldName:
            "topmostSubform[0].Page1[0].FilingStatus[0].c1_01[1]",
        pages: "1",
        text: "True"
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].f1_02[0]",
        pages: "1",
        text: "John A."
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].f1_03[0]",
        pages: "1",
        text: "Doe"
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].YourSocial_ReadOrderControl[0].f1_04[0]",
        pages: "1",
        text: "123456789"
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].YourSocial_ReadOrderControl[0].f1_05[0]",
        pages: "1",
        text: "Joan B.",
        fontName: "Arial",
        size: 8
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].YourSocial_ReadOrderControl[0].f1_06[0]",
        pages: "1",
        text: "Doe"
    },
    {
        fieldName:
            "topmostSubform[0].Page1[0].YourSocial_ReadOrderControl[0].f1_07[0]",
        pages: "1",
        text: "987654321"
    }
];

async function fillPdfForm() {
    if (!API_KEY) {
        throw new Error(
            "Set the PDFCO_API_KEY environment variable before running the application."
        );
    }

    const payload = {
        name: path.basename(DESTINATION_FILE),
        url: SOURCE_FILE_URL,
        password: "",
        async: false,
        fields
    };

    const apiResponse = await fetch(
        "https://api.pdf.co/v1/pdf/edit/add",
        {
            method: "POST",
            headers: {
                "x-api-key": API_KEY,
                "Content-Type": "application/json"
            },
            body: JSON.stringify(payload)
        }
    );

    const responseText = await apiResponse.text();

    let result;

    try {
        result = JSON.parse(responseText);
    } catch {
        throw new Error(
            `PDF.co returned an invalid response: ${responseText}`
        );
    }

    if (!apiResponse.ok || result.error) {
        throw new Error(
            result.message ||
            `PDF.co returned HTTP ${apiResponse.status}.`
        );
    }

    if (!result.url) {
        throw new Error(
            "PDF.co did not return a URL for the completed PDF."
        );
    }

    console.log("Downloading the completed PDF...");

    const fileResponse = await fetch(result.url);

    if (!fileResponse.ok) {
        throw new Error(
            `Unable to download the completed PDF: HTTP ${fileResponse.status}.`
        );
    }

    const fileBuffer = Buffer.from(
        await fileResponse.arrayBuffer()
    );

    await writeFile(DESTINATION_FILE, fileBuffer);

    console.log(
        `Completed PDF saved as "${DESTINATION_FILE}".`
    );
}

fillPdfForm().catch((error) => {
    console.error(error.message);
    process.exitCode = 1;
});

Run the Application

Run the script from the same terminal session in which you configured the API key:

node app.js

When the request completes successfully, the application saves the filled form as:

result.pdf

Open the file and verify that the supplied values appear in the correct fields.

Important Request Parameters

The PDF Add endpoint accepts the following principal parameters:

  • url Direct URL of the source PDF form
  • fields Array of existing form fields and their new values
  • annotations Text and form objects placed at specified coordinates
  • images Images placed on the document, such as a logo or signature image
  • password Password required to open a protected source PDF
  • name Filename assigned to the generated PDF
  • async Runs the job in the background when set to true
  • expiration Number of minutes the generated output link remains available
  • profiles Additional PDF.co processing options

Adding a Signature Image

A signature can be placed on the PDF as an image object. Add an images array to the request payload and provide the image URL, coordinates, dimensions, and page.

For example:

const payload = {
    name: path.basename(DESTINATION_FILE),
    url: SOURCE_FILE_URL,
    async: false,
    fields,
    images: [
        {
            url: "https://example.com/signature.png",
            x: 100,
            y: 600,
            width: 150,
            height: 50,
            pages: "0"
        }
    ]
};

The image URL must be directly accessible to PDF.co.

Flattening the Completed Form

If recipients should not be able to edit the completed form fields, add the flattening profile to the request:

const payload = {
    name: path.basename(DESTINATION_FILE),
    url: SOURCE_FILE_URL,
    async: false,
    fields,
    profiles: "{ 'FlattenDocument()': [] }"
};

Flattening renders the completed fields into the document content.

Processing Large Documents

The example uses synchronous processing:

async: false

For a large PDF or a request containing many fields and images, set:

async: true

PDF.co will return a jobId. Use the Background Job Check endpoint to monitor the job until its status is success, failed, or aborted. The successful job response contains the completed file URL.

Troubleshooting

A field remains empty

Confirm that:

  • The source PDF contains an interactive form field.
  • fieldName matches the internal field name exactly.
  • The specified page matches the field information.
  • The field accepts the supplied value.

A checkbox is not selected

Check the allowed values reported for that checkbox or radio-button field. Depending on the form, the required value may differ from "True".

The font size is not applied

Specify both fontName and size in the field object.

PDF.co cannot access the source

Use a direct file URL. The URL must allow PDF.co to download the PDF without opening an interactive sign-in page.

The result link expires

Download the generated PDF promptly or copy it to permanent storage. PDF.co output links are temporary and use the configured expiration period.

Full Code Sample

The complete PDF.co JavaScript sample is available on GitHub:

View the complete Fill PDF Forms JavaScript sample

You can also review the current request fields and examples in the PDF Add API documentation.

Conclusion

You have created a Node.js application that sends form-field values to PDF.co, generates a completed PDF, and downloads the result. You can adapt this workflow to populate forms with information from databases, spreadsheets, CRM systems, web forms, or other application data.

Related Tutorials

See Related Tutorials