Skip to main content
PPDF123

@pdf123/sdk: the PDF123 TypeScript SDK

@pdf123/sdk is the TypeScript SDK for the PDF123 PDF API. It runs 95 tools such as merge, split, compress and OCR, types the options of each tool, and adds batches, pipelines, password handling and typed errors. It is an ES module with no runtime dependencies.

@pdf123/sdkNode 20.3 or newer, or Bun

Install

npm install @pdf123/sdk
On this page

How do I install the SDK?

Install the package with your package manager. Node 20.3 or newer, Bun or a bundler is required. TypeScript types are included and @types/node is not needed.

Installbash
npm install @pdf123/sdk

bun add @pdf123/sdk and pnpm add @pdf123/sdk work the same way.

How do I merge two PDFs?

Create a client, run the merge tool on two files and save the result. With no options the client talks to the public API anonymously.

Merge and savets
import { Pdf123Client } from "@pdf123/sdk";
import { readFileInput, saveResult } from "@pdf123/sdk/node";

const client = new Pdf123Client();
const result = await client.run("merge", {
  input: [await readFileInput("a.pdf"), await readFileInput("b.pdf")],
});
const path = await saveResult(result, { output: "merged.pdf" });
console.log(path);

The result holds the bytes in data, the contentType, the server's filename, and json for tools that return a report. The same operation is available as Merge PDF on the website.

Which entry point should I import?

Entry pointNeedsProvides
@pdf123/sdkOnly fetchPdf123Client, TOOLS, getTool, Pdf123Error and the catalog helpers
@pdf123/sdk/nodeNode or BunreadFileInput, fileSource, saveResult, checkOutput and clientFromEnv

Import from the root entry in browsers and edge runtimes, and add the node entry only where you read or write files.

Which methods does the client have?

MethodWhat it does
run(tool, { input, params })Runs one tool and resolves with a result
runBatch(tool, inputs, options)Runs a single-file tool on many inputs and resolves with one entry per input
pipeline(steps, input, options)Runs several tools in one request
call(target, files, fields)Sends a raw request to a tool id, an operation id or an /api/... path
upload(file)Uploads one file through the chunked upload API and returns its id

Catalog helpers are plain functions: TOOLS lists all tools, getTool(id) returns the fields, defaults and accepted file types of one tool, and toolGroup, toolSummary, matchesQuery and suggestTools help build tool pickers. The command line prints the same catalog with pdfx list and pdfx describe.

How do I pass options to a tool?

params is typed per tool. It accepts only that tool's fields, and choice fields accept only their allowed values. Number fields are checked against their minimum and maximum, and a file is checked against the accepted types, before anything is uploaded. Defaults that the server needs are filled in.

Watermark with optionsts
const marked = await client.run("watermark", {
  input: await readFileInput("a.pdf"),
  params: { watermarkText: "DRAFT", fontSize: 40, rotation: -45 },
});
await saveResult(marked, { output: "draft.pdf" });

An empty string means "not set", so the default applies. See Watermark PDF for what each option does.

How do I process many files?

runBatch runs a single-file tool, such as Compress, Rotate or Protect, on each input with the same options. It runs two files at a time by default. One bad file never stops the others.

Batch with cancellationts
import { fileSource, saveResult } from "@pdf123/sdk/node";

const controller = new AbortController();
const entries = await client.runBatch("compress", [fileSource("a.pdf"), fileSource("b.pdf")], {
  concurrency: 2,
  signal: controller.signal,
  onResult: async (entry, index) => {
    if (entry.ok) await saveResult(entry.result, { output: "compressed/" });
  },
});
console.log(entries.map((entry) => entry.ok));
  • fileSource(path) is a lazy source that is read only when a worker takes it, so many large files never sit in memory together.
  • onResult receives each result as it completes. Save it there, and a cancel at file 50 of 100 keeps the first 49. With retainData: false the returned array does not keep the bytes.
  • Any call accepts timeoutMs and a signal to cancel it.
  • idempotencyKey becomes <key>:<index> for each file.

Filter tools (ids starting with filter-) resolve with matched: false instead of throwing when their condition does not hold. In a batch such a file still counts as ok.

How do I chain tools in one request?

