Node.js File Conversion SDK#

@enconvert/node-sdk is the official JavaScript / TypeScript client for converting URLs, images, and documents through the EnConvert API. Thirteen typed methods map 1:1 to REST endpoints like POST /v1/convert/url-to-pdf, and every single-file conversion method returns a presigned download URL. It targets Node.js 18+ with zero runtime dependencies, built on native fetch, FormData, and node:stream, and transparently recovers from reverse-proxy timeouts by polling job status. Ships dual ESM + CJS builds and full TypeScript declarations.

npm: @enconvert/node-sdk · Source: enconvert/node-sdk · Node: 18+

Install#

npm install @enconvert/node-sdk
pnpm add @enconvert/node-sdk
yarn add @enconvert/node-sdk

Quick Start#

import { Enconvert } from "@enconvert/node-sdk";

const client = new Enconvert({ apiKey: process.env.ENCONVERT_API_KEY! });

const result = await client.convertUrlToPdf("https://example.com", {
    saveTo: "page.pdf",
});

console.log(result.presignedUrl);

The SDK works in every modern Node runtime (Node 18+, Bun, Deno via the npm specifier). It is server-side only, so do not bundle your private API key into a browser app.


Methods#

The client exposes thirteen methods that map 1:1 to the REST API:

Method Endpoint Returns
convertUrlToPdf(url, options?) POST /v1/convert/url-to-pdf ConversionResult
convertUrlToScreenshot(url, options?) POST /v1/convert/url-to-screenshot ConversionResult
convertUrlToMarkdown(url, options?) POST /v1/convert/url-to-markdown ConversionResult
convertImage(file, options) POST /v1/convert/{from}-to-{to} ConversionResult
convertDocument(file, options?) POST /v1/convert/{from}-to-{to} ConversionResult
compressImage(file, options?) POST /v1/convert/compress-image ConversionResult
convertToMarkdown(file, options?) POST /v1/convert/anything-to-markdown ConversionResult
convertToPdf(file, options?) POST /v1/convert/anything-to-pdf ConversionResult
getJobStatus(jobId) GET /v1/convert/status/{jobId} JobStatus
convertWebsiteToPdf(url, options?) POST /v1/convert/website-to-pdf BatchSubmission
convertWebsiteToScreenshot(url, options?) POST /v1/convert/website-to-screenshot BatchSubmission
getBatchStatus(batchId) GET /v1/convert/batch/{batchId} BatchStatus
waitForBatch(batchId, options?) GET /v1/convert/batch/{batchId} (polled) BatchStatus

Every method returns a typed promise. All option fields are optional unless marked otherwise. The last four are whole-site batch helpers: they submit and poll async jobs, so they return a BatchSubmission or a BatchStatus rather than a ConversionResult.


convertUrlToPdf#

Render any public URL to a PDF.

const result = await client.convertUrlToPdf("https://example.com", {
    pdfOptions: { pageSize: "A4", orientation: "landscape" },
    singlePage: false,
    viewportWidth: 1440,
    saveTo: "report.pdf",
});
Option Type Default Description
saveTo string -- Local path to stream the PDF to. Parent directories are created automatically.
singlePage boolean true true produces one continuous page. false paginates using pdfOptions.pageSize.
pdfOptions PdfOptions -- Page size, orientation, margins, scale, grayscale, header/footer. See PDF options.
viewportWidth number 1920 Browser viewport width in pixels.
viewportHeight number 1080 Browser viewport height in pixels.
loadMedia boolean true Wait for images and videos before capture.
enableScroll boolean true Scroll top-to-bottom to trigger lazy loaders.
outputFilename string auto Override the generated filename. .pdf is appended if missing.

convertUrlToScreenshot#

Capture a full-page PNG of any URL.

const result = await client.convertUrlToScreenshot("https://example.com", {
    viewportWidth: 1440,
    saveTo: "screenshot.png",
});

Accepts the same viewport, media, scroll, and filename options as convertUrlToPdf (minus singlePage and pdfOptions).


convertUrlToMarkdown#

Extract clean GitHub-Flavored Markdown from any URL. The converter strips navigation, footers, ads, and scripts, keeps the main article body, and prepends YAML frontmatter (title, description, url, links, images).

const result = await client.convertUrlToMarkdown("https://example.com/article", {
    saveTo: "article.md",
});

Useful for building RAG pipelines, importing third-party content into a CMS, or generating training data.


convertImage#

Convert between jpeg, png, svg, heic, and webp.

