Developers · REST API v1

The diagram APIbuilt for code & agents.

One POST turns Mermaid or PlantUML source into a presentation-ready SVG or PNG. Drop it into CI, commit the output next to your code, or wire it into agent pipelines — same engine that powers the editor.

<2s
render budget
4
input formats
SVG / PNG
outputs
bash · ~/project
$ curl -X POST https://api.beauty-diagram.com/v1/beautify \
  -H "authorization: Bearer bd_live_..." \
  -d '{"source":"flowchart TD\nA-->B", "sourceFormat":"mermaid"}'

# → 200 OK application/json
{
  "ok": true,
  "diagramType": "flowchart",
  "svg": "<svg xmlns=...>...</svg>",
  "usage": { used: 7, limit: 100 }
}
Same engine the editor uses · no separate API quota
MermaidPlantUMLdraw.ioSVG
Overview

What you can build

The Beauty Diagram API takes diagram source (Mermaid, PlantUML, draw.io, or SVG), applies the same beautify pipeline as the editor, and returns a clean, deck-ready vector. Three things people typically wire it into:

CI / docs pipelines
Commit .mmd, regenerate the SVG on push, never hand-edit a diagram again.
Agents & LLM tools
Let an agent emit Mermaid, get a polished image back in one round trip.
Internal tools
Render architecture / runbook diagrams from your own data on demand.
01

Quick start

Authenticated request. Replace bd_live_... with your key from /account/api-keys. Or skip the header for a watermarked anonymous demo (rate limited by IP).

curl -X POST https://api.beauty-diagram.com/v1/beautify \
  -H "authorization: Bearer bd_live_..." \
  -H "content-type: application/json" \
  -d '{
    "source": "flowchart TD\nA-->B",
    "sourceFormat": "mermaid",
    "theme": "modern"
  }'
💡 Pair the API with the bd CLI to keep diagrams checked-in next to your code — see CLI section.
02

Authentication

Three identities are accepted. Pick the smallest one that covers your use case.

