Deno icon

Deno

Generate images from a template with the built-in fetch (no dependencies), plus Deno.writeFile for the bytes. Imejis returns the finished image directly in the response, so there is nothing to poll and no webhook to receive.

Set two environment variables first: IMEJIS_API_KEY (from your dashboard) and IMEJIS_DESIGN_ID (any design's id). Then send the fields you want to change as component.property pairs: headline.text, title.color, image.image. Only the fields you send change; everything else keeps its designed value, and a design's overridable fields are listed in its Share / API panel.

Every block below is a copy-paste runnable program, and every one of them is executed in Docker against a real render service before it ships.

Generate a single imageGenerate a single image

One POST, one image. The response body is the rendered image itself, with nothing to poll and no webhook to receive.

Deno: Generate a single image
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// Render one image. The response body IS the image — no polling, no webhook.
const res = await fetch(`${BASE}/v1/${DESIGN_ID}`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    "headline.text": "Hello from Node.js",
    "subtitle.text": "Rendered in one request",
  }),
})
if (!res.ok) throw new Error(`render failed: ${res.status}`)
const image = new Uint8Array(await res.arrayBuffer())
await Deno.writeFile("output.jpg", image)
console.log("single:", res.status, image.length, "bytes")

Every output format png jpeg webp and pdfEvery output format: PNG, JPEG, WebP and PDF

Pick the output with ?format=. PNG and WebP keep transparency, JPEG is the smallest lossy option, and PDF is a single print-ready page. ?quality= applies to JPEG only.

Deno: Every output format: PNG, JPEG, WebP and PDF
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// Every output format, with JPEG quality. PNG and WebP keep transparency; PDF
// is a single print-ready page. `quality` only affects JPEG.
for (const format of ["png", "jpeg", "webp", "pdf"]) {
  const params = new URLSearchParams({ format })
  if (format === "jpeg") params.set("quality", "85")
  const r = await fetch(`${BASE}/v1/${DESIGN_ID}?${params}`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ "headline.text": `Format: ${format}` }),
  })
  if (!r.ok) throw new Error(`${format} failed: ${r.status}`)
  const bytes = new Uint8Array(await r.arrayBuffer())
  await Deno.writeFile(`output.${format}`, bytes)
  console.log(`formats: ${format}`, r.headers.get("content-type"), bytes.length)
}

Render multiple sizes from one templateRender multiple sizes from one template

A multi-size design carries several canvases: an OG image, a square post, a vertical story. ?variation=<id> picks which one you get back; omit it for the base size.

Deno: Render multiple sizes from one template
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// One template, many sizes. `?variation=<id>` picks the size; omit it for the
// base. The ids are listed in the design's Share / API panel.
const sized = await fetch(
  `${BASE}/v1/${DESIGN_ID}?${new URLSearchParams({ format: "png", variation })}`,
  {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ "headline.text": "Story size" }),
  }
)
if (!sized.ok) throw new Error(`variation failed: ${sized.status}`)
console.log("variation:", sized.status, (await sized.arrayBuffer()).byteLength, "bytes")

An image url for img tags email and thumbnailsAn image URL for <img> tags, email and thumbnails

The same render as a plain GET URL, with the field values in the query string. Drop it into an <img src>, an HTML email, or a Slack message and it renders fresh every time it is fetched.

Deno: An image URL for img tags, email and thumbnails
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// The same render as a plain GET URL — drop it into an <img> tag, an HTML
// email, or a Slack message. Field overrides ride the query string.
const url = `${BASE}/v1/${DESIGN_ID}?${new URLSearchParams({
  "dma-api-key": API_KEY,
  format: "png",
  "headline.text": "Hello from a URL",
  "subtitle.text": "No request body needed",
})}`
console.log("get:", url)
const viaUrl = await fetch(url)
console.log("get:", viaUrl.status, (await viaUrl.arrayBuffer()).byteLength, "bytes")

Hosted delivery get a public image url backHosted delivery: get a public image URL back

?delivery=hosted stores the render and returns a public URL instead of raw bytes, which is what an email, a webhook payload, or a dashboard needs: a link rather than a file.

Deno: Hosted delivery: get a public image URL back
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// delivery=hosted stores the render and returns a PUBLIC URL instead of bytes —
// for an email, a webhook payload, or a dashboard that needs a link.
const hostedRes = await fetch(
  `${BASE}/v1/${DESIGN_ID}?${new URLSearchParams({ delivery: "hosted", format: "png" })}`,
  {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ "headline.text": "Hosted delivery" }),
  }
)
const hosted = await hostedRes.json()
console.log("hosted:", hosted.url, hosted.file.size, "bytes")

Signed delivery a private expiring image urlSigned delivery: a private, expiring image URL

