Chart Image Generator API: JSON to PNG, SVG, PDF Charts avatar

Chart Image Generator API: JSON to PNG, SVG, PDF Charts

Pricing

from $1.70 / 1,000 chart rendereds

Go to Apify Store
Chart Image Generator API: JSON to PNG, SVG, PDF Charts

Chart Image Generator API: JSON to PNG, SVG, PDF Charts

[$2 / 1,000 charts] Turn JSON into PNG, SVG or PDF chart images: bar, line, area, pie, doughnut, gauge, radar, scatter, sparkline, with your theme, size, colors and logo. Chart.js configs accepted, bulk batches. No API key, MCP-ready for AI agents, errors are free. $2 per 1,000 charts.

Pricing

from $1.70 / 1,000 chart rendereds

Rating

0.0

(0)

Developer

ABM

ABM

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Security

Limited permissions

Share

Chart Image Generator API – JSON to PNG, SVG & PDF charts

Price: $2 per 1,000 charts ($0.002 per rendered chart). You pay only for charts that are actually rendered and saved. Invalid configurations are reported in the dataset and never billed. There is no start fee and no proxy cost.

Send chart data as JSON and get back a ready-to-use PNG, SVG or PDF chart image with a public link. It handles bar, horizontal bar, stacked bar, line, area, pie, doughnut, gauge, radar, scatter and sparkline charts. You can set the theme, size, colors, title, footer and logo. One run can render one chart or a batch of hundreds.

Rendering runs inside the Actor (no headless browser, no third-party chart website, no scraping). Results are fast and the same input always gives the same image.

How much does it cost?

You pay $0.002 per rendered chart, which is $2 per 1,000 charts. You don't pay a monthly rental, a start fee or proxy costs.

RunCharts renderedPrice
One KPI chart for a daily e-mail1$0.002
A weekly report with 25 charts25$0.05
A monthly batch for 1,000 clients1,000$2.00

Charts that fail validation and exact duplicates in the same run are free. If you set a maximum cost per run, the Actor stops cleanly when it reaches that limit. It never adds unpaid extras.

What it does

  • 11 chart types: bar, horizontalBar, stackedBar, line, area, pie, doughnut, gauge, radar, scatter, sparkline.
  • 3 output formats: PNG (e-mail, Slack, Teams, WhatsApp), SVG (web, sharp at any zoom), PDF (printable reports).
  • Size control: 100–4000 px in each direction, with a pixel ratio up to 4× for retina PNGs.
  • Branding: 4 themes (light, dark, minimal, corporate), your own color palette, a transparent background, a footer or watermark text, a logo from a URL, and value prefixes/suffixes ($, %, kg).
  • Readable defaults: color-blind-checked palette, thin grid lines and automatic label rotation. Long pie charts are grouped into "Other". The doughnut shows the total in its center.
  • Batch mode: the charts array renders dozens or hundreds of charts in one run. Each chart gets its own file and dataset row.
  • Chart.js-compatible input: paste an existing { "type": "bar", "data": { "labels": [...], "datasets": [...] } } config.
  • Automation-friendly output: each row has a direct fileUrl. Enable includeDataUri to also get a base64 data: URI you can embed straight into an e-mail or HTML in n8n, Make or Zapier.

Input example – single chart

{
"type": "bar",
"title": "Monthly sales",
"labels": ["Jan", "Feb", "Mar"],
"data": [12, 19, 7],
"width": 800,
"height": 400,
"format": "png",
"showValues": true,
"valuePrefix": "$"
}

Input example – batch with styling defaults

Top-level styling fields apply to every chart, and each chart can override them.

{
"format": "png",
"theme": "dark",
"width": 1000,
"height": 500,
"scale": 2,
"colors": ["#2a78d6", "#eb6834", "#1baf7a"],
"brandText": "Source: ACME analytics",
"logoUrl": "https://example.com/logo.png",
"charts": [
{
"chartId": "revenue",
"type": "line",
"title": "Revenue by quarter",
"labels": ["Q1", "Q2", "Q3", "Q4"],
"datasets": [
{ "label": "2025", "data": [120, 150, 170, 210] },
{ "label": "2026", "data": [140, 160, 205, 260] }
],
"valuePrefix": "$"
},
{
"chartId": "traffic-share",
"type": "doughnut",
"title": "Traffic sources",
"labels": ["Organic", "Paid", "Social", "Direct"],
"data": [48, 22, 18, 12],
"valueSuffix": "%"
},
{
"chartId": "nps",
"type": "gauge",
"title": "NPS",
"value": 62,
"gaugeMin": -100,
"gaugeMax": 100,
"gaugeThresholds": [
{ "value": -100, "color": "critical" },
{ "value": 0, "color": "warning" },
{ "value": 50, "color": "good" }
]
},
{
"chartId": "chartjs-example",
"type": "bar",
"data": {
"labels": ["Red", "Blue"],
"datasets": [{ "label": "Votes", "data": [12, 19] }]
},
"options": { "plugins": { "title": { "display": true, "text": "Chart.js config" } } }
}
]
}