// From a path
await client.convertImage("photo.heic", {
    outputFormat: "webp",
    saveTo: "photo.webp",
});

// From bytes
import { readFile } from "node:fs/promises";
const buf = await readFile("photo.heic");

await client.convertImage(
    { data: buf, filename: "photo.heic" },
    { outputFormat: "webp", saveTo: "photo.webp" },
);

// Rasterize an SVG at a fixed width
await client.convertImage("logo.svg", { outputFormat: "png", width: 512, saveTo: "logo.png" });

The input format is detected from the path / filename extension. The output format is required.

Option Type Required Description
outputFormat "jpeg" \| "png" \| "svg" \| "heic" \| "webp" Yes Target format.
saveTo string -- Local path to stream the result to.
outputFilename string -- Override the generated filename.
width number -- SVG input only (svg-to-png, svg-to-jpeg, svg-to-webp), 1 to 10000. On its own it scales proportionally, taking the height from the SVG's aspect ratio.
height number -- SVG input only (svg-to-png, svg-to-jpeg, svg-to-webp), 1 to 10000. On its own it scales proportionally, taking the width from the SVG's aspect ratio.

Set both width and height to pin an exact canvas, which may change the aspect ratio. Omit both and the output keeps the SVG's intrinsic width, height, or viewBox. Total output pixels are capped at 25,000,000. Neither option is accepted by svg-to-heic, and the SDK throws before sending the request if you pass them to any other conversion.


convertDocument#

Convert documents and data formats. The default outputFormat is "pdf".

// docx -> pdf
await client.convertDocument("report.docx", { saveTo: "report.pdf" });

// json -> yaml
await client.convertDocument("data.json", {
    outputFormat: "yaml",
    saveTo: "data.yaml",
});

// markdown -> pdf with custom page setup
await client.convertDocument("README.md", {
    outputFormat: "pdf",
    pdfOptions: { pageSize: "A4", margins: { top: 20, bottom: 20, left: 25, right: 25 } },
    saveTo: "readme.pdf",
});

Supported inputs: doc, docx, xls, xlsx, ppt, pptx, html, htm, odt, ods, odp, ots, pages, numbers, markdown (.md, .markdown), csv, json, xml, yaml (.yaml, .yml), toml.

EPUB has no dedicated document conversion pair. Send .epub files through convertToPdf or convertToMarkdown instead.

Option Type Default Description
outputFormat string "pdf" Target format.
saveTo string -- Local path to stream the result to.
outputFilename string -- Override the generated filename.
pdfOptions PdfOptions -- Page setup. Only honored when output is PDF.

compressImage#

Shrink a PNG, JPEG, or WebP without changing its format.

// Lossless pass only
const result = await client.compressImage("photo.jpg", { saveTo: "photo-small.jpg" });

// Aim for a 200 KB budget
const capped = await client.compressImage("photo.jpg", {
    targetSizeKb: 200,
    saveTo: "photo-capped.jpg",
});

console.log(capped.fileSize);

Supported inputs: .png, .jpg, .jpeg, .webp.

The output keeps the input format and extension, so there is no output format to choose. The first stage is lossless: metadata is stripped, the ICC profile and EXIF orientation are preserved, and the result is never larger than the input. Setting targetSizeKb adds a second stage that downscales with the aspect ratio locked until the budget is met. That target is best effort: an unreachable budget returns the smallest file achieved instead of an error, so check result.fileSize. Animated APNG and animated WebP are rejected with 400, and the decoded canvas is capped at 40,000,000 pixels.

Option Type Required Description
targetSizeKb number -- Size budget in KB, integer, minimum 1. Omit it to run the lossless pass only.
saveTo string -- Local path to stream the result to.
outputFilename string -- Override the generated filename. The input extension is kept.

convertToMarkdown#

Convert any supported document, spreadsheet, presentation, ebook, web, or plain-text file to Markdown.

await client.convertToMarkdown("handbook.docx", {
    saveTo: "handbook.md",
});

Supported inputs (22): .csv, .doc, .docx, .epub, .htm, .html, .markdown, .md, .mdown, .mkd, .odp, .ods, .odt, .pdf, .ppt, .pptx, .rtf, .text, .txt, .xhtml, .xls, .xlsx.

The output is a single heading-aware .md file built for RAG chunking: the document's heading hierarchy survives the conversion, so a semantic chunker can split on headings instead of arbitrary character counts. There are no PDF options on this endpoint. Any other extension throws before a request is made.

Option Type Required Description
saveTo string -- Local path to stream the Markdown to.
outputFilename string -- Override the generated filename.

