PDF to EPUB with Node.js and TypeScript — API Integration Guide
Automate PDF-to-EPUB conversion in your Node.js or TypeScript project using the toolkit.bot API. Here's a complete integration guide with async/await patterns, TypeScript types, and production patterns.
Install and Setup
npm install node-fetch form-data
# Or with the official SDK
npm install @toolkit-bot/sdk
// Set your API key
export TOOLKIT_API_KEY="tk_live_your_key_here"
Basic Conversion — TypeScript
import fs from 'fs';
import FormData from 'form-data';
import fetch from 'node-fetch';
const API_BASE = 'https://toolkit.bot/api/v1';
const API_KEY = process.env.TOOLKIT_API_KEY!;
interface JobResponse {
job_id: string;
status: 'queued' | 'processing' | 'complete' | 'failed';
download_url?: string;
progress?: number;
}
async function convertPdfToEpub(pdfPath: string): Promise<Buffer> {
// 1. Submit job
const form = new FormData();
form.append('file', fs.createReadStream(pdfPath));
form.append('output_format', 'epub3');
const submitRes = await fetch(`${API_BASE}/convert`, {
method: 'POST',
headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
body: form,
});
if (!submitRes.ok) throw new Error(`Submit failed: ${submitRes.status}`);
const { job_id } = await submitRes.json() as JobResponse;
// 2. Poll until complete
let job: JobResponse;
do {
await new Promise(r => setTimeout(r, 2000));
const pollRes = await fetch(`${API_BASE}/jobs/${job_id}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
job = await pollRes.json() as JobResponse;
} while (job.status === 'queued' || job.status === 'processing');
if (job.status === 'failed') throw new Error('Conversion failed');
// 3. Download result
const dlRes = await fetch(job.download_url!, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
return Buffer.from(await dlRes.arrayBuffer());
}
// Usage
const epub = await convertPdfToEpub('./document.pdf');
fs.writeFileSync('./document.epub', epub);
console.log('Done:', epub.length, 'bytes');
Batch Conversion with Concurrency Limit
import { promisePool } from './utils'; // or use p-limit
async function batchConvert(
pdfPaths: string[],
concurrency = 3
): Promise<void> {
const limit = pLimit(concurrency); // npm install p-limit
const tasks = pdfPaths.map(pdf =>
limit(async () => {
const epub = await convertPdfToEpub(pdf);
const out = pdf.replace(/[.]pdf$/i, '.epub');
fs.writeFileSync(out, epub);
console.log('Converted:', out);
})
);
await Promise.all(tasks);
}
await batchConvert(
fs.readdirSync('./pdfs').map(f => `./pdfs/${f}`)
);
Webhook Handler (Express)
import express from 'express';
const app = express();
app.use(express.json());
app.post('/hooks/epub-done', async (req, res) => {
const { event, job_id, download_url } = req.body;
if (event !== 'job.complete') return res.sendStatus(200);
// Download and save
const dlRes = await fetch(download_url, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const buffer = Buffer.from(await dlRes.arrayBuffer());
fs.writeFileSync(`./output/${job_id}.epub`, buffer);
console.log('Saved:', job_id);
res.sendStatus(200);
});
app.listen(3000);
Error Handling and Retry
async function convertWithRetry(
pdfPath: string,
maxRetries = 3
): Promise<Buffer> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await convertPdfToEpub(pdfPath);
} catch (err) {
if (attempt === maxRetries) throw err;
const delay = Math.pow(2, attempt) * 1000; // exponential backoff
console.warn(`Attempt ${attempt} failed, retry in ${delay}ms`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('unreachable');
}
Streaming Upload for Large Files
// For files near the size limit, stream the upload
const form = new FormData();
form.append('file', fs.createReadStream(largePdfPath), {
filename: path.basename(largePdfPath),
contentType: 'application/pdf',
knownLength: fs.statSync(largePdfPath).size,
});
// knownLength lets the server know Content-Length upfront
// avoiding chunked transfer encoding issues
Get your API key at toolkit.bot →