Skip to main content

PDF4me JavaScript SDK Getting Started

The pdf4me package on npm is the official TypeScript/JavaScript ESM client for the PDF4me REST API. It provides 106 actions across 18 modules: convert, optimize, merge, split, stamp, and extract content from documents with async functions and typed results. Types ship inside the package, so TypeScript projects need no separate @types install.

Package facts​

FieldValue
Package namepdf4me
Current version10.0.1 (published 2026-09-14)
LicenseMIT
Node.js support>=22, declared in engines
Module formatESM only ("type": "module")
TypesBundled (dist/index.d.ts)
Side effectsNone ("sideEffects": false)
Actions106 across 18 import subpaths
Runtime dependenciesFive Microsoft Kiota packages (abstractions plus the JSON, form, text, and multipart serializers)
Sourcegithub.com/pdf4me/pdf4me-clientapi-javascript
npmnpmjs.com/package/pdf4me

Prerequisites​

  • Node.js 22 or newer.
  • A PDF4me account and API key.
  • A project that can use ESM import syntax ("type": "module" in your package.json, or .mjs files).

Install​

npm install pdf4me@^10

The ^10 range matters. A ^9 dependency range will not pick up version 10 on its own, because version 10 is a rewrite with breaking changes. See Upgrading from 9.x below.

Node.js 22 is required, but npm only warns

engines declares node: ">=22", and by default npm emits a warning rather than an error on a mismatch. A project still running Node.js 18 will install version 10 successfully and then fail at runtime. Check node --version before you ship.

To work from a source checkout instead of the registry, run npm install and npm run build inside the package directory, then run npm install /absolute/path/to/pdf4me from your application directory.

Authenticate​

Set your API key in the environment:

export PDF4ME_API_KEY="your-api-key"

The client sends the key as an Authorization: Basic <apiKey> header on every request. The library does not log credentials or document content. Constructing a client with a blank key throws a TypeError immediately, before any network call.

Quick start​

Save this as optimize-pdf.mjs and put an input.pdf in the same working directory. The example uploads the PDF for optimization and saves the result as optimized.pdf.

import { readFile, writeFile } from "node:fs/promises";
import { Pdf4meClient } from "pdf4me";
import { optimize } from "pdf4me/optimize";

const apiKey = process.env.PDF4ME_API_KEY;
if (!apiKey) {
throw new Error("Set PDF4ME_API_KEY before running this example.");
}

const client = new Pdf4meClient(apiKey);
const data = await readFile("input.pdf");
const result = await optimize(client, data, { docName: "input.pdf" });
await writeFile("optimized.pdf", result);
console.log("Saved optimized.pdf");

Run it with:

node optimize-pdf.mjs

The same imports work in TypeScript ESM projects, with types included in the package.

Three things to notice:

  1. One client, many actions. new Pdf4meClient(apiKey) holds the configuration. fetch owns the connection pool, so there is no close() method to call and nothing to release when you are finished.
  2. Actions are standalone functions, not client methods. Each one is imported from its own subpath (pdf4me/optimize, pdf4me/merge-split, pdf4me/edit) and called as action(client, source, options).
  3. The argument order is fixed. Reusable client first, source content next when the action takes one, and an options object last.

What the package root exports​

The root subpath (pdf4me) carries the client and its supporting types. Actions never live here.

ExportKindPurpose
Pdf4meClientclassHolds the API key and transport settings; passed as the first argument to every action
Pdf4meExceptionclassThrown when the service returns a failure
Pdf4meTimeoutErrorclassThrown when a request or job time limit is reached
Pdf4meClientOptionstypeThe options object accepted by the client constructor
DocumentSourcetypeUint8Array | string — the accepted shape of source content
DEFAULT_BASE_URLconsthttps://api.pdf4me.com
DEFAULT_TIMEOUTconst100000
DEFAULT_MAX_WAITconst300000
DEFAULT_POLL_INTERVALconst10000

Requests and results​

