Saved charts
Durable chart documents with stable image, embed, edit, and management URLs.
A saved chart gives one durable chart identity to a published document, an optional newer Studio draft, metadata, and stable delivery URLs. Updating the document does not require replacing links already used in email, a product, or a report.
When to use which
| Need | Use |
|---|---|
| Render one image without keeping identity | POST /chart |
| Publish stable image and embed URLs | POST /api/charts |
| Edit visually and preserve unpublished work | Studio or the web editor |
| Replace a published document in place | PUT /api/charts/{id}/document |
| Show a responsive interactive chart | /e/{id} |
| Share a human-facing chart page | /v/{id} |
Saved-chart creation itself does not consume render allowance. An origin cache miss for a public image or embed does.
The chart object
Create, list, read, update, and rename return the same metadata-and-links representation:
type SavedChart = {
id: string;
source: string;
title: string;
createdAt: string;
updatedAt: string;
sizeBytes: number;
publishedAt: string | null;
imageUrl: string;
embedUrl: string;
documentUrl: string;
};source is an open set. Current first-party values are api, app, figma, and mcp. List items add hasDraft so Studio can identify unpublished edits without reading document bytes.
publishedAt: null means public image and embed URLs are dark. A chart can be draft-only, or it can retain storage history after being unpublished. createdAt, updatedAt, and non-null publishedAt are ISO-8601 strings.
The object never embeds the document. Fetch the owner-only documentUrl when you need authored state.
Document state
GET /api/charts/{id}/document returns:
type SavedChartDocumentState = {
document: ChartDocument | null;
draft: ChartDocument | null;
publishedAt: string | null;
title: string;
};document is the published revision. draft is a newer unpublished Studio revision when present. A draft-only chart has document: null and no public image or embed.
Both values are returned as current documents. Older stored versions are converted when supported. A temporary storage failure returns 503 and never substitutes the draft for the publication.
Authentication and ownership
Saved-chart API operations accept either a Bearer API key or the dashboard session, unless noted otherwise. Every metadata and document read is owner-scoped. A missing or unowned id returns 404 so callers cannot test whether another user's id exists.
Public /c/{id}, /e/{id}, and /v/{id} URLs are opaque capabilities. Anyone holding a published URL can access that output until it is unpublished or deleted and caches expire.
API reference
Create a chart – POST /api/charts
Create and publish a current document:
curl 'https://szum.io/api/charts' \
--fail-with-body \
--max-time 30 \
-X POST \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: report-revenue-2026-q1' \
-d '{
"document": {
"version": "2026-09-26",
"config": {
"version": "2026-09-26",
"type": "column",
"category": { "field": "quarter" },
"values": { "type": "long", "field": "revenue" }
},
"data": [
{ "quarter": "Q1", "revenue": 42 },
{ "quarter": "Q2", "revenue": 58 }
],
"output": { "format": "svg" }
},
"source": "api"
}'Optional title sets the library title independently from the title rendered inside the document. If omitted, creation derives a useful title from the document. source defaults to api.
Use one idempotency key per intended chart and reuse it on retries. HTTP create deduplicates by key alone, so changing the body while reusing a key still refers to the original create. Use a fresh key for a different chart. A concurrent retry returns 409 with Retry-After; retry the same key after the requested delay. Omitting the key creates a new identity on each successful request.
Dashboard-session callers may pass draft: true to create a draft-only editor chart. Bearer callers cannot; draft creation is an editor workflow, not part of the public API.
List charts – GET /api/charts
GET /api/charts?source=api,mcp&sort=updated&q=revenue&limit=100&cursor=...The response is { items, nextCursor }, plus total when search is active or includeTotal=1 is requested.
sourceaccepts one or more ofapi,app,figma, andmcp.sortiscreated,updated, ortitle; default iscreated.qis a normalized title substring.limitis 1–1000; default is 100.cursoris opaque and must be reused with the same sort.
Listing reads metadata only. It does not load chart documents or consume render allowance.
Get chart metadata – GET /api/charts/{id}
Returns one chart object. It does not read or return document bytes.
Get one document state – GET /api/charts/{id}/document
Returns the published document, optional newer draft, publication timestamp, and library title. The response is private, no-store.
Get documents in batch – GET /api/charts/documents
GET /api/charts/documents?ids=ID_1,ID_2,ID_3Reads up to 100 owner-scoped preview documents:
type SavedChartDocuments = {
documents: { id: string; document: ChartDocument }[];
missing: { id: string; reason: "not_found" | "unavailable" }[];
};The preview read prefers a newer draft when present. not_found covers absent or unowned ids, draft-only rows without a draft, and missing or owner-mismatched publication data. unavailable covers temporary storage failures and data that cannot be read or converted. A structurally readable current document can still open in Studio for repair, but it must pass validation before rendering or saving.
Replace a document – PUT /api/charts/{id}/document
Send { document } to publish a replacement while keeping the chart id and all public URLs stable.
Szum writes the replacement before making it the published revision, updates updatedAt and publishedAt, and schedules the previous revision for cleanup. If publication fails, the existing revision remains current.
Session callers promote and clear their matching Studio draft. Bearer callers preserve drafts and receive 409 if unpublished editor work exists. Read current state and resolve that conflict instead of overwriting it.
Save or discard a Studio draft
PUT /api/charts/{id}/draftDELETE /api/charts/{id}/draft
These endpoints are session-only. They store or clear the owner-scoped unpublished document without publishing, purging CDN, charging render quota, or changing public URLs.
Unpublish – POST /api/charts/{id}/unpublish
This endpoint is session-only. Unpublishing keeps chart identity, metadata, stored publication history, and Studio access while making /c/{id}, /e/{id}, and /v/{id} unavailable. Publishing a later replacement brings the same URLs back.
Rename – PATCH /api/charts/{id}
Changes only the library title. It does not change config.title, render output, public URLs, publication bytes, or draft state.
Delete – DELETE /api/charts/{id}
Permanently removes the chart identity, revokes origin access, records owned Blob cleanup, and requests CDN purge. The HTTP route returns 404 when the owner-scoped id is already absent; SDK and MCP adapters may expose idempotent delete behavior by treating that result as success.
Account settings can delete all saved charts in one bounded operation. Do not loop over public per-chart deletes when performing account cleanup.
Older configs and loss acceptance
New integrations send { document }. Create and replace also accept one supported 2026-03-20 { config }.
Supported older input is classified as exact, normalized, or lossy before anything is saved. A lossy response lists stable codes; retry with acceptLosses containing every approved code. Unknown or incomplete acceptance is rejected. Unsupported input is not written.
Supported older publications and drafts continue to open without changing their stored definition. Invalid or unsupported chart data is preserved instead of being silently replaced. See older config compatibility.
Rendering
Image – GET /c/{id}
The bare URL uses the document's saved output preference. Add .svg or .png to request a specific format:
<img src="https://szum.io/c/CHART_ID.svg" alt="Quarterly revenue" />Query-string variants redirect to the canonical URL so arbitrary parameters cannot fragment cache identity or drain the owner's allowance.
Interactive embed – GET /e/{id}
<iframe
src="https://szum.io/e/CHART_ID"
title="Quarterly revenue"
loading="lazy"
></iframe>The embed is responsive and adds tooltips and compatible legend filtering to the same saved chart. See Interactive embeds.
Public chart page – GET /v/{id}
The share page adds Szum chrome and OpenGraph metadata. It is not indexable or framable. The page itself is not metered; its /c/{id}.png social preview follows normal image accounting when fetched on an origin cache miss.
Caching and updates
Successful saved images and embed HTML are publicly cached for 24 hours. CDN hits do not reach origin and do not consume another render. Update, unpublish, and delete request cache purges; if a purge fails, previously cached output can remain available until cache expiry.
Public failures are private, no-store. A temporary Blob problem is never cached as the chart's durable public state.
Document size
Chart requests accept at most 56 KB. Durable document transport and both compressed and decoded saved representations are bounded independently at 64 KB.
These are transport and storage safety limits, not data-display limits. Szum does not truncate rows to satisfy them. Reduce unnecessary source data or split the chart intentionally.
Plan and storage
All signed-in plans can save and publish charts. Free and Creator share the standard storage budget; Pro has the larger storage budget. Publication rejects a new write before replacing the current revision when the account cap would be exceeded.
The account storage budget sums current compressed publication bytes; sizeBytes reports that published size and is 0 for a draft-only chart. Drafts are independently bounded by the per-document storage-safety limit but do not add to the account publication budget. Replaced and deleted storage objects are cleaned up asynchronously; cleanup failure does not change which publication is current.
On downgrade
Saved charts stay live. A Free account keeps the authored document but delivery suppresses custom theme overrides and adds attribution. Upgrading restores permitted overrides because downgrade never rewrites the document.
Storage over the new-plan budget blocks new publication growth; it does not delete existing charts automatically. Remove unused charts or upgrade before publishing more.
Errors and conflicts
400invalid body, id, query, cursor, semantic document, or loss acceptance.401missing or invalid authentication.403current plan cannot publish requested theme overrides.404chart absent or not owned.409pending create, identity collision, draft conflict, or conditional publication conflict.413document or account storage limit.429burst limit.500application failure.503temporary publication, database, or Blob read failure.
No route silently swaps publication and draft state to avoid an error. See Errors for retry guidance.
Rate limits
Metadata and document routes use the ordinary edge and credential burst limits but do not consume monthly render allowance. Public image and embed origin cache misses consume the chart owner's render allowance. CDN hits, draft autosave, create, list, metadata reads, document reads, rename, unpublish, and delete do not.