Your First POST /api/v1/render Call

Published October 15, 2026 by Balaji

Uncategorized

This is the first successful POST /api/v1/render — not a second homepage. Create an API key, send a template id and optional layer overrides, wait up to 60 seconds, download the file. 25 images are free; no card.

Overview

Every product team needs generated images at some point: Open Graph cards for blog posts, product banners for thousands of SKUs, certificates, ad creatives for A/B tests, personalized email headers. You have three options — design them by hand in Figma or Canva (doesn’t scale past a few images), run a headless browser like Puppeteer (high maintenance and expensive), or use a purpose-built image generation API.

jsontoimg is the third option, stripped to its core job: JSON in, image out. You design a template once, send JSON data, and get back a rendered PNG, JPG, WebP, AVIF, or PDF, at any volume.

This tutorial gets you from zero to your first rendered image in about five minutes. By the end you’ll have:

  • A simple template defined in JSON
  • Your first API call with curl
  • Working examples in Node.js and Python
  • A batch job generating multiple images in one request

No prior image-generation experience needed. If you know what a POST request is, you’re ready.

What “JSON to Image” Actually Means

The core idea is a three-part pipeline:

Design a template (once)
  → Send JSON data (every time)
  → Get an image back (instantly)

A template is a design with placeholders — dynamic text, images, and colors you control through the API. Your data is plain JSON that fills those placeholders. The render is the image that comes out.

The power is in the separation: you design the visual once, then any system that can send JSON — a CMS, a database, a spreadsheet, a webhook — can produce branded images without touching a design tool. This is the same pattern every serious template-based image API uses: Bannerbear sends a modifications array against a template (Bannerbear API Quick Start), Placid sends layers against a template_uuid (Placid REST API docs), and Pixelixe describes it as “one JSON request, one rendered output, unlimited reuse” (Pixelixe JSON to Image API). jsontoimg follows the same convention, so if you’ve used any of them, the mental model transfers immediately.

Before You Start

You need two things:

  1. A jsontoimg account and API key — grab your key from the dashboard. It’s a bearer token; keep it server-side and never in client code.
  2. A way to make HTTP requests — curl works for testing. For real integrations, Node.js 18+ (native fetch) or Python 3 with requests are covered below.

Step 1: Design Your First Template

Templates are JSON documents that describe the canvas: dimensions, background, and layers. Here’s a minimal branded card — a 1200×630 social share image with a gradient background, a headline, and a subtitle:

{
  "width": 1200,
  "height": 630,
  "background": {
    "type": "gradient",
    "colors": ["#1a1a2e", "#16213e", "#0f3460"]
  },
  "layers": [
    {
      "type": "text",
      "name": "headline",
      "text": "Your headline goes here",
      "font": { "size": 72, "weight": "bold", "color": "#ffffff" },
      "position": { "x": 80, "y": 200 },
      "width": 1040,
      "align": "left"
    },
    {
      "type": "text",
      "name": "subtitle",
      "text": "Your subtitle goes here",
      "font": { "size": 36, "color": "#a0aec0" },
      "position": { "x": 80, "y": 330 },
      "width": 1040,
      "align": "left"
    }
  ]
}

A few things to note:

  • name is the layer’s ID — you’ll reference it in every API call. Keep it stable: headline, subtitle, logo, price.
  • text is the default value. The API overrides it per request, but a sensible default makes the template testable in the editor.
  • Text styling — size, weight, color, alignment, and wrapping are all first-class properties. You can also add image layers (for logos and photos) and shape layers (for badges and dividers).

Once saved, the template gets a template ID that you’ll use in all render calls.

Step 2: Make Your First API Call

With the template saved, generating an image is a single POST to the render endpoint:

POST https://app.jsontoimg.com/api/v1/render

Here’s the same request with curl:

curl -X POST "https://app.jsontoimg.com/api/v1/render?wait=60" \
  -H "Authorization: Bearer $JSONTOIMG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "social-share-card",
    "format": "png",
    "layers": {
      "headline": { "text": "Launching the New Dashboard" },
      "subtitle": { "text": "Real-time analytics for your team" }
    }
  }'

The response is small and predictable:

{
  "id": "rnd_a1b2c3",
  "status": "done",
  "format": "png",
  "assetUrl": "https://app.jsontoimg.com/api/v1/renders/rnd_a1b2c3/asset?sig=..."
}

The image_url is the finished PNG, ready to host, serve, or upload anywhere. You’re done — that’s the whole first call.

Node.js

const JSONTOIMG_API_KEY = process.env.JSONTOIMG_API_KEY;

async function renderImage() {
  const response = await fetch('https://app.jsontoimg.com/api/v1/render?wait=60', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${JSONTOIMG_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      template: 'social-share-card',
      format: 'png',
      layers: {
        headline: { text: 'Launching the New Dashboard' },
        subtitle: { text: 'Real-time analytics for your team' }
      }
    })
  });

  const data = await response.json();
  console.log(data.assetUrl);
}

renderImage();

Python

import os
import requests

API_KEY = os.environ['JSONTOIMG_API_KEY']

response = requests.post(
    'https://app.jsontoimg.com/api/v1/render',
    headers={
        'Authorization': f'Bearer {API_KEY}',
        'Content-Type': 'application/json'
    },
    json={
        'template': 'social-share-card',
        'format': 'png',
        'layers': {
            'headline': { 'text': 'Launching the New Dashboard' },
            'subtitle': { 'text': 'Real-time analytics for your team' }
        }
    }
)