Every action submits isAsync: true and awaits the completed result. An immediate HTTP 200 returns straight away. An HTTP 202 hands back a job id, which the client then polls at GetActionStatus until the job finishes. Your code sees one await either way.

What you pass in​

DocumentSource is Uint8Array | string, which covers four practical forms:

Source formNotes
Uint8ArrayIncludes Node.js Buffer, which subclasses Uint8Array. Bytes are base64-encoded for you, in bounded chunks so large documents do not overflow the stack.
base64 stringPassed straight through.
pdf4meblobid://...A reference to content already held by PDF4me.
URLThe service fetches the document itself.

Empty content is rejected locally with a TypeError rather than being sent.

Actions with multiple inputs accept ordered arrays. addAttachmentToPdf is the exception: it takes an attachments object mapping filenames to content.

What you get back​

Result kindTypeExample
File actionsUint8ArrayAccepted directly by Node.js writeFile
JSON actionsTyped modelsDocMetadata, SplitPdfRes

Models and enum constants are exported both from the action's own module and from the pdf4me/models subpath. Unknown response fields are retained in additionalData, so a newer API field is never silently dropped.

Two more calls​

import { merge } from "pdf4me/merge-split";
import { stamp, StampAlignX } from "pdf4me/edit";

const merged = await merge(client, [firstPdf, secondPdf]);
const stamped = await stamp(client, merged, {
text: "DRAFT",
alignX: StampAlignX.Center,
});

StampAlignX.Center is a typed constant, so your editor auto-completes the legal values and tsc catches typos before the request is ever sent.

Options and defaults​

All options interfaces are exported, for example OptimizeOptions. Two behaviours are worth knowing:

  • Defaults are applied when an option is absent. The default optimize profile is Max. An explicit null does not select a default.
  • Every action accepts extra, an object serialized as additional request fields. Use the API's own field names, and avoid keys the action options already supply.

Client configuration​

const client = new Pdf4meClient(apiKey, {
baseUrl: "https://api.pdf4me.com",
timeout: 100_000,
maxWait: 300_000,
pollInterval: 10_000,
});
All durations are milliseconds

The Python client uses seconds for the same settings. If you are porting code between the two SDKs, rescale every duration.

OptionDefaultWhat it caps
baseUrlhttps://api.pdf4me.comThe API host every request is sent to
timeout100000 msEach individual request, including reading its body
maxWait300000 msPolling after the initial job submission, including in-flight status requests
pollInterval10000 msThe fallback gap between status checks
fetchglobalThis.fetchA custom transport, useful for testing or proxy configuration

Each default is also exported as a constant (DEFAULT_TIMEOUT and friends), so you can scale from the shipped value rather than hard-coding a number.

The constructor validates what you pass:

  • baseUrl must be an http: or https: URL with no credentials, query string, or fragment. Anything else throws a TypeError.
  • timeout, maxWait, and pollInterval must each be a positive finite number of milliseconds no greater than 2147483647. Anything else throws a RangeError.

Numeric and HTTP-date Retry-After values from the service control the polling cadence; pollInterval is used only when the service does not send one. If the next poll would land past the maxWait deadline, the client gives up immediately rather than sleeping out the remaining budget first.

Redirects are rejected

The client sets redirect: "error". If you route traffic through a proxy that answers with a 3xx, point baseUrl at the final destination instead of relying on the redirect being followed.

Errors​

ThrownWhen
Pdf4meExceptionThe service returned a failure. Carries message, responseStatusCode, traceId, and responseHeaders.
Pdf4meTimeoutErrorA request time limit (timeout) or a job time limit (maxWait) was reached.
TypeErrorInvalid local input: a blank API key, a bad baseUrl, or empty document content.
RangeErrorA duration option outside the accepted range.
Ordinary ErrorA 202 whose body carried no usable jobId, or a JSON action that returned an empty result.
Network errorsPropagated from fetch unchanged.

