Venn Diagram API
Create diagrams with 2–6 circles using HTTP. No account or API key. The editor uses JavaScript; this documentation and the APIs do not.
Generate an SVG with one URL
GET /api/venn.svg accepts a (A only), b (B only), ab (A∩B), and optional colorA / colorB (#rrggbb). This friendly mode always uses two circles; other state fields use product defaults. Missing or empty text leaves the region blank; no query produces an empty diagram. Colors may be omitted but cannot be empty.
https://venn.applepig.net/api/venn.svg?a=%E8%97%A5%E5%93%81%E6%BF%AB%E7%94%A8&b=%E7%89%A9%E8%B3%AA%E6%BF%AB%E7%94%A8&ab=%E6%AF%92%E5%93%81&colorA=%23E76F51&colorB=%232A9D8F
Use URLSearchParams or a URL encoder. Encode # as %23 and + as %2B; unencoded # starts a fragment and never reaches the server. For full styles and shapes, use /api/venn.svg?s=<state>. Do not mix s with friendly parameters. Duplicate or unknown parameters return 400; the serialized query including ? is limited to 22156 characters. Success returns image/svg+xml; charset=utf-8 and X-Venn-Url for the same editable diagram. Fonts are not embedded: use PNG when identical glyph rendering across readers is required. SVG serialization does not use the PNG worker quota.
Generate a PNG in one request
POST /api/png accepts a complete VennState JSON body. Save the response as PNG. The X-Venn-Url response header gives an editable share link for the same diagram.
curl -sD headers.txt -o venn.png -H 'content-type: application/json' \
--data '{
"v": 1,
"n": 2,
"style": "translucent",
"opacity": 0.6,
"overlap": 1.2,
"radius": 0.3,
"colors": [
"#2e9be6",
"#e6a92e"
],
"bg": "#fafafa",
"size": 400,
"texts": {
"1": {
"t": "Coffee"
},
"2": {
"t": "Sleep"
},
"3": {
"t": "Me at 3am"
}
}
}' \
'https://venn.applepig.net/api/png'Inspect headers.txt for X-Venn-Url. To fetch the diagram again, reuse its s parameter with GET.
Fetch a diagram by URL
GET /api/png?s=<state> returns a square PNG at state.size pixels. s is base64url without padding of raw DEFLATE of UTF-8 JSON, not gzip or zlib-wrapped DEFLATE. Copy it from an editor/share link, or generate it with Node:
import { deflateRawSync } from 'node:zlib';
const state = {
"v": 1,
"n": 2,
"style": "translucent",
"opacity": 0.6,
"overlap": 1.2,
"radius": 0.3,
"colors": [
"#2e9be6",
"#e6a92e"
],
"bg": "#fafafa",
"size": 400,
"texts": {
"1": {
"t": "Coffee"
},
"2": {
"t": "Sleep"
},
"3": {
"t": "Me at 3am"
}
}
};
const s = deflateRawSync(Buffer.from(JSON.stringify(state), 'utf8')).toString('base64url');
console.log(new URL('/api/png?s=' + s, "https://venn.applepig.net").href);
Complete state
Required: v, n, style, opacity, overlap, radius, colors, bg, size, texts. Missing required fields return 400; they do not receive defaults. arr defaults to ring. Colors must contain exactly n entries. Title and region text have a 80-Unicode-code-point limit; newlines wrap text. XML-invalid characters are rejected. fs and title_fs are canvas-width fractions (0.001–1). fill affects only flat regions. Unknown state fields are ignored for old-link compatibility.
Optional: arr, title, title_fill, title_fs, stroke_width, stroke. Title styling is kept only with a nonempty title; stroke defaults depend on style, and stroke color is ignored when the resolved width is zero.
v | integer; must be 1 |
|---|---|
n | integer; 2–6 |
arr | string; ring / row |
style | string; translucent / flat / outline |
opacity | number; 0–1; Required for all styles; only translucent uses it. |
overlap | number; 0.6–1.6 |
radius | number; Canvas-width fraction; ring 0.2–0.35, row 0.1–0.35. See shape-specific constraints. |
colors | array; exactly n #rrggbb colors |
bg | string; #rrggbb color |
size | integer; 400–2000 |
texts | object; decimal region keys → {t, fs?, fill?}; see the region table below |
title | string; up to 80 code points |
title_fill | string; #rrggbb color |
title_fs | number; 0.001–1 |
stroke_width | number; 0–0.03 |
stroke | string; #rrggbb color |
Region keys
texts maps decimal bitmasks to {"t":"words","fs":0.09,"fill":"#ffffff"}; fs and fill are optional. Circle i is bit i: 1 = A only, 2 = B only, 3 = A∩B, 7 = A∩B∩C. These describe exclusive regions, not whole-set membership. Empty texts: {} is valid. Legal keys are computed from the shape:
ring(2) 1 2 3 ring(3) 1 2 4 3 5 6 7 ring(4) 1 2 4 8 3 5 10 12 7 11 13 14 15 ring(5) 1 2 4 8 16 3 6 12 17 24 7 14 19 25 28 15 23 27 29 30 31 ring(6) 1 2 4 8 16 32 3 6 12 24 33 48 7 14 28 35 49 56 row(3) 1 2 4 3 6 row(4) 1 2 4 8 3 6 12 row(5) 1 2 4 8 16 3 6 12 24 row(6) 1 2 4 8 16 32 3 6 12 24 48
ring(2) and ring(3) support every combination. Other shapes may lack overlaps; for example row(3) has no 5 or 7. An unavailable slot is a 400. Rearrange circles or use fewer circles when necessary.
Social image
GET /api/og.png?v=6&s=<state>&lang=en returns a branded 1200 × 630 PNG, rather than the square diagram. Without s it renders a sample. Language uses only the query (zh-TW, en, ja); cookies and Accept-Language do not change a cached image. v is a layout cache buster, not the state version.
Errors and limits
Expected API errors are English JSON {"error":"..."} with Cache-Control: no-store. 400 means invalid/missing input; 413 means the POST body exceeds 32768 bytes. Encoded s is limited to 7300 characters, and decompressed JSON to 32768 bytes. PNG and OG share 3 concurrent rasterizers: 503 with Retry-After: 2 means wait and retry the same request. A missing OG base image returns 500.
Caching
GET PNG/OG and SVG s mode use public, max-age=31536000, immutable; POST PNG is no-store and renders on every call. Friendly SVG mode is no-cache because product defaults can change. Produce with POST, then reuse GET for repeat fetches. Renderer or watermark changes may leave previously cached images visible at unchanged URLs; deployments must account for cache invalidation.
Other interfaces
Use the agent guide for CLI and plugin instructions, or read the source and README.