Overview
chart.hanif.app accepts a chart config (the Chart.js shape) and returns a rendered image. Two ways to call it:
- POST with a JSON body — best for large or programmatic data.
- GET with the config in the query string — best for dropping straight into an
<img src>.
Every image carries a chart.hanif.app watermark in the bottom-right corner. It can be disabled with watermark: false.
POST /api/v1/chart
Send JSON, receive an image with Content-Type matching the requested format.
cURL
curl -X POST https://chart.hanif.app/api/v1/chart \
-H "Content-Type: application/json" \
-o chart.png \
-d '{
"type": "bar",
"data": {
"labels": ["Jan", "Feb", "Mar"],
"datasets": [{ "label": "Sales", "data": [12, 19, 8] }]
},
"width": 800,
"height": 500,
"format": "png"
}'
The body accepts either a flat shape (type, data, options at the root) or a nested one (wrapped in chart):
JSON · nested
{
"chart": { "type": "line", "data": { }, "options": { } },
"width": 800,
"height": 500,
"backgroundColor": "#ffffff",
"format": "png",
"watermark": true
}
GET /api/v1/chart
Pass the config through the c query parameter — either URL-encoded JSON or base64-encoded JSON. Perfect for embedding:
HTML
<img src="https://chart.hanif.app/api/v1/chart?c=
{%22type%22:%22pie%22,%22data%22:{%22labels%22:[%22A%22,%22B%22],
%22datasets%22:[{%22data%22:[60,40]}]}}&width=400" />
Extra options (width, height, format, backgroundColor, watermark) ride alongside as ordinary query params.
Use base64 for larger configs to keep the URL clean: ?c=<base64-json>.
Errors
Errors return JSON with an appropriate HTTP status (400 for validation, 429 rate-limited, 500 render). Server-side detail is never leaked on 5xx.
JSON · 400
{
"error": "ValidationError",
"message": "Field \"type\" wajib diisi."
}
Code examples
JavaScript (fetch)
const res = await fetch("https://chart.hanif.app/api/v1/chart", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "line",
data: {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: [{ label: "Visitors", data: [120, 190, 170, 220, 280] }]
}
})
});
const blob = await res.blob();
document.querySelector("img").src = URL.createObjectURL(blob);
Python (requests)
import requests
payload = {
"type": "doughnut",
"data": {
"labels": ["Done", "Doing", "Pending"],
"datasets": [{"data": [70, 20, 10]}]
},
"width": 600, "height": 600
}
r = requests.post("https://chart.hanif.app/api/v1/chart", json=payload)
open("chart.png", "wb").write(r.content)
Shell (base64 GET)
CFG=$(echo '{"type":"bar","data":{"labels":["A","B"],"datasets":[{"data":[5,9]}]}}' | base64)
curl "https://chart.hanif.app/api/v1/chart?c=$CFG&width=600" -o chart.png