SDKs and reference
Node.js
@j2i/sdk is the official TypeScript client for the jsontoimg Render API. Use it in Node.js, Bun, or any TypeScript project to create renders, wait for results, download assets, and run batches.
For shell scripts and CI, see the CLI. For raw HTTP endpoints, see the API reference. Python and PHP call the same HTTP API until those SDKs ship.
Install
npm install @j2i/sdk
pnpm add @j2i/sdk
yarn add @j2i/sdk
bun add @j2i/sdk
Auth and config
Create an API key in the dashboard under Integrations → API keys, then pass it to the client. See Authentication.
import { createClient } from "@j2i/sdk";
const client = createClient({
apiKey: process.env.JSONTOIMG_API_KEY!,
});
| Option | Environment variables (first match wins) | Default |
|---|---|---|
apiKey |
JSONTOIMG_API_KEY, RENDER_API_KEY |
Required |
baseUrl |
JSONTOIMG_API_URL, JSONTOIMG_BASE_URL, RENDER_BASE_URL |
https://app.jsontoimg.com |
Use the API origin only for baseUrl (no trailing slash). /api/v1 and /mcp suffixes are stripped if present. Requests are authenticated with Authorization: Bearer <apiKey>.
Quick start
import { createClient } from "@j2i/sdk";
const client = createClient({
apiKey: process.env.JSONTOIMG_API_KEY!,
});
const job = await client.renderAndWait({
template: "<design-id>",
format: "png",
layers: { headline: { text: "Hello" } },
});
if (job.status === "done") {
console.log(job.id, job.assetUrl);
}
renderAndWait submits a render, waits up to 60 seconds by default, and can write the finished file to disk with out.
Client methods
createRender
createRender(
body: CreateRenderRequest,
opts?: { wait?: number }
): Promise
Creates a render. Pass wait (seconds) to block until the job finishes or the wait window ends. Without wait, you get a queued job and can poll or call waitForRender later.
const accepted = await client.createRender({
template: "<design-id>",
format: "png",
});
const done = await client.createRender(
{ template: "<design-id>", format: "png" },
{ wait: 60 }
);
getRender
getRender(id: string): Promise
Fetches the current status of a render job.
const job = await client.getRender(renderId);
console.log(job.status);
waitForRender
waitForRender(
id: string,
opts?: { timeout?: number }
): Promise
Waits for a render to finish. If the job is still running, the response may include retryAfterMs. See SSE and wait.
const result = await client.waitForRender(renderId, { timeout: 60 });
downloadRenderAsset
downloadRenderAsset(id: string): Promise<{
buffer: Buffer;
contentType: string;
}>
Downloads the finished asset. The job must be done. Uses your API key. For <img> / OG tags, use job.assetUrl (includes ?sig=) or signImageUrl.
import { writeFile } from "node:fs/promises";
const { buffer, contentType } = await client.downloadRenderAsset(renderId);
await writeFile("out.png", buffer);
console.log(contentType);
signImageUrl
signImageUrl(body: SignImageRequest): Promise
Mints an HMAC-signed URL for CMS / Open Graph. Crawlers fetch it with no API key. See URL rendering.
const { url } = await client.signImageUrl({
template: "<design-id>",
format: "png",
layers: { headline: { text: "Hello" } },
});
signImageUrls
signImageUrls(body: SignImageBatchRequest): Promise
Mints up to 50 signed URLs in one call. Same contract as signImageUrl, not queued batch renders.
const { urls } = await client.signImageUrls({
jobs: [
{ template: "<design-id>", layers: { headline: { text: "Hello" } } },
{ template: "<design-id>", layers: { headline: { text: "World" } } },
],
});
renderAndWait
renderAndWait(
body: CreateRenderRequest,
opts?: { wait?: number; out?: string }
): Promise
Creates a render with wait (default 60 seconds). If you pass out and the job completes, the asset is saved to that path.
const job = await client.renderAndWait(
{
template: "<design-id>",
format: "png",
layers: { headline: { text: "Hello" } },
},
{ wait: 90, out: "./hello.png" }
);
listTemplates
listTemplates(opts?: {
project?: string;
limit?: number;
offset?: number;
}): Promise
Returns one page of templates (default 50, max 100). If hasMore is true, pass offset for the next page. Use name and description to pick one, then call getTemplateSchema.
const { templates, hasMore } = await client.listTemplates();
const sale = templates.find((item) =>
item.description?.includes("sale")
);
getTemplateSchema
getTemplateSchema(
id: string,
opts?: { version?: number }
): Promise
Returns the named layers you can override for a template.
const schema = await client.getTemplateSchema("<template-id>", { version: 1 });
for (const layer of schema.layers) {
console.log(layer.layerName, layer.layerType);
}
createBatch / getBatch
createBatch(body: CreateBatchRequest): Promise
getBatch(id: string): Promise
Queue up to 50 renders in one request, then check progress. See Batch renders.
const batch = await client.createBatch({
jobs: [
{
template: "<design-id>",
format: "png",
layers: { headline: { text: "A" } },
},
{
template: "<design-id>",
format: "jpg",
layers: { headline: { text: "B" } },
},
],
});
const status = await client.getBatch(batch.id);
console.log(status.status, status.totals);
Request body
{
"template": "<design-id>",
"format": "png",
"scale": 1,
"transparent": false,
"layers": {
"headline": { "text": "Hello" }
},
"variables": { "plan": "pro" },
"webhookUrl": "https://example.com/hook"
}
| Field | Notes |
|---|---|
template |
Design ID (required) |
version |
Optional design version |
format |
png | jpg | webp | pdf | avif |
scale |
Default 1 |
transparent |
Default false; png / webp / avif only |
layers |
Layer name → property overrides |
variables |
JSON for logic-rule conditions (source: variables) |
webhookUrl |
Optional webhook when the job finishes |
expandVariants |
Batch-only. { "layer": "<variant-set name>" } fans one job into one per variant |
Errors
API failures throw JsontoimgApiError:
import { createClient, JsontoimgApiError } from "@j2i/sdk";
try {
await client.createRender({ template: "<design-id>" });
} catch (err) {
if (err instanceof JsontoimgApiError) {
console.error(err.status, err.message, err.code, err.details);
}
throw err;
}
| Property | Meaning |
|---|---|
status |
HTTP status |
code |
API error code when present |
details |
Extra validation or context |
body |
Raw response body |
See Errors for the full error shape.
Advanced
client.api is a typed low-level HTTP client if you need an endpoint shape the helpers do not wrap. The one helper-less path is GET /api/v1/renders/{id}/events (SSE).
const { data, error, response } = await client.api.GET(
"/api/v1/renders/{id}/events",
{ params: { path: { id: renderId } } }
);
Useful exports include CreateRenderRequest, RenderJob, BatchStatus, TemplateList, TemplateSchema, JsontoimgApiError, resolveApiKey, and resolveBaseUrl.
Related
- CLI — same API from the shell
- MCP — drive renders from Cursor, Claude, and VS Code
- Webhooks — completion callbacks
- URL rendering — signed URLs for CMS / OG
- API reference — OpenAPI and Try it