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.
@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.
.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? }.
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:
- Before each request, the SDK generates a UUID and sends it as
job_idin the request body. - If the original request returns 5xx, the SDK silently switches to polling
GET /v1/convert/status/{job_id}every 3 seconds. - As soon as the job is recorded as
success, the SDK returns the result. As soon as it is recorded asfailed, the SDK throwsAPIError. - 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. |
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#
- npm: @enconvert/node-sdk
- GitHub: enconvert/node-sdk
- License: MIT
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.