The SDK
@roundertools/sdk gives Node.js the tools from roundertools.com as typed functions. It's the site's own code, bundled for Node, so results match what the site makes. Everything runs in your process; there are no API keys and no network requests.
npm install @roundertools/sdkIt needs Node 22.13 or newer. Two things are optional: @napi-rs/canvas (for PDF to JPG and for GIF, BMP or SVG input) and an ffmpeg on your PATH, or at ROUNDERTOOLS_FFMPEG, for video and audio.
Basics
import { pdf, image } from '@roundertools/sdk';
// paths, file: URLs, bytes or Blobs all work as input
const small = await pdf.compress('report.pdf', { target: '1mb' });
console.log(small.size, small.meta.fits); // 412345 true
await small.save(); // ./report-compressed.pdf, never over an existing file
const webp = await image.convert('photo.png', { to: 'webp', quality: 80 });
await webp.save('out/'); // out/photo.webpEach function returns a FileOutput (or a list of them, or text): data (the bytes),name, mime, size, meta (details like page counts, pixel sizes or whether a target was met) and warnings. save(path?) writes it atomically and refuses to replace an existing file unless you pass { overwrite: true }.
Sizes are decimal, like the site's targets: "100kb" is 100,000 bytes. Use "100KiB" for 102,400.
Every tool by name
import { run, describe, ops } from '@roundertools/sdk';
// any tool by its slug, including the site's preset pages
const r = await run('compress-pdf-to-100kb', ['scan.pdf']);
await r.files[0].save();
describe('compress-pdf').options; // JSON Schema of its options
ops.map((o) => o.slug); // every tool the SDK hasProgress and cancelling
const controller = new AbortController();
const out = await pdf.compress('big.pdf', { level: 'strong' }, {
signal: controller.signal,
onProgress: (p) => console.log(p.stage, p.done, p.total),
});Errors
Everything throws a RounderError with a plain-English message, an optional hint, and a code: needs-password, wrong-password, damaged,unsupported-input, unsupported-codec, missing-dependency,invalid-options, out-of-memory, aborted, exists or failed.
import { RounderError } from '@roundertools/sdk';
try {
await pdf.unlock('statement.pdf');
} catch (e) {
if (e instanceof RounderError && e.code === 'needs-password') {
// ask for it, then: pdf.unlock('statement.pdf', { password })
}
}The functions
pdf.merge(files, options?): Combine PDFs (and JPG or PNG images) into one PDF, in the order given.pdf.split(file, options?): Cut a PDF into several files: by page ranges, every N pages, or parts under a size limit.pdf.compress(file, options?): Shrink a PDF: downscale and recompress its photos, then repack the file losslessly, or hit a size target.pdf.protect(file, options?): Lock a PDF with a password (AES-256), and optionally stop printing, copying or editing.pdf.unlock(file, options?): Remove the password and restrictions from a PDF you can open.pdf.organize(file, options?): Reorder, rotate and delete pages of a PDF.pdf.addPageNumbers(file, options?): Stamp page numbers on a PDF, in any corner, with "Page 1 of N" style labels.pdf.watermark(file, options?): Stamp text like CONFIDENTIAL across the pages of a PDF.pdf.fromImages(files, options?): Turn photos and images (JPG, PNG, WebP, AVIF) into one PDF, one image per page.pdf.toText(file, options?): Pull the text out of a PDF, in reading order, as plain text or Markdown.pdf.toImages(file, options?): Save each PDF page as a JPG or PNG image.pdf.extractImages(file, options?): Save the photos and pictures inside a PDF as the original JPGs, or as PNGs.
image
image.convert(file, options?): Convert images between JPG, PNG, WebP and AVIF.image.compress(file, options?): Make images smaller: re-encode at a lower quality, or hit a size target.image.resize(file, options?): Resize images by pixels, by percent, or down to a file size.image.crop(file, options?): Crop an image to a box in pixels, or to an aspect ratio from the center.image.rotate(file, options?): Turn images by 90, 180 or 270 degrees, or flip them.image.stripMetadata(file, options?): Remove EXIF, GPS location, XMP and other metadata from JPG, PNG and WebP, without re-encoding.
files
files.zip(files, options?): Pack files into one ZIP archive.files.unzip(file, options?): Extract the files from a ZIP archive, safely: no paths outside the output folder, no symlinks, size caps.files.checksum(file, options?): MD5, SHA-1, SHA-256, SHA-384, SHA-512 and CRC32 of a file, and a check against an expected value.files.info(file, options?): What a file is: PDF pages, encryption and metadata; image size and format; video and audio streams and length.
media
media.compress(file, options?): Shrink a video to a size target (like 25mb for Discord or email) or by a quality preset.media.convert(file, options?): Convert videos to MP4, WebM, MOV or MKV, or pull the sound out as MP3, M4A or WAV.media.trim(file, options?): Cut a video down to the part between two times.media.mute(file, options?): Remove the sound from a video, without re-encoding the picture.media.toGif(file, options?): Turn a video clip into an animated GIF with a per-clip palette.media.toAudio(file, options?): Save the soundtrack of a video as MP3, M4A, OGG, WAV or FLAC.media.convertAudio(file, options?): Convert audio between MP3, M4A, WAV, OGG and FLAC.
The options of each are on the tool table, and your editor shows them as you type.