JavaScript library
Convert, compress and clean up files from your own code. One call does the whole round trip: upload, queue the job, follow it, hand you the result.
Install
Works anywhere fetch does — Node 20+, Bun, Deno, edge runtimes. Nothing from node: is imported, so bundlers have nothing to shim.
npm install @formatika/sdkFirst call
No key needed: without one you get the same free daily allowance a person gets in the browser.
import { readFile, writeFile } from 'node:fs/promises'
import { Formatika } from '@formatika/sdk'
const formatika = new Formatika()
const result = await formatika.run({
tool: 'image.convert',
files: { filename: 'photo.heic', data: await readFile('photo.heic') },
params: { format: 'webp', quality: 82 },
})
for (const file of result.files) {
await writeFile(file.filename, await file.download())
}Result links are signed and short-lived — made to be downloaded now, not stored.
Keys and limits
A key raises the daily allowance, and work beyond it is paid with credits. Create one in your account.
const formatika = new Formatika({ apiKey: process.env.FORMATIKA_API_KEY })FORMATIKA_API_KEY is picked up from the environment on its own, so passing apiKey is only needed when the key lives somewhere else.
Tools and their parameters: 43
The list comes from the service rather than being baked into the package: a tool added to the site shows up here without a new release. params is the very schema the server validates against, so it can be fed to a form generator, a validator or an agent without being copied by hand.
const tools = await formatika.tools()
// { id: 'image.convert', title, description, accept, maxFiles, params: <JSON Schema> }Progress, cancelling and timeouts
Aborting stops the work on the server too, not just the waiting: a running encode is cancelled within a second.
const controller = new AbortController()
setTimeout(() => controller.abort(), 30_000)
const result = await formatika.run({
tool: 'video.compress',
files: bigVideo,
params: { targetMB: 24 },
onProgress: (percent) => console.log(percent),
signal: controller.signal,
})The same happens when timeoutMs runs out — three minutes by default. Nothing is left burning CPU for a result nobody is waiting for.
Errors
A failed job raises the job's own code, not the HTTP status: the request went fine, the work did not.
import { FormatikaError } from '@formatika/sdk'
try {
await formatika.run({ tool: 'image.convert', files, params: { format: 'webp' } })
} catch (error) {
if (error instanceof FormatikaError) {
error.code // 'RATE_LIMITED' | 'QUOTA_EXCEEDED' | 'UNSUPPORTED_FORMAT' | …
error.retryable // имеет ли смысл повторять
error.retryAfterSeconds
}
}Retries are yours to make, deliberately. Repeating a job means a second job and a second charge, so the client never does it behind your back — it tells you whether repeating is sensible and, when the service said so, how long to wait.
Step by step
run is the whole path. The steps are public too, when you need to hold the pieces yourself — a queue of your own, progress in a database, a job outliving the process.
const upload = await formatika.upload('pdf.merge', file)
const job = await formatika.createJob({ tool: 'pdf.merge', uploadIds: [upload.id] })
const done = await formatika.wait(job.id)
const bytes = await formatika.download(done.files[0])
await formatika.cancel(job.id)What happens to the files
Files are processed and deleted; results live for a limited time and then go. Nothing is kept for training, analysis or resale — that is the point of the service, not a footnote.