print(response.json()['assetUrl'])

Step 3: Sync vs Async vs Webhooks

Rendering a single image is fast — usually a second or two — so the synchronous call above is all you need for most cases. But at volume you’ll want the async patterns that production integrations use. Here’s how the three modes compare:

Mode How it works Best for
Synchronous Connection stays open; image_url returned in the response Simple renders, low volume, Lambda/edge functions
Async + polling Response returns job_id and status: "queued"; you poll a status endpoint Large or complex renders where you don’t want to hold the connection
Webhook Pass webhook_url; jsontoimg POSTs the finished image to your endpoint High-volume batch pipelines, event-driven systems

For polling, the response looks like this:

{
  "status": "queued",
  "job_id": "job_8f3k2m"
}

Then you poll until the job completes:

curl -X GET "https://app.jsontoimg.com/api/v1/renders/rnd_8f3k2m" \
  -H "Authorization: Bearer $JSONTOIMG_API_KEY"

A common production pattern is exponential backoff — poll at 1s, 2s, 4s, 8s — rather than hammering the endpoint every second (Wireflow’s developer guide covers exactly this trade-off). And for anything beyond a few seconds of processing, webhooks are the standard recommendation: your server gets notified when the job completes instead of polling at all (Apiframe’s 2026 API roundup). Bannerbear and Placid offer the same dual pattern — poll a GET endpoint or receive a webhook POST (Bannerbear help, Placid docs).

Step 4: Generate Your First Batch

Single renders are nice; batches are where the API earns its keep. Send an array of renders in one request:

const response = await fetch('https://app.jsontoimg.com/api/v1/renders/batch', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${JSONTOIMG_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    jobs: [
      {
        template: 'social-share-card',
        format: 'png',
        layers: {
          headline: { text: 'Post 1: The Ultimate Guide' },
          subtitle: { text: 'Everything you need to know' }
        }
      },
      {
        template: 'social-share-card',
        format: 'png',
        layers: {
          headline: { text: 'Post 2: Case Study' },
          subtitle: { text: 'How one team scaled to 10K images' }
        }
      }
    ]
  })
});

const results = await response.json();
// GET /api/v1/renders/batches/{id}; each done job has assetUrl

One template, hundreds of variations, one request. This is the workflow behind the big use cases on the site:

If you’re evaluating whether template-based rendering is the right pattern for your stack at all, the Abyssale comparison of template-based image APIs is a good industry overview — and the jsontoimg vs Bannerbear and jsontoimg vs Placid pages cover where the trade-offs land.

Common Mistakes and How to Fix Them

Error / symptom Likely cause Fix
401 Unauthorized Wrong or missing API key Confirm Authorization: Bearer ; never hardcode keys in client code
404 template not found Typo in template Copy the ID from the dashboard exactly
Render succeeds but text is unchanged name doesn’t match a layer The modification name must equal a layer name in the template
Text clipped or overflowing Font too large for the layer width Check font.size against the layer width and align settings
Slow responses on big batches Many heavy renders in one call Use the batch endpoint with a webhook_url and process asynchronously
Rate limit errors Too many requests per second Add backoff; the async/webhook path is more forgiving at volume

Next Steps

You’ve made your first call. The natural next step is to wire this into a real workflow:

  1. Read the use case guides — OG images, email hero images, event tickets show the full pipelines.
  2. Build something — the product banner generator walkthrough takes the batch pattern above and points it at a real product catalog.
  3. Compare options — if you’re still deciding between providers, the comparison series covers the honest trade-offs.

And if you’re curious about the technical story behind why a JSON-driven API beats a headless browser, check out Why I Built a JSON-to-Image API Instead of Using Puppeteer (coming soon) — spoiler: it’s the cost and the ops burden.

Key Takeaways

  • JSON in, image out — a template + a JSON payload is all you need to generate branded images at scale
  • Design once, render forever — templates separate the visual design from the data, so any system that speaks JSON becomes an image generator
  • The API is one POST — template + layers → assetUrl; the same convention as Bannerbear, Placid, and Pixelixe
  • Pick the right mode — synchronous for simple renders, polling for long jobs, webhooks for production pipelines
  • Batch is where scale happens — one request, hundreds of images
  • First render in ~5 minutes — template → curl → done

FAQ

What do I need before my first render?

An account, an API key, and one template with its changing layers named. Every account starts with 25 free API images and no card, so you can get through a first call before deciding anything.

What does the minimal JSON to image request look like?

A POST /api/v1/render with the template ID and an object of layer names to values. Layers you do not mention keep the defaults you set in the editor.

Why is my render returning 422?

Almost always a layer name that does not exist in the template — a typo or a layer renamed in the editor. The API rejects unknown keys instead of quietly rendering an image with a missing field.

When should I move from single renders to the batch endpoint?

As soon as you are rendering a list rather than one image. POST /api/v1/renders/batch takes up to 50 jobs per request and can report back by webhook.

Do failed calls use up my free images?

No. Credits are spent on successful renders only, and designing in the editor with watermarked previews does not touch them at all.

Related Resources

Next: docs, named layers, pricing.

Zoom out: How JSON-to-Image Conversion Works maps the whole template, named-layer, and render-job architecture.