Data shapes

Chart typeData fields
bar, horizontalBar, line, area, pie, doughnut, sparklinelabels + data: [numbers], or datasets: [{label, data, color}] (up to 8 series)
stackedBar, radarlabels + datasets (radar needs 3+ labels)
gaugevalue (+ gaugeMin, gaugeMax, gaugeThresholds)
scatterdata: [{"x": 1, "y": 2}, ...] or [[1, 2], ...], or datasets of points

Empty values (null) are allowed. Lines show a gap and bars show nothing.

Output example

Each chart becomes one dataset row, and the image itself is stored in the run's key-value store.

{
"index": 1,
"chartId": "revenue",
"status": "success",
"type": "line",
"title": "Revenue by quarter",
"format": "png",
"fileKey": "revenue.png",
"fileUrl": "https://api.apify.com/v2/key-value-stores/AbC123/records/revenue.png",
"contentType": "image/png",
"sizeBytes": 48213,
"width": 1000,
"height": 500,
"pixelWidth": 2000,
"pixelHeight": 1000,
"seriesCount": 2,
"dataPoints": 8,
"renderMs": 182,
"markdown": "![Revenue by quarter](https://api.apify.com/v2/key-value-stores/AbC123/records/revenue.png)",
"warnings": [],
"error": null
}

If a chart is identical to an earlier one in the same run, you get "status": "duplicate" with the first chart's fileUrl, and it is not billed.

A failed chart (not billed):

{
"index": 2,
"chartId": "chart-0002",
"status": "error",
"error": "Unknown chart type \"funnel\". Use one of: bar, horizontalBar, stackedBar, line, area, pie, doughnut, gauge, radar, scatter, sparkline",
"fileKey": null,
"fileUrl": null
}

Use cases

1. Daily sales e-mail for an e-commerce manager (n8n)

Every morning at 7:00, an n8n workflow reads yesterday's orders from Shopify. It sends the last 7 days to this Actor and puts the chart into the team e-mail. That is 1 chart a day, about $0.06 a month.

Input:

{
"type": "bar",
"title": "Orders, last 7 days",
"labels": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
"data": [132, 118, 141, 155, 170, 210, 188],
"showValues": true,
"width": 600,
"height": 300,
"includeDataUri": true
}

Output (excerpt):

[{ "chartId": "chart-0001", "status": "success", "fileUrl": "https://api.apify.com/v2/key-value-stores/AbC123/records/chart-0001.png", "dataUri": "data:image/png;base64,iVBORw0KG..." }]

Next step: the n8n "Send Email" node embeds dataUri as <img src="...">, so the image shows even when the mail client blocks remote images.

2. Monthly client reports for a marketing agency (Make)

An agency sends a PDF report to each of 40 clients. Make loops over the Google Sheets rows and builds one charts array, with a traffic line chart and a channel doughnut per client. The run renders 80 charts for $0.16.

Input:

{
"format": "svg",
"theme": "corporate",
"brandText": "Prepared by Northwind Agency",
"logoUrl": "https://example.com/agency-logo.png",
"charts": [
{ "chartId": "acme-traffic", "type": "line", "title": "ACME – sessions", "labels": ["Jul", "Aug", "Sep"], "datasets": [{ "label": "2025", "data": [8200, 9100, 9900] }, { "label": "2026", "data": [10400, 11800, 12650] }] },
{ "chartId": "acme-channels", "type": "doughnut", "title": "ACME – channels", "labels": ["Organic", "Paid", "Social", "Email"], "data": [52, 21, 17, 10], "valueSuffix": "%" }
]
}

Output (excerpt):

[
{ "chartId": "acme-traffic", "status": "success", "format": "svg", "fileUrl": ".../records/acme-traffic.svg" },
{ "chartId": "acme-channels", "status": "success", "format": "svg", "fileUrl": ".../records/acme-channels.svg" }
]

Next step: Make puts each fileUrl into a Google Docs template, exports it to PDF and e-mails it to the client.

3. SLA gauge for a support lead in Slack (Zapier)

When the weekly helpdesk export lands, Zapier sends the first-response SLA to this Actor. It then posts the gauge to the #support channel. That is 52 charts a year, about $0.10.

Input:

