Fig. — Reference

API documentation

Base URL https://chart.hanif.app. Two endpoints, a handful of parameters, zero ceremony.

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.

Shortcut mode

For the common case you don't need the full Chart.js shape. Pass labels + values (or series) and a few top-level shortcuts, and the API expands them into a complete config:

JSON · minimal
{
  "type": "bar",
  "labels": ["Mon", "Tue", "Wed", "Thu", "Fri"],
  "values": [12, 19, 8, 15, 22],
  "title": "Weekly orders",
  "theme": "dark"
}

Multiple series with the series shortcut:

JSON · series
{
  "type": "line",
  "labels": ["Jan", "Feb", "Mar"],
  "series": [
    { "name": "Revenue", "data": [40, 55, 48] },
    { "name": "Target",  "data": [50, 50, 60] }
  ]
}
ShortcutExpands to
labels + valuesA single dataset.
series[{ name, data }] → multiple datasets.
titleBold title, top-left.
subtitle / descriptionMuted line under the title.
caption / sourceSmall note at the bottom-left (source is prefixed with “Source:”).
xLabel / yLabelAxis titles.
legendfalse hides it; "top"/"bottom"/"left"/"right" sets position.
gridfalse hides grid lines.
stackedtrue stacks both axes.

Shortcuts never override explicit options — set both and your options win.

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>.

Parameters

ParamTypeDefaultNotes
type / chart.typestringRequired. Chart type.
data / chart.dataobjectRequired. { labels, datasets }.
optionsobject{}Chart.js options (title, scales, legend…).
widthnumber800Pixels, 50–3000.
heightnumber600Pixels, 50–3000.
devicePixelRationumber2Crispness multiplier, 1–4.
backgroundColorstring#ffffffAny CSS color, or transparent.
formatstringpngpng · jpeg · webp.
themestringlightlight or dark (dark sets a dark bg + light text).
palettestring / arraydefaultNamed palette or a custom ["#...", …] array.
watermarkbool / string / objecttrueSee Branding.
logostring / objectOverlay your own logo (base64 data URI). See Branding.

Chart types

Native Chart.js types plus a few convenience aliases:

Flowcharts & diagrams

Beyond data charts, type: "flowchart" (alias graph) renders node-and-arrow diagrams using a subset of Mermaid syntax — laid out server-side with no headless browser.

JSON · mermaid syntax
{
  "type": "flowchart",
  "definition": "graph TD\nA[User submits] --> B{Valid?}\nB -->|yes| C[Save to DB]\nB -->|no| D([Show error])\nC --> E((Done))",
  "title": "Form flow"
}

Or pass structured nodes + edges instead of a definition string:

JSON · structured
{
  "type": "flowchart",
  "direction": "LR",
  "nodes": [{ "id": "a", "label": "Start", "shape": "stadium" }, { "id": "b", "label": "End" }],
  "edges": [{ "from": "a", "to": "b", "label": "go" }]
}
  • Directions: TD/TB (top-down), LR, RL, BT.
  • Node shapes: [rect] · (round) · ([stadium]) · {diamond} · ((circle)) · {{hexagon}}.
  • Edges: --> arrow · --- line · -.-> dashed · labels via -->|text| or -- text -->.
  • Styles: style = soft (default) · solid · colorful · outline.
  • title, subtitle, caption, theme, palette, watermark and logo all apply. Limits: 200 nodes, 400 edges.

Full Mermaid (sequence, gantt, class, ER) needs a real browser to lay out — flowcharts are supported here because they can be laid out without one.

Type aliases

AliasBecomesEffect
arealineAll datasets get fill: true.
horizontalBarbarindexAxis: 'y'.
stackedBarbarx & y axes stacked: true.
mixedbarEach dataset may set its own type (bar/line).

Theme & palette

Set theme: "dark" for a dark background with light text and grid — ideal for dashboards and dark UIs. Pick a palette by name or pass your own colors:

JSON
{ "type": "bar", "values": [5, 9, 7], "theme": "dark", "palette": "sunset" }
{ "type": "line", "series": [...], "palette": ["#2730ff", "#ff4a1c"] }

Named palettes: default · cobalt · sunset · ocean · forest · candy · mono · warm · pastel · corporate.

Branding & logo

Built for teams shipping charts under their own brand. Replace the watermark text, reposition it, or overlay your own logo image.

Custom watermark — pass a string, or an object for full control:

JSON · watermark
"watermark": false                       // remove it
"watermark": "ACME Media"                // custom text
"watermark": {
  "text": "ACME Media",
  "position": "bottom-left",             // any corner / center
  "color": "#2730ff",
  "opacity": 0.4
}

Logo overlay — supply a base64 data URI (external URLs are rejected for safety):

JSON · logo
"logo": {
  "src": "data:image/png;base64,iVBORw0KG...",
  "position": "top-right",
  "height": 40,
  "opacity": 1
}

Positions: top-left · top-right · bottom-left · bottom-right · top-center · bottom-center · center.

Auto-theming

If you omit colors and labels, the renderer fills the gaps so output looks intentional out of the box:

  • Datasets without colors get a balanced brand palette (arc charts get per-segment colors).
  • A single unlabeled dataset hides its legend instead of printing undefined; multiple unlabeled datasets become Series 1, Series 2
  • Sensible line tension, point sizes, bar radius, fonts and grid colors are applied — all overridable via options.

Metadata endpoints

EndpointReturns
GET /api/v1/typesSupported types, formats and presets.
GET /api/v1/presetsAll preset configs.
GET /api/v1/presets/:keyOne preset config (e.g. bar).
GET /health · /livez · /readyzLiveness & readiness.

Limits

  • Effective canvas area is capped at 4,000,000 pixels (width × height × dpr²).
  • Up to 50 datasets, 5,000 points per dataset.
  • Request body capped at 512 KB.
  • Anonymous rate limit applies per IP; identical requests are served from cache (X-Cache: HIT).

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