convertToPdf#

Convert any supported document, image, ebook, web, or plain-text file to PDF.

// docx -> pdf
await client.convertToPdf("contract.docx", { saveTo: "contract.pdf" });

// html -> pdf with full page geometry
await client.convertToPdf("invoice.html", {
    pdfOptions: { pageSize: "A4", margins: { top: 15, bottom: 15, left: 15, right: 15 } },
    saveTo: "invoice.pdf",
});

// pdf -> grayscale pdf (passthrough)
await client.convertToPdf("scan.pdf", {
    pdfOptions: { grayscale: true },
    saveTo: "scan-gray.pdf",
});

Supported inputs (36): .bmp, .csv, .doc, .docx, .epub, .gif, .heic, .heif, .htm, .html, .jpeg, .jpg, .markdown, .md, .mdown, .mkd, .numbers, .odp, .ods, .odt, .ots, .pages, .pdf, .png, .ppt, .pptx, .rtf, .svg, .text, .tif, .tiff, .txt, .webp, .xhtml, .xls, .xlsx.

A .pdf input is accepted and passed through, so with pdfOptions: { grayscale: true } this method doubles as a PDF normalize path. EPUB is handled here too, since it has no dedicated document conversion pair. Any other extension throws before a request is made.

Geometry is input-dependent. Full page geometry (page size, page width and height, orientation, margins, scale, header, footer) is honored for HTML (.html, .htm, .xhtml), Markdown, plain text, EPUB, image, and SVG input. Office, ODF, iWork, RTF, and CSV input plus PDF passthrough support grayscale only, and return 400 if an explicit geometry option is set. grayscale itself is honored for every input.
Option Type Required Description
saveTo string -- Local path to stream the PDF to.
outputFilename string -- Override the generated filename. .pdf is appended if missing.
pdfOptions PdfOptions -- Page setup. See the caveat above for which inputs honor geometry.

getJobStatus#

Poll the status of an async or recovered job.

const status = await client.getJobStatus("job_abc123");

if (status.status === "success") {
    console.log(status.presignedUrl);
} else if (status.status === "failed") {
    console.error(status.error);
}

Returns { status: "processing" | "success" | "failed", presignedUrl?, objectKey?, error? }.

You usually do not need to call this directly. The SDK polls automatically when a sync request returns 5xx. See Timeout recovery below.

PDF Options#

Passed via the pdfOptions field on convertUrlToPdf, convertDocument, and convertToPdf.

const result = await client.convertUrlToPdf("https://example.com", {
    pdfOptions: {
        pageSize: "A4",
        orientation: "landscape",
        margins: { top: 10, bottom: 10, left: 15, right: 15 },
        scale: 0.9,
        grayscale: false,
    },
    saveTo: "report.pdf",
});
Field Type Description
pageSize string "A4", "A3", "Letter", "Legal", etc.
orientation "portrait" \| "landscape" Defaults to portrait.
margins { top, bottom, left, right } (mm) All four are optional.
scale number Render scale, e.g. 0.9 for 90%.
grayscale boolean Post-process the PDF through Ghostscript to grayscale.
header Record<string, string> Header text per page region.
footer Record<string, string> Footer text per page region.

Error handling#

Errors are typed exception classes that you can match with instanceof.

import {
    Enconvert,
    APIError,
    AuthenticationError,
    RateLimitError,
} from "@enconvert/node-sdk";

try {
    await client.convertUrlToPdf("https://example.com");
} catch (e) {
    if (e instanceof AuthenticationError) {
        console.error("Invalid API key. Check ENCONVERT_API_KEY.");
    } else if (e instanceof RateLimitError) {
        console.error("Hit your monthly quota or per-second rate limit.");
    } else if (e instanceof APIError) {
        console.error(`API error [${e.statusCode}]: ${e.message}`);
    } else {
        throw e;
    }
}
Class Thrown on Status code
AuthenticationError Invalid, missing, or revoked key 401, 403
RateLimitError Quota or rate limit exceeded 429
APIError Any other 4xx / 5xx the actual code
EnconvertError Base class for all of the above --

The full error message map is in the Error Codes reference.


Timeout recovery#

Long URL-to-PDF or large document conversions can exceed the 60-120 second reverse-proxy timeout limit, even when the conversion eventually succeeds on the server. The SDK handles this transparently:

  1. Before each request, the SDK generates a UUID and sends it as job_id in the request body.
  2. If the original request returns 5xx, the SDK silently switches to polling GET /v1/convert/status/{job_id} every 3 seconds.
  3. As soon as the job is recorded as success, the SDK returns the result. As soon as it is recorded as failed, the SDK throws APIError.
  4. Polling deadline is 5 minutes. If exceeded, the SDK throws APIError(504, "Conversion timed out").