Pdf4meException.message is taken from the service's own message field when present, falling back to the HTTP status text. Quote the traceId when you contact support: it identifies the exact job on the service side.

import { Pdf4meException, Pdf4meTimeoutError } from "pdf4me";
import { optimize } from "pdf4me/optimize";

try {
const result = await optimize(client, data, { docName: "input.pdf" });
} catch (error) {
if (error instanceof Pdf4meTimeoutError) {
// The job exceeded maxWait. Retry, or raise maxWait for large documents.
} else if (error instanceof Pdf4meException) {
console.error(error.responseStatusCode, error.traceId);
} else {
throw error;
}
}

The 18 modules​

Each module is its own import subpath, so bundlers and editors only load what you use. The package sets "sideEffects": false, which lets bundlers drop unused modules entirely.

A few actions sit in a module you might not guess from the name — OCR is in find-search, PDF/A creation is in convert, and pdf4me holds a single hyperlink action. The table below lists every exported action so you do not have to guess.

Import subpathActionsExported functions
pdf4me/ai-document-extraction14aiDocumentParser, processBankCheque, processBankStatement, processContract, processCreditCard, processHealthCard, processInvoice, processMarriageCertificate, processMortgageDocument, processOrder, processPayStub, processReceipt, processShippingLabel, processTaxDocument
pdf4me/barcode7addBarcode, createBarcode, createSwissQrBill, readBarcodes, readSwissQrBill, createEpcQrCode, readBarcodesFromImage
pdf4me/convert13convertHtmlToPdf, convertJsonToExcel, convertMdToPdf, convertPdfToExcel, convertPdfToPowerpoint, convertPdfToWord, convertToPdf, convertUrlToPdf, convertVisio, convertWordToPdfForm, createPdfA, flattenPdf, linearizePdf
pdf4me/edit7addAttachmentToPdf, addHtmlHeaderFooter, imageStamp, addMargin, addPageNumber, stamp, signPdf
pdf4me/excel1findAndReplaceTextInExcel
pdf4me/extract8classifyDocument, extractAttachmentFromPdf, extractPdfFormData, extractResources, extractTableFromPdf, extractTextByExpression, extractTextFromWord, parseDocument
pdf4me/find-search2findAndReplace, convertOcrPdf
pdf4me/forms2addFormField, fillPdfForm
pdf4me/generate6enableTrackingChangesInWord, generateDocumentSingle, generateDocumentMultiple, getTrackingChangesInWord, replaceTextWithImageInWord, generateDocumentSingleV2
pdf4me/image13addImageWatermarkToImage, addTextWatermarkToImage, compressImage, convertImageFormat, createImages, cropImage, flipImage, getImageMetadata, imageExtractText, removeExifTagsFromImage, resizeImage, rotateImage, rotateImageByExifData
pdf4me/merge-split5merge, mergeOverlay, splitPdf, splitPdfByBarcode, splitByText
pdf4me/optimize1optimize
pdf4me/organize5deleteBlankPages, deletePages, extractPages, rotate, rotatePage
pdf4me/pdf16getPdfMetadata, repairPdf, createHyperlinkAnnotation, deleteHyperlinkAnnotation, digitalSignPdf, extractHyperlinkAnnotation, getDocumentByText, getTextAndCoordinates, getTextByPosition, highlightText, mergeV3, prepareForPrint, replaceTextWithImage, resizePdf, setPdfMetadata, signDocument
pdf4me/pdf4me1updateHyperlinkAnnotation
pdf4me/security2protect, unlock
pdf4me/word2disableTrackingChangesInWord, findAndReplaceTextInWord
pdf4me/zugferd1createZugferdInvoice

Two more subpaths sit alongside the action modules: the package root (pdf4me), covered above, and pdf4me/models, which re-exports every model and enum constant.

For each action's parameters and response shape, see the PDF4me REST API reference or the generated type declarations in node_modules/pdf4me/dist/.

Upgrading from 9.x​

Version 10 is a rewrite. To stay on the 9.x line, pin pdf4me@^9.10.15.