Recommended
Personal access token
Create at /account/api-keys. Pick scopes; the raw key shows once. Pass as Authorization: Bearer bd_live_....
Browser
Web session
Front-end code in your own app can hit /v1/* directly using the user's Supabase session cookie. No extra setup.
No key
Anonymous demo
Skip the Authorization header — falls through to the demo bucket. Watermarked output, capped at 20 beautify requests / minute / IP and 1 export / 24h / IP. Denials include a hints block with sign-up / sign-in URLs so agents can self-onboard.
03

Scopes

Tokens are issued with explicit scopes. Request only what you need.

ScopeCapability
render:writeBeautify diagrams
export:writeExport SVG and PNG files
share:writeCreate public share links
ai:writeGenerate Mermaid via AI (Pro/Premium only)
Reference

Endpoint matrix

Every /v1/* route at a glance — auth, plan-gated capabilities, monthly quota, and the limits applied to each.

EndpointAuthScopeQuotaLimits
POST/v1/beautify
Optionalrender:writeNot metered
120/min/keyIP-rate-limited (anon)
POST/v1/export
Optionalexport:writeFree 3 · Pro 100 · Premium ∞ /mo
120/min/keyIP-rate-limited (anon)1 export / IP / 24h trial
POST/v1/share
Requiredshare:writePlan diagram cap (free 5 · pro 100 · premium ∞)
120/min/key
POST/v1/ai/generate
Requiredai:writePro 100 · Premium 500 /mo (free not allowed)
30/min/key
GET/v1/themes
None
120/min/keyIP-rate-limited (anon)
GET/v1/usage
Required
120/min/key
GET/v1/beautify.svg
None
300/min/IP (anon)source ≤ 5 KB decoded
GET/v1/share/<id>.svg
None (public shares)
ETag / 304 revalidations-maxage=300

PNG scaleis plan-gated separately: anonymous & free 1×, pro 2×, premium 4×. Higher requests are silently clamped — see X-BD-Scale-Clamped in the response headers reference below.

Reference

Endpoints

All endpoints return JSON (unless explicitly noted, like /v1/export which streams binary). Authenticate with a bearer token on every request.

Base URL
https://api.beauty-diagram.com

Prefix every path below with this origin. CORS is wide open — browser, server, and CLI clients all use the same host.

Auth header
Authorization: Bearer bd_live_...

Personal access token created at /account/api-keys. The same header accepts a Supabase session JWT for browser callers.

POST/v1/beautifyRender a diagram, return inline SVG

Beautify and render a diagram. Returns the SVG inline so you can preview it immediately or persist it yourself. Anonymous calls are accepted but receive a watermarked SVG.

Request body
FieldTypeDescription
source
required
stringDiagram source code. UTF-8, max 100 KB.
sourceFormat
required
"mermaid" | "plantuml" | "drawio" | "svg"Tells the parser how to interpret source.
theme
optional
string
default "modern"
Theme id from GET /v1/themes.
Response (200)
FieldTypeDescription
ok
optional
trueSuccess indicator.
diagramType
optional
stringDetected type (e.g. "flowchart", "sequence").
svg
optional
stringRendered SVG markup, ready to inline or save.
meta
optional
object{ theme, width, height, hasWatermark } — render metadata.
usage
optional
objectExport counter snapshot: { used, limit, resetsAt }.
Example
POST /v1/beautify
Authorization: Bearer bd_live_...
Content-Type: application/json

{
  "source": "flowchart TD\nA-->B",
  "sourceFormat": "mermaid",
  "theme": "modern"
}

→ {
  "ok": true,
  "diagramType": "flowchart",
  "svg": "<svg ...>",
  "meta": { "theme": "modern", "width": 720, "height": 480, "hasWatermark": false },
  "usage": { "used": 7, "limit": 100, "resetsAt": "2026-05-01T00:00:00Z" }
}
POST/v1/exportBinary file download (SVG or PNG)

Returns the rendered file as a binary download (Content-Type: image/svg+xml or image/png) so curl -o file.svg just works. Metadata travels in response headers so the body stays pristine. Pass Accept: application/json or ?response=json to opt into the legacy JSON wrapper.

Request body
FieldTypeDescription
source
required
stringDiagram source code, UTF-8, ≤ 100 KB.
sourceFormat
required
"mermaid" | "plantuml" | "drawio" | "svg"Source language.
format
optional
"svg" | "png"
default "svg"
Output format.
scale
optional
1 | 2 | 4
default 1
PNG scale; clamped to plan cap (anon/free 1×, pro 2×, premium 4×). Reported via X-BD-Scale-Clamped.
theme
optional
string
default "modern"
Theme id (see /v1/themes).
Response (200)

Binary body. Read state from response headers (full reference): X-BD-Diagram-Type, X-BD-Theme, X-BD-Quota-Used, X-BD-Quota-Limit, X-BD-Quota-Resets-At, X-BD-Scale-Clamped, Content-Disposition.

Example
POST /v1/export
Authorization: Bearer bd_live_...
Content-Type: application/json

{ "source": "...", "sourceFormat": "mermaid", "format": "svg" }

→ HTTP/1.1 200 OK
   Content-Type: image/svg+xml; charset=utf-8
   Content-Disposition: attachment; filename="diagram-flowchart.svg"
   X-BD-Diagram-Type: flowchart
   X-BD-Theme: modern
   X-BD-Quota-Used: 7
   X-BD-Quota-Limit: 100
   X-BD-Quota-Resets-At: 2026-05-01T00:00:00Z

   <svg xmlns="..."> ... </svg>
More examples (filename auto-pick, PNG @ 4×)

Let the server pick the filename via curl's -OJ:

curl -OJ -X POST https://api.beauty-diagram.com/v1/export \
  -H "authorization: Bearer bd_live_..." \
  -H "content-type: application/json" \
  -d '{ "source": "...", "sourceFormat": "mermaid", "format": "svg" }'

# saves ./diagram-flowchart-a3f9k2.svg

PNG @ 4× for premium plan:

curl -OJ -X POST https://api.beauty-diagram.com/v1/export \
  -H "authorization: Bearer bd_live_..." \
  -H "content-type: application/json" \
  -d '{ "source": "...", "sourceFormat": "mermaid", "format": "png", "scale": 4 }'

Filenames default to diagram-<type>-<sourceHash6>.<ext> so two different sources never collide and the same source is idempotent. Override with ?filename=my-name.

POST/v1/shareSave source, return public URL

Save the source as a shared diagram and return a public URL. Authenticated only (free plan or above). Counts against your plan's diagram cap.

Request body
FieldTypeDescription
source
required
stringDiagram source, ≤ 100 KB UTF-8.
sourceFormat
required
"mermaid" | "plantuml" | "drawio" | "svg"Source language.
title
optional
stringDisplay title (≤ 120 chars). Used to derive the slug segment of shareUrl.
theme
optional
stringStored alongside the source so collaborators see the same render.
Response (200)
FieldTypeDescription
ok
optional
trueSuccess indicator.
diagramId
optional
string (uuid)Stable id you can store and re-reference.
shareToken
optional
stringRandom token embedded in the URL — keep secret if you want the link to stay private.
sharePath
optional
stringSite-relative path (e.g. "/s/release-flow-x4f9").
shareUrl
optional
stringAbsolute URL on www.beauty-diagram.com.
Example
POST /v1/share
Authorization: Bearer bd_live_...
Content-Type: application/json

{ "source": "...", "sourceFormat": "mermaid", "title": "Release flow" }

→ {
  "ok": true,
  "diagramId": "8e1d…",
  "shareToken": "k4m9…",
  "sharePath": "/s/release-flow-x4f9",
  "shareUrl": "https://www.beauty-diagram.com/s/release-flow-x4f9"
}
POST/v1/ai/generateNatural language → diagram source

Generate a diagram from a plain-language prompt. Returns Mermaid source text, not an image — pipe the result into /v1/beautify or /v1/export to render. Paid-only (Pro / Premium); anonymous and free callers are rejected before any model call.

Output language is currently Mermaid. The endpoint name is intentionally generic so we can extend the output format without a versioned rename.

Request body
FieldTypeDescription
prompt
required
stringPlain-language description of the diagram you want (e.g. “user signup with email verification”). Trimmed; ≤ 2000 chars.
hint
optional
"flowchart" | "sequence" | "state" | "class" | "er"Optional shape hint. Improves output for prompts that are ambiguous about the target diagram type.
Response (200)
FieldTypeDescription
ok
optional
trueSuccess indicator.
mermaid
optional
stringGenerated diagram source — already validated by the parser before being returned.
diagramType
optional
stringDetected diagram type from the generated source.
requestId
optional
string (uuid)Tracing id; include when reporting issues.
quota
optional
objectMonthly AI bucket after this call: { limit, used, resetsAt }. Quota only increments on success.
Errors

In addition to the generic codes (Errors), this endpoint can return prompt_injection (input looked like an injection attempt), parse_failed_after_retry (output unparseable after one retry — quota not consumed), safety_blocked, upstream_timeout, and upstream_error.

Example
POST /v1/ai/generate
Authorization: Bearer bd_live_...
Content-Type: application/json

{
  "prompt": "user signup with email verification",
  "hint": "flowchart"
}

→ {
  "ok": true,
  "mermaid": "flowchart TD\n  A[Sign up] --> B{Email valid?}\n  ...",
  "diagramType": "flowchart",
  "requestId": "1f3e…",
  "quota": { "limit": 100, "used": 23, "resetsAt": "2026-05-01T00:00:00Z" }
}
Tighter rate limit: 30 requests / min / key (vs 120 elsewhere). Each call is a real model invocation, so the inner cap protects both you and the AI service from accidental burst loops.
GET/v1/themesList available themes

Build a theme picker that always reflects what the engine actually supports. Public — no auth needed.

Request

No body, no query parameters.

Response (200)
FieldTypeDescription
ok
optional
trueSuccess indicator.
themes
optional
Theme[]Array of { id, name, tone, description }. id is what other endpoints accept as theme; tone is one of "light" / "dark" for UI grouping.
Example
GET /v1/themes

→ {
  "ok": true,
  "themes": [
    { "id": "modern", "name": "Modern", "tone": "light", "description": "Editor default." },
    { "id": "midnight", "name": "Midnight", "tone": "dark", "description": "Slide-deck dark." }
  ]
}
GET/v1/usagePlan, export counter, AI counter

Returns the actor's plan and per-feature counters. Use it before running expensive batches so you can pre-empt quota_exhausted. Authenticated only.

Request

No body, no query parameters.

Response (200)
FieldTypeDescription
ok
optional
trueSuccess indicator.
plan
optional
"free" | "pro" | "premium"Plan tier of the resolved actor.
actor
optional
"user" | "api_key"How the request was authenticated.
exports
optional
object{ plan, used, limit, resetsAt } — monthly export bucket. limit is null for unlimited (premium).
ai
optional
object{ enabled, limit, used, resetsAt } — monthly AI generation bucket. enabled is false on free plan (limit null); on Pro/Premium it reflects the current month's consumption. The bucket is keyed on the owner user, so multiple API keys share one counter.
Example
GET /v1/usage
Authorization: Bearer bd_live_...

→ {
  "ok": true,
  "plan": "pro",
  "actor": "api_key",
  "exports": { "plan": "pro", "used": 7, "limit": 100, "resetsAt": "2026-05-01T00:00:00Z" },
  "ai":      { "enabled": true, "used": 23, "limit": 100, "resetsAt": "2026-05-01T00:00:00Z" }
}
GET/v1/beautify.svgAnonymous inline embed — returns SVG directly

Render a Mermaid diagram and return it as image/svg+xml, with no auth required — designed to be used as an <img src> URL for quick embeds in READMEs, Notion, and blog posts. A watermark is always applied. Any token passed in the query string is ignored; this endpoint is intentionally anonymous.

On parse failure the response is still HTTP 200 with a fallback SVG explaining the error — because <img> consumers cannot display diagnostic messages for 4xx responses. To get clean (watermark-free) output, save the diagram with POST /v1/share and embed using GET /v1/share/<id>.svg instead.

Query parameters
FieldTypeDescription
source
required
stringBase64url-encoded Mermaid source. Decoded payload must be ≤ 5 KB. Use base64url (URL-safe alphabet, no padding) or standard base64 — both are accepted.
theme
optional
string
default "modern"
Theme id from GET /v1/themes.
Status codes
StatusMeaning
200 image/svg+xmlSuccessful render with watermark, OR fallback SVG on parse failure (HTTP 200 by design — <img> consumers cannot show 4xx diagnostics).
413Decoded source exceeds the 5 KB limit.
429Rate limit hit (300 req / min / IP). Retry-After header included.
Cache

Cache-Control: public, max-age=3600, s-maxage=86400 — no immutable, because cross-deploy renders can differ when the engine is updated.

Example
curl "https://api.beauty-diagram.com/v1/beautify.svg?source=$(echo -n 'graph TD; A-->B' | base64)" > diagram.svg

# Markdown / HTML embed (watermarked, no signup required):
# ![Diagram](https://api.beauty-diagram.com/v1/beautify.svg?source=Z3JhcGg...)
# <img src="https://api.beauty-diagram.com/v1/beautify.svg?source=Z3JhcGg..." />
Rate limit is 300 req / min / IP — higher than the 20/min default for other anonymous routes because image proxies (GitHub Camo, Notion, Medium) concentrate many reader-side requests onto a small number of egress IPs.
GET/v1/share/<id>.svgSaved-share embed — returns SVG with ETag caching

Render the diagram saved by POST /v1/share and return it as image/svg+xml. Supports If-None-Match / ETag revalidation. Edits to the saved diagram propagate within ~5 minutes (browser ETag revalidation + CDN edge TTL).

Watermark behavior is tied to the owner's plan at render time: free owner → watermark applied; pro / premium owner → clean SVG. Per-node colors, edge presets, and font overrides set in the canvas editor are faithfully rendered. Animations are not forwarded — browsers do not run animations in <img>-loaded SVGs.

Path parameter
FieldTypeDescription
<id>
required
stringAccepts both the raw 12-char base62 shareToken (e.g. abc12345xyz0) and the slugified form <title-slug>-<token> that appears in share URLs (e.g. release-flow-abc12345xyz0).
Query parameters (all optional)
FieldTypeDescription
theme
optional
stringSilently override the saved theme — useful for matching the host page's dark/light environment.
Status codes
StatusMeaning
200 image/svg+xmlRender of the saved diagram. Watermark present iff the owner's plan is free.
304 Not ModifiedIf-None-Match matched the current ETag — body is empty.
404Share not found, archived, soft-deleted, or private. Private shares always 404 from this endpoint by design — embed is anonymous and cannot resolve viewer identity. Private share access is grant-based on the web /s/<slug> page.
Cache

Cache-Control: public, max-age=300, s-maxage=300, stale-while-revalidate=600. ETag-based revalidation. Saved diagram edits propagate to direct <img>embeds within ~5 minutes. GitHub README embeds may lag a few hours due to GitHub's image proxy cache — that is a GitHub-side cache, not something the API can purge.

Example
# Download the SVG (inspect headers for ETag)
curl -i "https://api.beauty-diagram.com/v1/share/abc12345xyz0.svg" > diagram.svg

# Slugified form also works:
curl -i "https://api.beauty-diagram.com/v1/share/release-flow-abc12345xyz0.svg"

# Markdown embed (clean if owner is Pro/Premium):
# ![Architecture](https://api.beauty-diagram.com/v1/share/abc12345xyz0.svg)
#
# Override theme without re-saving:
# ![Architecture](https://api.beauty-diagram.com/v1/share/abc12345xyz0.svg?theme=midnight)
Operations

Limits, rate limiting & headers

The web app and the API share a single export counter — there is no separate "API quota". Rate limits are enforced cross-instance for API keys and IP-based for anonymous demo traffic.

Anonymous
Rate · Rate-limited per IP
1 export / IP / 24h trial
Free
Rate · 120 req/min/key
3 exports / month
Popular
Pro
Rate · 120 req/min/key
100 exports / month
Premium
Rate · 120 req/min/key
Unlimited exports
Rate limiting
Authenticated (API key or session)

120 requests / minute / key, fixed window. The counter is shared across the entire /v1/* namespace and persists across function instances.

Over-budget calls get 429 rate_limited with a Retry-After header and a rateLimit object describing window state.

Anonymous demo

Rate-limited per IP — exact ceiling for most anonymous routes is intentionally undocumented to discourage abuse. Sign in or use an API key for higher allowances.

Exception — GET /v1/beautify.svg: the anonymous cap is raised to 300 req / min / IP because image proxies (GitHub Camo, Notion, Medium) concentrate reader traffic onto a small number of egress IPs, making the standard 20/min threshold a false positive.

A 1-export-per-IP-per-24h trial budget on /v1/export lets agents and evaluators verify the toolchain end-to-end before registering. Denials carry a hints block with signUpUrl / signInUrl / apiDocsUrl.

Sample 429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 47
Content-Type: application/json

{
  "ok": false,
  "error": "rate_limited",
  "message": "API key rate limit (120 requests / 60s) reached. Retry after 2026-04-27T04:31:12Z.",
  "retryAfterMs": 47000,
  "rateLimit": {
    "limit": 120,
    "windowSeconds": 60,
    "used": 121,
    "resetsAt": "2026-04-27T04:31:12Z"
  }
}
Response headers

Metadata that does not fit in the JSON body — useful for CLI / batch pipelines that need to read state without parsing the response.

HeaderWhereMeaning
X-BD-Diagram-Type/v1/exportDetected type — flowchart, sequence, etc.
X-BD-Theme/v1/exportTheme that produced the output.
X-BD-Watermark/v1/exporttrue when the badge is baked in.
X-BD-Scale/v1/export (PNG)Effective scale (1, 2, or 4).
X-BD-Scale-Clamped/v1/export (PNG)true when scale was lowered to plan cap.
X-BD-Quota-Plan/v1/exportPlan tier of the actor.
X-BD-Quota-Used/v1/exportExports used in the current month.
X-BD-Quota-Limit/v1/exportPlan limit, or unlimited.
X-BD-Quota-Resets-At/v1/exportISO timestamp when the counter resets.
Retry-AfterAny 429Seconds until the next attempt is safe.
Body limits

source on render endpoints: 100 KB (UTF-8 bytes). Oversize → source_too_large (HTTP 413).

Encoding

UTF-8 only; CJK and emoji labels supported in SVG. PNG raster ships Latin glyphs only — CJK falls back to tofu boxes. Newlines must be JSON-escaped as \n.

Timeouts

POST routes carry maxDuration = 60s; render aims for <2s. PNG worst case (4× composite) is ~1.1s plus ~1.5s cold start.

Operations

Errors

Every error is JSON of the shape { ok: false, error, message }. Anonymous-actor denials additionally carry hints: { signUpUrl, signInUrl, apiDocsUrl } so CLIs and agents can guide the user to register without scraping these docs. Codes you should handle:

CodeMeaning
invalid_inputMalformed body or wrong types.
not_authenticatedNo key, no session.
scope_missingKey lacks the required scope.
plan_not_allowedPlan does not include this capability.
parse_failedSource did not parse as the declared format.
quota_exhaustedLimit reached for this period.
rate_limitedPer-key (120/min, 30/min for AI) or anonymous IP bucket is full.
output_too_largeRasterized PNG exceeds the 8192 px ceiling — lower scale or simplify.
prompt_injectionAI input matched a prompt-injection pattern; rejected before any model call.
instruction_rejectedAI: prompt did not describe a diagram (off-topic). Quota not consumed.
parse_failed_after_retryAI: model output unparseable after one retry. Quota not consumed.
safety_blockedAI: provider safety filter rejected the request. Rephrase the prompt.
upstream_timeoutAI: provider took too long. Safe to retry after a moment.
upstream_errorAI: provider failed for an unknown reason.
Operations

Privacy

  • We do not persist your source or AI instruction unless you explicitly call /v1/share.
  • Logs store a hash, length, format, and diagram type — never the raw input.
  • You can revoke any API key from /account/api-keys. Revocation takes effect immediately.
Tooling

CLI

The bd CLI wraps the API for repo-friendly use — same auth, same quotas, same engine, zero runtime dependencies. It runs single diagrams, parallel batches, and embeds Mermaid blocks straight back into your Markdown.

Single diagram
Beautify, export, or share one .mmd / .puml file.
Batch render
New
Render every diagram under a directory in parallel.
Embed in Markdown
New
Render fenced mermaid blocks to sidecar SVGs that GitHub actually displays.
# zero-install
npx @beauty-diagram/cli --help

# global install
npm install -g @beauty-diagram/cli

Ready to ship?

Create a key, paste the curl from above, and you'll have your first rendered SVG inside a minute. Free plan includes 3 watermark-free exports a month — enough to wire up CI.