npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@api2convert/sdk

v10.3.1

Published

Official Node.js/TypeScript SDK for the API2Convert file-conversion API. Convert, compress and transform images, documents, audio, video, ebooks, archives and CAD with one line of code.

Readme

API2Convert Node.js SDK

CI npm Node License

The official Node.js/TypeScript client for the API2Convert file-conversion API. Convert, compress and transform images, documents, audio, video, ebooks, archives and CAD — and run operations like OCR, merge, thumbnail and website capture — in one line of code.

import { Api2Convert } from '@api2convert/sdk';

const client = new Api2Convert('YOUR_API_KEY');

const result = await client.convert('invoice.docx', 'pdf');
await result.save('invoice.pdf');

That single call creates a job, uploads your file, starts it, waits for it to finish and gives you back a result you can save. No polling loops, no manual upload handling.

Requirements

  • Node.js 18+ (uses the built-in global fetch, Blob, FormData and crypto).
  • Zero runtime dependencies. The HTTP layer is the platform fetch (undici); HMAC uses node:crypto. Ships as dual ESM + CommonJS with TypeScript types.

Install

npm install @api2convert/sdk

Get an API key from the API2Convert dashboard / documentation.

Quick start

import { Api2Convert } from '@api2convert/sdk';

// Reads the API2CONVERT_API_KEY environment variable when no key is passed.
const client = new Api2Convert('YOUR_API_KEY');

// 1) From a local file
await (await client.convert('photo.png', 'jpg')).save('photo.jpg');

// 2) From a URL
await (await client.convert('https://example.com/photo.png', 'jpg')).save('photo.jpg');

// 3) With conversion options (discover them via client.options('jpg'))
const result = await client.convert('photo.png', 'jpg', { quality: 85, width: 1280, height: 720 });
await result.save('out/'); // the processed-file directory

convert(input, to, options?, opts?)input is a local path string, a public URL, a Uint8Array/Buffer, a Node Readable, or a web ReadableStream; to is the target format; options are the conversion options for that target. Less-common controls live on the opts object (so they can never collide with an open-ended API option): category, timeout, outputIndex, filename, downloadPassword. The returned ConversionResult lets you:

const result = await client.convert('report.docx', 'pdf');

await result.save('report.pdf'); // stream to a file
await result.save('downloads/'); // ...or a directory (keeps the server filename)
const bytes = await result.contents(); // ...or get the raw Buffer
const url = result.url(); // ...or just the download URL

Password-protect the result

Pass a downloadPassword and the output is locked behind it. The SDK remembers the password and sends it automatically when you download — you don't pass it again:

const result = await client.convert('statement.docx', 'pdf', null, { downloadPassword: 'hunter2' });
await result.save('statement.pdf'); // the password is applied for you

The download URL still needs the password from anywhere else (a browser, cURL, another process), via the X-Api2convert-Download-Password header. When you already hold an OutputFile — e.g. from the Jobs API — hand the password to download():

await client.download(output, 'hunter2').save('out/');

Asynchronous conversions & webhooks

For long-running jobs, start the conversion and get notified via a webhook instead of waiting:

const job = await client.convertAsync('movie.mov', 'mp4', null, {
  callback: 'https://your-app.example.com/webhooks/api2convert',
});

In your webhook handler, verify and parse the callback:

import { Api2Convert, SignatureVerificationError } from '@api2convert/sdk';

const payload = rawBody; // the RAW request body (string or Buffer)
const signature = req.headers['x-oc-signature'];

try {
  const event = Api2Convert.webhooks().constructEvent(payload, signature, 'YOUR_WEBHOOK_SECRET');
  const job = event.job;
  // ... react to job.status.code ...
} catch (err) {
  if (err instanceof SignatureVerificationError) {
    // respond 400
  }
}

Signed webhooks are being rolled out. Until they are enabled for your account no signature is sent — call Api2Convert.webhooks().parse(payload) (or pass an empty secret) to deserialize the callback without verifying.

Cloud storage

Read an input straight from your own S3/Azure/FTP/Google Cloud storage, and/or deliver the converted output into a bucket — no need to route bytes through your app. Credentials ride in the request body, so the SDK masks the whole credentials object to [REDACTED] on inspection/logging and never puts it in an error message.

Read the input from Amazon S3 and save the result locally (per-provider factory; flat, lowercase keys):

import { Api2Convert, CloudInput } from '@api2convert/sdk';

const input = CloudInput.amazonS3('my-bucket', 'invoices/march.docx', 'AKIA…', 'wJalr…');
const result = await client.convert(input, 'pdf');
await result.save('march.pdf');

Deliver the output into a bucket via an OutputTarget and the outputTargets option — when an output target is set the conversion delivers straight to your storage and there's no local download, so convert() returns the completed job:

import { OutputTarget, CloudProvider } from '@api2convert/sdk';

const target = OutputTarget.of(
  CloudProvider.AmazonS3,
  { bucket: 'my-bucket', file: 'out/report.pdf' },
  { accesskeyid: 'AKIA…', secretaccesskey: 'wJalr…' },
);
await client.convert('report.docx', 'pdf', null, { outputTargets: [target] });

azure, ftp and googleCloud have matching CloudInput input factories; output uses the generic OutputTarget for every provider.

Error handling

Every failure is an exception extending Api2ConvertError:

import {
  AuthenticationError,
  ConversionFailedError,
  RateLimitError,
  ValidationError,
} from '@api2convert/sdk';