{
"type": "gauge",
"title": "First-response SLA",
"value": 87,
"valueSuffix": "%",
"gaugeThresholds": [{ "value": 0, "color": "critical" }, { "value": 80, "color": "warning" }, { "value": 90, "color": "good" }],
"width": 500,
"height": 300
}

Output (excerpt):

[{ "type": "gauge", "status": "success", "fileUrl": ".../records/chart-0001.png", "markdown": "![First-response SLA](.../records/chart-0001.png)" }]

Next step: the Slack step posts fileUrl as an image, which Slack unfurls inline.

4. AI agent answering "show me the trend"

A data assistant built on Claude or ChatGPT queries a database and gets numbers. It calls this Actor as an MCP tool and pastes the returned markdown field into its answer, so the user sees a real chart instead of a table.

Use with AI agents (MCP)

This Actor works as a tool in the Apify MCP server, so Claude Desktop, Claude Code, Cursor, ChatGPT and other MCP clients can call it. Add https://mcp.apify.com/?tools=<username>/chart-image-renderer to your MCP client. The agent sends the chart JSON and gets back fileUrl and a ready-to-paste markdown image tag. The input fields have plain names (type, labels, data, title), so LLMs fill them in correctly without examples.

Call it from the API

The synchronous endpoint run-sync-get-dataset-items returns the rows in a single HTTP call.

Python:

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("<username>/chart-image-renderer").call(run_input={
"type": "line", "title": "Signups", "labels": ["W1", "W2", "W3", "W4"], "data": [40, 52, 61, 75],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["status"], item["fileUrl"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('<username>/chart-image-renderer').call({
type: 'pie', title: 'Budget', labels: ['Ads', 'Content', 'Tools'], data: [50, 30, 20], format: 'svg',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => i.fileUrl));

cURL (one call, rows returned directly):

curl -X POST "https://api.apify.com/v2/acts/<username>~chart-image-renderer/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"type":"bar","labels":["Jan","Feb","Mar"],"data":[12,19,7]}'

Integrations: n8n, Make, Zapier, Google Sheets

  • n8n: add the Apify node, choose "Run Actor and get dataset", pick this Actor and map your data to labels and data. Then use {{$json.fileUrl}} or {{$json.dataUri}} in the Email, Slack or Telegram node.
  • Make: use the Apify module "Run an Actor" (synchronous) followed by "Get Dataset Items". Map fileUrl into Gmail, Slack, Google Docs or Google Slides.
  • Zapier: use the Apify action "Run Actor", then "Find Last Dataset Items", and send fileUrl to any app.
  • Google Sheets: use Apify's Google Sheets integration, or run Make or n8n on a schedule. Read a range, build labels and data from the columns, and write fileUrl back with =IMAGE(url) to show the chart in a cell.

FAQ

Do I pay for charts that fail? No. You are billed through the chart-rendered event only for charts that were rendered, saved and delivered. Errors are listed in the dataset with a clear reason, and you pay nothing for them or for duplicates.

Do I need an API key or an account on a chart service? No. You only need your Apify account. No other API key or login is used.

How long are the image links valid? They last as long as the run's storage is kept, which depends on your Apify plan's data retention. Copy the files elsewhere if you need them permanently.

Is my data sent to a third-party service? No. Charts are drawn inside the Actor's container. The only outbound request is the optional download of your logoUrl.

Can I use my Chart.js configs? Yes, for the common parts: type, data.labels, data.datasets[].label/data/backgroundColor/borderColor, and options.plugins.title.text. Plugins and custom JavaScript callbacks are not executed.

Can I control the spending? Yes. Set a maximum charge for the run, and the Actor stops cleanly once the limit is reached. maxCharts adds a cap on the batch size too.

What if the logo URL is broken? The chart is still rendered without the logo, and the row contains a warning.

Limits

  • Up to 1,000 charts per run and 8 series per chart. Each image can be up to 4000 px on a side and 16 megapixels in total, counting the PNG pixel ratio (for example 4000 × 4000, or 2000 × 2000 at scale 2). With the default 256 MB run memory, PNGs are capped at 8 megapixels (for example 4000 × 2000, or 1600 × 1200 at scale 2); run with 512 MB for up to 16 megapixels. SVG and PDF are vector files and are not affected.
  • Static images only (no interactive HTML or animations).
  • No dual-axis charts, by design (two scales on one chart mislead readers). Use two charts instead.
  • Pie and doughnut charts with more slices than palette colors group the smallest slices into "Other".
  • Font: DejaVu Sans (Latin, Greek and Cyrillic). CJK and emoji characters may not render.
  • Logo: public PNG or JPEG, 5 MB max.