pipeline sends a list of steps and one or more inputs. The server feeds the output of each step into the next.

Merge, watermark, compressts
const piped = await client.pipeline(
  [{ tool: "merge" }, { tool: "watermark", params: { watermarkText: "DRAFT" } }, { tool: "compress" }],
  [await readFileInput("a.pdf"), await readFileInput("b.pdf")],
);
await saveResult(piped, { output: "out.pdf" });

Pipelines accept the same password options as run.

How do I work with encrypted PDFs?

Pass password to run to open an encrypted input first. The unlock and the tool run in one request. For tools that take several files, such as Merge, pass passwords with one entry per input, using an empty string for an open file. Each file is unlocked separately.

Passwordsts
await client.run("compress", { input: await readFileInput("locked.pdf"), password: "secret" });
await client.run("merge", {
  input: [await readFileInput("locked.pdf"), await readFileInput("open.pdf")],
  passwords: ["secret", ""],
});

For a batch, passwordFor(file, index) returns the password for each input. To remove protection permanently, use the Unlock tool.

How do errors work?

Failures throw Pdf123Error. It has status (undefined when there was no response), code, reason for the fine-grained cause such as password_required, and problem with the server's problem details, which include hint when there is one. Validation errors are thrown before anything is uploaded.

Handle an errorts
import { Pdf123Error } from "@pdf123/sdk";

try {
  await client.run("compress", { input: await readFileInput("locked.pdf") });
} catch (error) {
  if (error instanceof Pdf123Error) {
    console.error(error.status, error.code, error.reason, error.problem?.hint);
  } else {
    throw error;
  }
}

Server codes are listed on Error codes. The client adds these codes of its own:

CodeMeaning
network_errorThe request got no response
timeoutA request exceeded timeoutMs
cancelledYour signal aborted the call
input_unreadableA local path could not be read
output_unwritableThe result could not be saved at the given path
output_mismatchsaveResult refused to write a ZIP to a .pdf name
unsupported_file_typeThe file type is not accepted by the tool
unknown_toolThe tool id does not exist; the message suggests close ids
invalid_targetcall refused an /api/... path with .., . or empty segments
no_contentThe tool had nothing to return (HTTP 204)

saveResult never overwrites a file inside a directory. Call checkOutput(output, { several }) first to find out before uploading whether a path can be written.

Which TypeScript and module settings work?

The types resolve under the moduleResolution settings nodenext, node16, bundler and the older node10, including the @pdf123/sdk/node subpath. The package is an ES module, so import works everywhere. require("@pdf123/sdk") from CommonJS works on Node 22.12 or newer and is not available on Node 20.

How do I configure the client?

OptionEnvironment variableDefault
baseUrlPDFX_API_BASEhttps://pdf123.xyz
apiKeyPDFX_API_KEYnone, anonymous
timeoutMsnone300000

new Pdf123Client() never reads the environment. clientFromEnv() from @pdf123/sdk/node reads the two variables, and options you pass win. The key is sent as the X-API-KEY header; see Authentication.

To use your own server, set baseUrl to its address, for example http://localhost:8080. See Self-host.

Can I use the SDK in a browser?

Yes. The root entry @pdf123/sdk only needs fetch and has no Node dependencies. Build FileInput objects from a File or a Uint8Array yourself, because the file helpers live in the node entry. Do not ship an API key in browser code that other people can read.

FAQ

Does the SDK need an API key?

No. A client created without options calls the public API anonymously. Pass apiKey to send the X-API-KEY header.

Which Node version does the SDK need?

Node 20.3 or newer, or Bun. The root entry only needs fetch, so it also runs in browsers and bundlers.

How do I get a tool that is newer than the SDK?

Use client.call. It takes a tool id, an operation id such as general/merge-pdfs or an /api/... path, plus files and fields. Nothing is validated or defaulted locally.

How large a file can I upload?

Inputs over 95 MB in total go through the chunked upload API automatically. A single direct request body is limited to 100 MiB.

Why did my call throw for a PDF with no tables?

Tools such as pdf-to-csv and pdf-to-xlsx answer with no content when the PDF has no detectable table. The SDK throws Pdf123Error with the code no_content instead of returning an empty file. Check that code if an empty result is acceptable.