Two changes break every call site​

You will find these immediately, because nothing compiles or runs until they are fixed.

1. ESM only. require("pdf4me") no longer resolves. Use import.

2. A client plus standalone actions. pdf4me.createClient(key), which returned a single object of methods, is replaced by new Pdf4meClient(key) plus action functions imported from subpaths and called as action(client, source, options).

// 9.x
const pdf4me = require("pdf4me");
const client = pdf4me.createClient(key);
const result = await client.optimize(/* ... */);

// 10
import { Pdf4meClient } from "pdf4me";
import { optimize } from "pdf4me/optimize";
const client = new Pdf4meClient(key);
const result = await optimize(client, data, { docName: "input.pdf" });

Three changes compile and run, but misbehave​

These are the ones to check by hand, because nothing will tell you they are wrong.

1. Binary results are Uint8Array, not Buffer. Buffer subclasses Uint8Array, so writeFile and friends still work. But Buffer-only methods quietly produce nonsense rather than throwing:

result.toString("base64"); // 9.x: base64. 10: "0,255,65,128"
Buffer.from(result).toString("base64"); // do this instead

2. Every action polls. Version 10 submits isAsync: true and polls until the job completes, so a call that was one round trip may now block for up to maxWait (default 300000 ms) and can throw Pdf4meTimeoutError — a failure mode 9.x did not have.

3. Redirects are rejected. 9.x followed them; version 10 sets redirect: "error". If you route traffic through a proxy that answers 3xx, point baseUrl at the final destination.

Also worth knowing​

  • Node.js 22 is now required and declared in engines, but npm only warns on a mismatch by default.
  • Thumbnail creation (createThumbnail / createThumbnails) and PDF/A validation (validate / validateDocument) have no version 10 equivalent, because the v2 API does not expose them.
  • The license changed from ISC to MIT.

The full method-by-method mapping, including renames and the integrationConfig replacement, ships with the package. Read it at node_modules/pdf4me/MIGRATION.md after installing.

Troubleshooting​

Error [ERR_REQUIRE_ESM] or require() of ES Module ... not supported. Version 10 is ESM only. Add "type": "module" to your package.json, rename the entry file to .mjs, or pin pdf4me@^9.10.15 if the project cannot move off CommonJS yet.

ERR_MODULE_NOT_FOUND for pdf4me/optimize. Subpath imports resolve through the package exports map, which requires a reasonably current Node.js and bundler. Confirm node --version reports 22 or newer, and that you are importing the subpath exactly as spelled in the modules table (for example pdf4me/merge-split, with a hyphen).

Base64 output looks like "0,255,65,128". You called .toString("base64") on a Uint8Array. Wrap it first: Buffer.from(result).toString("base64").

Pdf4meTimeoutError on large documents. The job exceeded maxWait, which defaults to 300000 ms. Raise it on the client, for example new Pdf4meClient(apiKey, { maxWait: 900_000 }), and confirm timeout is still large enough to read the response body. Note that both values are capped at 2147483647 ms.

A 401 in Pdf4meException.responseStatusCode. The API key is present but rejected. Regenerate it in the PDF4me dashboard and confirm the environment variable is actually visible to the process — set in Windows CMD does not persist across sessions, so use setx for a permanent value.

TypeError: an API key is required. The constructor received an empty or whitespace-only string, usually because process.env.PDF4ME_API_KEY was undefined. Check the variable before constructing the client, as the quick start does.

TypeError: document content is empty. The Uint8Array or string you passed has zero length. This is a local check, so no request was sent — verify the file actually read successfully.

Requests fail behind a corporate proxy. The client sets redirect: "error" and will not follow a proxy's 3xx. Point baseUrl at the final destination, or supply a custom fetch that handles your transport.

createThumbnail or validate is not exported. Both were removed in version 10, because the v2 API does not expose them. There is no drop-in replacement; stay on 9.x if you depend on them.

Where to go next​