try {
  await (await client.convert('photo.png', 'jpg')).save('photo.jpg');
} catch (err) {
  if (err instanceof ValidationError) {
    // bad target / option — err.message explains
  } else if (err instanceof AuthenticationError) {
    // bad or missing API key
  } else if (err instanceof RateLimitError) {
    // too many requests — retry after err.retryAfter seconds
  } else if (err instanceof ConversionFailedError) {
    // the job failed — inspect err.errors()
  }
}

| Error | When | | ---------------------------- | -------------------------------------------------------- | | AuthenticationError | 401 / 403 — bad or missing key | | PaymentRequiredError | 402 — no remaining quota | | ValidationError | 400 / 422 — invalid request (e.g. unknown target) | | NotFoundError | 404 — resource doesn't exist | | RateLimitError | 429 — exposes retryAfter | | ServerError | 5xx | | ConversionFailedError | the job reached failed; exposes .job and .errors() | | ConversionTimeoutError | the job didn't finish within the poll timeout | | NetworkError | a transport failure / non-JSON response / malformed URL | | SignatureVerificationError | a webhook payload failed verification |

Transient failures (429, 5xx, network errors) are retried automatically with jittered exponential backoff. A non-idempotent POST (e.g. creating a job) is never blindly retried, so a transient error can't create a duplicate job — pass an idempotency key to make it retry-safe: client.jobs().create(payload, 'my-idempotency-key').

Power user: the full job API

convert() is sugar over the Jobs API. Drop down to it for compound jobs, merges, presets, custom polling or job chaining:

const job = await client.jobs().create({
  process: false,
  conversion: [{ target: 'pdf', options: { pdf_a: true } }],
});

await client.jobs().upload(job, 'contract.docx'); // local file
await client.jobs().addInput(job.id, {
  type: 'remote',
  source: 'https://example.com/appendix.docx',
});

await client.jobs().start(job.id);
const done = await client.jobs().wait(job.id, 120); // poll to completion (120s timeout)

for (const output of done.output) {
  await client.download(output).save('out/');
}

Available resources: jobs(), conversions() (the catalog + option discovery), presets(), stats(), contracts().

Discover the valid options for any target:

const options = await client.options('jpg'); // -> { quality: {...}, width: {...}, ... }

Configuration

const client = new Api2Convert('YOUR_API_KEY', {
  timeout: 30, // per-request network timeout (seconds)
  maxRetries: 2, // automatic retries for transient failures
  pollInterval: 1.0, // first poll interval when waiting (seconds)
  pollMaxInterval: 5.0, // backoff cap (seconds)
  pollTimeout: 300, // give up waiting after this many seconds
});

Bring your own HTTP transport by implementing HttpSender and passing it as httpSender, or supply a custom fetch via new FetchHttpSender(myFetch).

Security — never publish your API key

  • Never hard-code or commit your API key. Load it from the environment (API2CONVERT_API_KEY) or a secrets manager.
  • In CI, store it as a masked & protected secret and never print it to logs.
  • Treat the per-job upload token and your webhook signing secret with the same care.
  • The SDK never logs your key/token and never puts them in exception messages. A request carrying any secret header never follows a redirect (a redirect could otherwise forward the secret to another host); only the self-contained, no-secret download path follows redirects.
  • If a key is ever exposed, revoke and rotate it in the API2Convert dashboard immediately.

See SECURITY.md.

Development

npm run check          # lint + typecheck + unit tests + security suite + audit
npm test               # offline unit tests only
npm run test:security  # the independent security suite (real loopback servers)
npm run build          # emit dual ESM + CJS + type declarations to dist/

Live conformance tests run against the real API when API2CONVERT_API_KEY is set (they auto-skip otherwise):

API2CONVERT_API_KEY=... npm run test:live

The live conformance suite doubles as an executable, end-to-end tour of the SDK: it runs each documented example against the real API and asserts success, plus two negative scenarios (an unknown target is a typed validation error; a bad key is a typed authentication error that never leaks the key). It runs automatically against the real API on every release tag (see .github/workflows/live-conformance.yml), so a published version is always verified end to end.

Every guide has a matching runnable program in examples/. Run one with a real key:

API2CONVERT_API_KEY=... npx tsx examples/quickstart.ts

| Example | What it shows | | ------------------------------------------------------- | ---------------------------------------------------------------- | | quickstart.ts | Convert a remote JPG to PNG, fetch the job, download the output. | | convert-files.ts | Browse the conversions catalog, then convert. | | uploading-files.ts | Upload a local file and convert it in one call. | | job-lifecycle.ts | Drive create → add input → start → wait → outputs by hand. | | add-watermark.ts | Stamp a PNG onto a PDF (a two-input job). | | create-thumbnails.ts | Render the first PDF page to a PNG thumbnail. | | compress-files.ts | Compress a JPG with the compress operation. | | create-archives.ts | Bundle a PDF and a PNG into a ZIP. | | create-hashes.ts | Compute the SHA-256 of a file. | | extract-assets.ts | Extract embedded assets from a DOCX. | | file-analysis.ts | Read a file's metadata as JSON. | | compare-files.ts | Diff two images with SSIM. | | capture-website.ts | Screenshot a URL to PNG. | | audio-operations.ts | Transcode a WAV to AAC with explicit codec settings. | | image-operations.ts | Resize an image, cropping to keep aspect ratio. | | webhooks.ts | Start an async job with a callback URL and verify the callback. | | presets.ts | List saved conversion presets. | | statistics.ts | Read API usage for a month. | | rate-limits.ts | Read the account's contract/quota information. | | authentication.ts | Verify your API key by listing your jobs. |

This SDK is hand-written and kept in sync with the API by an AI agent — see AGENTS.md and docs/SDK_CONTRACT.md. Notable changes are recorded in docs/CHANGELOG.md.

License

MIT — see LICENSE.