?delivery=signed stores the render privately and returns a time-limited signed URL. ?expiresIn= is minutes.

Deno: Signed delivery: a private, expiring image URL
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// delivery=signed stores it privately and returns a time-limited signed URL.
// `expiresIn` is minutes (default 60, max 7 days).
const signedRes = await fetch(
  `${BASE}/v1/${DESIGN_ID}?${new URLSearchParams({
    delivery: "signed",
    format: "png",
    expiresIn: "30",
  })}`,
  {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ "headline.text": "Signed delivery" }),
  }
)
const signed = await signedRes.json()
console.log("signed:", signed.expiresAt, `${signed.url.slice(0, 80)}...`)

Bulk image generation in one callBulk image generation in one call

POST /bulk to render many images in a single request: several sizes, several data variants, a mix of designs, or the ordered slides of a carousel. results preserves input order, so results[i] is items[i].

Deno: Bulk image generation in one call
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// Many images in ONE call. `results` keeps input order, so results[i] is
// items[i] — for a carousel the array IS the slide order. Each item is one
// metered render, and a failed item does not abort the rest.
const bulkRes = await fetch(`${BASE}/bulk`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    delivery: "hosted",
    format: "png",
    items: [
      { designId: DESIGN_ID, overrides: { "headline.text": "Slide 1" } },
      {
        designId: DESIGN_ID,
        variation,
        format: "jpeg",
        quality: 80,
        overrides: { "headline.text": "Slide 2" },
      },
      { designId: DESIGN_ID, format: "pdf", overrides: { "headline.text": "Slide 3" } },
    ],
  }),
})
const bulk = await bulkRes.json()
console.log("bulk:", JSON.stringify(bulk.summary))
for (const item of bulk.results) {
  console.log("  ", item.index, item.format, item.url ?? item.error)
}

Bulk from a list of rowsBulk from a list of rows

Send data instead of items and every row becomes one render, with no item list to build. That is the shape to reach for when the input is a spreadsheet, a database query, or a CSV.

Deno: Bulk from a list of rows
const API_KEY = Deno.env.get("IMEJIS_API_KEY")
const DESIGN_ID = Deno.env.get("IMEJIS_DESIGN_ID")
const BASE = Deno.env.get("IMEJIS_RENDER_URL") ?? "https://render.imejis.io"
const HEADERS = { "dma-api-key": API_KEY, "Content-Type": "application/json" }
// The size to render on a multi-size design; absent/"master" is the base.
const variation = Deno.env.get("IMEJIS_VARIATION_ID") ?? "story"
 
// The same design over a LIST OF ROWS — one render per row, no items[] to
// build. Use `variations: ["master", "story"]` instead to render every SIZE.
const rowsRes = await fetch(`${BASE}/bulk`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    designId: DESIGN_ID,
    delivery: "signed",
    expiresIn: 15,
    format: "png",
    data: [
      { "headline.text": "Ada Lovelace", "subtitle.text": "Mathematician" },
      { "headline.text": "Alan Turing", "subtitle.text": "Computer scientist" },
      { "headline.text": "Grace Hopper", "subtitle.text": "Rear admiral" },
    ],
  }),
})
const rows = await rowsRes.json()
console.log("bulk-data:", JSON.stringify(rows.summary))

The response is the raw image (`image/jpeg` by default). Choose the format with `?format=png|jpeg|webp|pdf` (default jpeg) and set JPEG quality with `?quality=1-100`. Store the result and get a URL back with `?delivery=hosted` (or `?delivery=signed&expiresIn=60`), and render a specific size of a multi-size design with `?variation=<id>`.

ParameterValuesDefaultWhat it does
?format=png · jpeg · webp · pdfjpegOutput image format.
?quality=1–100noneCompression quality for lossy output; ignored for other formats. (JPEG only)
?delivery=stream · hosted · signedstreamstream = raw image bytes (default); hosted = store + return a public URL; signed = store + return a time-limited URL.
?expiresIn=minutes60Signed-URL lifetime in minutes (default 60, max 10080 = 7 days). (delivery=signed only)
?variation=<size id>masterWhich multi-dimension SIZE to render. Omit or use "master" for the base size; an unknown id falls back to the master. (multi-size designs only)
  • PNG (?format=png): Lossless, supports transparency. Largest file.
  • JPEG (?format=jpeg): Smaller, lossy, no transparency. Quality adjustable.
  • WebP (?format=webp): Smallest, supports transparency. Modern browsers.
  • PDF (?format=pdf): Vector page, for print and documents.

See the delivery modes and generate images with the API guides.

The same calls in other runtimes are on the JavaScript and Bun pages, and Hono is the framework version. Deno needs the permissions these programs use, --allow-env --allow-net --allow-write, which is a feature: nothing reads your key unless you say so. One render per call, 100 free a month, pricing for more.