Render infographic scenes as PNG images, one per scene, in portrait, square or story sizes. Ideal for LinkedIn and Instagram carousels
Render infographic scenes as still PNG images, one image per scene. Use it to build LinkedIn or Instagram carousels, then combine the images with /convert/images/to/pdf for a PDF carousel.
Colours and font sizes can be customised with an optional style object, for the whole request or for individual scenes.
This is an async job: the request returns a job_id immediately. Poll the status URL until the job is completed, then read the image URLs from the result.
POST https://eranol.com/api/v1/ffmpeg/image/render
POST /api/v1/ffmpeg/image/render returns a job.GET /api/v1/ffmpeg/status/{job_id} every 1–2 seconds until status is completed, failed or cancelled. While queued, progress shows Queued — N job(s) ahead.result.images (or call GET /api/v1/ffmpeg/result/{job_id}).{
"size": "portrait",
"style": {
"background": "#fcfcfc",
"title_size": 72
},
"scenes": [
{
"template": "stat_countup",
"kicker": "THE NUMBER",
"title": "Up to",
"value": 100000,
"prefix": "£",
"bar_max": 100000,
"accent": "orange"
},
{
"template": "ring_percent",
"title": "Who is at fault",
"percent": 46,
"label": "AT FAULT",
"style": { "accent_color": "#e46030" }
}
]
}
Unknown top-level fields, unknown scene fields and unknown style fields are rejected with 422.
| Name | Type | Default | Description |
|---|---|---|---|
scenes | array | required | 1 to 80 scenes, one image each, in order |
size | string | portrait | portrait, square or story |
style | object | none | Colours and font sizes for every scene (see Styling) |
| Value | Pixels | Use |
|---|---|---|
portrait | 1080×1350 (4:5) | LinkedIn / Instagram feed |
square | 1080×1080 | Square posts |
story | 1080×1920 | Stories |
| Name | Type | Default | Description |
|---|---|---|---|
template | string | required | Picks the layout (see below) |
kicker | string | '' | Small uppercase line above the title (max 38 chars) |
title | string | '' | Headline (max 56 chars) |
subtitle | string | '' | Grey line under the title (max 90 chars) |
accent | string | blue | blue, green, purple or orange |
illustrative | bool | false | Print the ILLUSTRATIVE SHAPE · NO REAL DATA tag |
style | object | none | Colours and font sizes for this scene only. Overrides the request-level style |
footnote | string | '' | Small note at the bottom of the slide (max 140 chars) |
source | string | '' | Source line under the footnote (max 80 chars). Source: is added for you, so send gov.uk, 2026 |
Leave start, end and every at field out. For images the server picks the timing and captures each slide settled.
| Template | Required | Optional |
|---|---|---|
stat_countup | value (0–1e12) | prefix (max 4), suffix (max 6), decimals (0–2), label (max 40), bar_max (at least value), icon |
hbar_compare | bars (2–4 of {label max 24, value_text max 14, ratio 0–1, accent?}) | hero {value, prefix, suffix, decimals} |
ring_percent | percent (0–100) | label (max 20), stamp {text max 24} |
rows | items (2–5 of {text max 44, state: "keep" or "reject"}) | |
bar_columns | columns (3–12 of {label max 4, value_text max 8}) | note (max 24) |
growing_bars | count (4–10), left_label (max 20), right_label (max 28) | |
progress_track | label (1–30 chars) | direction (fill or drain), text (max 20) |
icon_pair | icons (1–3 of {name, caption max 40, accent?}) | symbol (max 2 chars), numbered, stamp {text} |
logo_flow | logo_domain (bare domain, e.g. mod.uk) | to_icon, arrow |
headline_quote | quote (1–120 chars) | highlight (text from the quote, shown in the accent colour, max 60), sub (max 90), rule (accent line above, default true), marks (wrap in quotation marks, default true) |
timeline | events (2–6 of {date max 24, label max 24, key?}) | |
stacked_column | segments (2–6 of {label max 36, value ≥ 0, value_text? max 10}, top to bottom) | |
pie_chart | slices (2–6 of {label max 24, value ≥ 0, accent?}; values must add up to more than 0) | donut (default true; false draws a full pie), center_text (donut only, max 12) |
line_chart | x_labels (2–12, each 1–6 chars), series (1–3 of {name max 20, values one per x label, accent?}) | y_prefix, y_suffix (max 4 each) |
bar_chart | bars (2–8 of {label max 12, value ≥ 0, value_text? max 12, tone? strong/light/muted, accent?}) | side (2–5 segments {label, value, value_text?} shown as a stacked block on the right) |
hbar_chart | rows (2–8 of {label max 20, value ≥ 0, value_text? max 12, tone?, accent?}) |
How the new templates behave:
headline_quote: a large left-aligned statement for hook and takeaway slides. Put \n in the quote to force a line break (for example "Cheap plans\ncan cap\nyour invoices"); the text size steps down automatically for longer quotes. highlight must appear in quote exactly, otherwise the request returns 422.timeline: milestones on a horizontal line, date above and label below. key: true events use the accent colour; the others are tinted.stacked_column: one tall column whose segment heights follow value (small segments are kept tall enough to read). Each segment gets a leader line to its label and its text colour is chosen for contrast. Shades run light to dark from the accent colour.bar_chart: vertical bars scaled to the largest value, with the value above each bar. Give one bar an accent (for example orange) to highlight it, and use tone: "light" or "muted" for the others. Spacing and label size tighten automatically as bars are added. side adds a stacked block on the right.hbar_chart: horizontal bars from a left axis, with the label on the left and the value after the bar. A row with value 0 shows just its label and value.pie_chart: slice shades come from the accent colour unless a slice names its own accent. The legend shows whole-number percentages that add up to 100.line_chart: the y axis starts at 0 (and goes below it if a value is negative) with tidy tick values. The last value of each series is labelled, and a legend appears when there is more than one series.Icon names are Lucide names such as shield-check. An unknown name fails validation with 422. Logos are looked up by domain; if one isn't found the slide renders without it. When any logo is used, an attribution line is printed on the last slide.
The optional style object changes colours, font sizes and the title weight. It can be set on the request (applies to every scene) and on any scene (applies to that scene only and wins over the request-level value). Fields you leave out keep the default look, and a request with no style renders exactly as it always has.
style changes appearance, not layout. Each template keeps its structure; to get a different layout, use a different template.
All colours are #RRGGBB hex strings (for example #e46030).
| Field | Changes |
|---|---|
background | The slide background |
text_color | Titles, axis and column labels, and the number badges on icon_pair |
muted_color | Row text on rows, and the credit and illustrative tag lines |
label_color | Captions (on ring_percent) and bar labels (on hbar_compare, progress_track) |
alert_color | Stamps and the strike-through on rejected rows items |
accent_color | Replaces the scene's accent: the kicker, bars, rings, tiles and hero numbers. Items that name their own accent (for example a bar in hbar_compare) keep it |
Sizes are whole numbers in pixels, on the 1080-wide canvas.
| Field | Range | Changes |
|---|---|---|
title_size | 24–160 | The scene title |
quote_size | 40–220 | The headline_quote text. By default it steps down with the quote's length (136, 108, 86 or 68 px) |
title_weight | 400, 500, 600, 700, 800 or 900 | The scene title weight |
kicker_size | 16–90 | The small uppercase line above the title |
caption_size | 16–90 | Captions on ring_percent and icon_pair tiles |
label_size | 16–90 | Bar labels, column labels, axis labels, and timeline dates and stacked-column labels |
value_size | 24–240 | The centre number of ring_percent, the note on bar_columns, the segment values on stacked_column, the donut's center_text and the end labels on line_chart |
Large numbers elsewhere (the count-up number on stat_countup and hbar_compare, bar values, stamp text) are sized automatically to fit their text and are not changed by value_size.
#RRGGBB, a size outside its range, or an unknown field returns 422.style is merged over request-level style field by field. A scene that sets only accent_color still gets the request's background.A dark-on-cream look for the whole carousel, with one orange slide:
{
"size": "square",
"style": {
"background": "#fcfcfc",
"text_color": "#14171c",
"accent_color": "#2472c6",
"title_size": 76,
"title_weight": 800
},
"scenes": [
{ "template": "stat_countup", "kicker": "THE NUMBER", "title": "Claims paid", "value": 4200, "prefix": "£" },
{ "template": "ring_percent", "title": "Who is at fault", "percent": 46, "label": "AT FAULT",
"style": { "accent_color": "#e46030" } }
]
}
[
{
"job_id": "1406c589-a1f4-45d5-8745-aae07bc4de38",
"status": "pending",
"message": "Job created. Poll status_url for progress.",
"result_url": "https://eranol.com/api/v1/ffmpeg/result/1406c589-a1f4-45d5-8745-aae07bc4de38",
"status_url": "https://eranol.com/api/v1/ffmpeg/status/1406c589-a1f4-45d5-8745-aae07bc4de38"
}
]
{
"images": [
{ "index": 1, "url": "https://ffmpeg.eranol.com/outputs/<uuid>.png", "filename": "<uuid>.png" }
],
"filenames": ["<uuid>.png"],
"count": 4,
"format": "png",
"resolution": "1080x1350",
"size": "portrait",
"logos_used": ["mod.uk"]
}
images is in the same order as scenes.
Four portrait slides, then pass the image URLs to /convert/images/to/pdf for a PDF carousel.
Request Body
{
"size": "portrait",
"scenes": [
{ "template": "stat_countup", "kicker": "THE NUMBER", "title": "Claims paid", "value": 4200, "prefix": "£" },
{ "template": "hbar_compare", "title": "Fault split", "bars": [
{ "label": "Driver A", "value_text": "62%", "ratio": 0.62 },
{ "label": "Driver B", "value_text": "38%", "ratio": 0.38, "accent": "orange" }
] },
{ "template": "rows", "title": "Do and don't", "items": [
{ "text": "Photograph the scene", "state": "keep" },
{ "text": "Admit fault", "state": "reject" }
] },
{ "template": "logo_flow", "title": "Report it to", "logo_domain": "gov.uk", "arrow": true }
]
}
A single 1080×1920 slide.
Request Body
{
"size": "story",
"scenes": [
{ "template": "ring_percent", "title": "Who is at fault", "percent": 46, "label": "AT FAULT", "accent": "purple" }
]
}
The same slides in your brand colours: one style on the request, no per-scene changes.
Request Body
{
"size": "portrait",
"style": {
"background": "#f4f7fb",
"text_color": "#0c122a",
"accent_color": "#0666c0",
"title_size": 72,
"kicker_size": 26
},
"scenes": [
{ "template": "stat_countup", "kicker": "THE NUMBER", "title": "Claims paid", "value": 4200, "prefix": "£" },
{ "template": "ring_percent", "title": "Who is at fault", "percent": 46, "label": "AT FAULT" }
]
}
progress shows Queued — N job(s) ahead.failed, the same request can be retried.1 credit per request (Standard Plan), regardless of the number of scenes.
| Code | Meaning |
|---|---|
401 | Missing or invalid API key |
402 | Insufficient credits |
422 | Validation error — loc names the offending field (e.g. unknown field or icon, invalid style colour or size, too many scenes) |
429 | Rate limit exceeded — slow down requests |
500 | Internal server error — try again or contact support |