You don't need to write any code for this, it just works. Set timeout on the constructor if you want to bound the initial request.


Configuration#

const client = new Enconvert({
    apiKey: process.env.ENCONVERT_API_KEY!,
    timeout: 300_000, // ms, 5 min default
    baseUrl: "https://api.enconvert.com", // override for self-hosted gateways
});
Option Type Default Description
apiKey string -- (required) Private API key (sk_live_...).
timeout number 300_000 Request timeout in ms. Aborts the underlying fetch via AbortController.
baseUrl string https://api.enconvert.com API base URL. Trailing slashes are stripped.
Never hardcode the API key. Read it from an environment variable or your secret manager. Anyone who gets your private key can run conversions against your project's quota.

Result shape#

Every conversion method returns a ConversionResult:

interface ConversionResult {
    presignedUrl: string;          // signed URL to download the output (1 hour)
    objectKey: string;             // storage object key
    filename: string;              // server-side filename
    fileSize?: number;             // bytes
    conversionTimeSeconds?: number;
    jobId?: string;                // present when timeout recovery polled
}

The presigned URL is valid for one hour. If you need permanent access, download the file (use saveTo, or fetch the URL yourself) and store it in your own bucket.


TypeScript#

Type definitions ship with the package, so no @types/... install is needed. The package is dual-published (ESM + CJS) with proper exports, types, and .d.ts / .d.cts so it works under any Node module resolution mode.

import type {
    ClientOptions,
    CompressImageOptions,
    ConversionResult,
    ConvertDocumentOptions,
    ConvertImageOptions,
    ConvertToMarkdownOptions,
    ConvertToPdfOptions,
    FileInput,
    JobStatus,
    PdfOptions,
    UrlToMarkdownOptions,
    UrlToPdfOptions,
    UrlToScreenshotOptions,
} from "@enconvert/node-sdk";

Upgrading#

The package ships a small CLI, enconvert-sdk, for keeping itself current.

npx enconvert-sdk upgrade
npx enconvert-sdk upgrade --dry-run
npx enconvert-sdk version

upgrade detects npm, pnpm, yarn, or bun from the ambient package manager and always prints the exact install command before running it, so nothing happens to your lockfile unseen. --dry-run prints that command and stops. version reports the installed SDK version.


Source and issues#


Frequently asked questions#

How do I convert files in Node.js with an npm package?#

Install @enconvert/node-sdk, create a client with your API key (new Enconvert({ apiKey: process.env.ENCONVERT_API_KEY! })), and call a typed method like convertUrlToPdf, convertImage, or convertDocument. Pass saveTo to stream the result straight to disk.

How do I convert HEIC to WebP in Node.js?#

Call convertImage with the HEIC file (a path or a { data, filename } buffer object) and outputFormat: "webp". The SDK converts between jpeg, png, svg, heic, and webp; input format is detected from the filename extension.

How do I compress an image in Node.js without changing its format?#

Call compressImage with a .png, .jpg, .jpeg, or .webp file. The output keeps the input format and extension, strips metadata while preserving the ICC profile and EXIF orientation, and is never larger than the input. Add targetSizeKb to downscale toward a size budget; the target is best effort, so read result.fileSize to see what was actually achieved.

How do I convert any document to Markdown for a RAG pipeline?#

Call convertToMarkdown with the file and pass saveTo to write the .md straight to disk. It accepts 22 extensions across Office, OpenDocument, PDF, EPUB, HTML, CSV, and plain text, and returns one heading-aware Markdown file, so your chunker can split on the document's own headings instead of arbitrary character counts.

How does the SDK handle long conversions that hit the reverse-proxy timeout?#

Before each request the SDK generates a UUID and sends it as job_id; if the request returns 5xx, it silently polls GET /v1/convert/status/{job_id} every 3 seconds until the job is success or failed. The polling deadline is 5 minutes, after which it throws APIError(504, "Conversion timed out").

Can I use the Node.js SDK in a browser app?#

No, the SDK is server-side only, because it authenticates with a private API key (sk_live_...) that must never be bundled into client-side code. It works in Node 18+, Bun, and Deno via the npm specifier.

How long is the presigned download URL valid?#

The presignedUrl in every ConversionResult is valid for one hour. For permanent access, download the file (use saveTo, or fetch the URL yourself) and store it in your own bucket.