diagramkit

diagramkit API reference

Make, change, animate and download diagrams from your own code. Every endpoint, parameter, response and error, with examples in curl, JavaScript and Python. Get an API key.

The same reference as plain Markdown, for coding agents: https://diagramkit.ai/docs/api.md.

Before you start

  • The base URL is https://app.diagramkit.ai/api/v1. Requests and responses are JSON, except downloads, which are the file itself.
  • Create a key at https://app.diagramkit.ai/settings/api and send it on every request, downloads included: Authorization: Bearer aid_…. A key is shown once; store it as a secret and revoke it there if it leaks.
  • Call the API from your server or a script. Browsers are not allowed to call it from other sites, and a key in a web page is a leaked key.
  • Diagrams made with the API appear in your diagrams in the app and use your brand kit and writing style.

Credits

The API is paid in credits, from the same balance as the app and the MCP connector:

What Credits a page Pay as you go
A new diagram (POST /diagrams) 5 $0.50
A change to a diagram (POST /diagrams/{id}/revisions), for each page it changes 2 $0.20
An animation (animate: true, or POST /diagrams/{id}/animate) 8 $0.80
Edits without the AI (POST /diagrams/{id}/edits), downloads, reading and listing 0 free

Most diagrams are one page. A diagram with several pages is priced by its pages: a new three-page diagram is 15 credits, and a change that redraws two of its pages is 4. A request is charged once, when its result is kept; a retry with the same Idempotency-Key is not charged again. Your plan's credits are used first, then bought credits. Buy credits or turn on automatic top-up at https://app.diagramkit.ai/settings/api, where you also see your balance and this month's use. When the balance cannot pay for a request, it is refused with 402 no_credits before any work starts, and nothing is charged.

A set of pages (slides, a carousel, "one page per step") has as many pages as the prompt asks for. It is planned first, and its whole cost is checked before any page is drawn: a set the balance cannot pay for fails with error.type no_credits, a message saying how many pages were planned, the credits they need and the balance, and nothing drawn or charged. The API never asks before drawing, so a set of more than 30 pages needs your yes: send max_pages with the most pages you accept. Without it, a longer set fails with error.type too_many_pages and a message naming its page count and cost; send the request again with max_pages. A job's time limit grows with its pages.

Quick start

Make a diagram and wait for it in one request, then download it for print:

curl https://app.diagramkit.ai/api/v1/diagrams \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: book-7-figure-3.2" \
  -d '{"prompt": "The water cycle for a grade 7 science textbook: evaporation, condensation, precipitation and collection, with the sun driving it", "wait": 180}'
curl "https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/image.png?quality=print&width_in=3.25" \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" -o figure-3-2.png

Make a diagram

POST /api/v1/diagrams. Making one takes about a minute, sometimes two.

Parameter In Values Default What it does
prompt body text, 1 to 4,000 characters required What to draw. Paste notes, data or a passage from your text into it.
images body up to 4 objects: {"name", "mime", "data_base64"} [] Pictures to work from: a sketch, a photo of a whiteboard, an existing figure, a logo. mime is image/png, image/jpeg, image/webp or image/gif; data_base64 is the file's bytes in standard base64, without a data: prefix. Up to 6 MB and 25 megapixels each.
animate body true, false false, or true for the ad and explainer_video presets Make it animated in the same request.
preset body docs, blog, social, carousel, ad, data_story, explainer_video, or null none Where the diagram will be used. See Presets.
max_pages body 1 to 100 30 The most pages a set may draw. A set with more fails with too_many_pages before anything is drawn or charged. Also on revisions and animate.
wait body whole seconds, 0 to 300 0 Hold the request open until the diagram is done or this time passes. With 0 you get 202 and the job at once.
Idempotency-Key header any text, up to 200 characters none Name each figure (book-7-figure-3.2). A request with a key this account has used before returns that job, whatever its body, instead of making another. Keys do not expire.

Write the prompt the way you would brief an illustrator: what the figure must show, who it is for, the labels and numbers it needs, and the size it will be seen at ("for a phone screen", "a full textbook page"). The API never stops to ask questions: when something is ambiguous it makes a reasonable choice and says so in description.

Presets

A preset says where the diagram will be used, not what kind of diagram it is. It changes how the agent composes it (shape, words, emphasis, pages) and which download comes first; the prompt's own words override it. It costs nothing extra: credits are per page as always. The diagram keeps its preset, so every change to it is made for the same destination, until a change names another (preset on revisions, or null for none). Leave it out for a general diagram, exactly as before.

Preset Made for Animates Pages First download
docs Documentation: accurate and neutral, no headline or call to action no one image.png
blog A figure in an article: one point, landscape about 2:1 no one image.png?quality=high
social A LinkedIn or X post: one idea readable at phone width, 4:5 or square, no call to action no one image.png?quality=high; recordings size=portrait
carousel A LinkedIn or Instagram carousel: a cover with a hook and a swipe cue, one idea per numbered page (n / N), an end card; 4:5, 6 to 12 pages unless the prompt asks for more. The prompt's own sentences stay on the pages word for word, and a page gets a figure only where it shows structure no many image.pdf?pages=all
ad Paid social: a short line, the product largest, brand bottom left and call to action bottom right, a 1:1 master yes one animation.mp4?size=square
data_story One chart from real data whose title is the finding, with a small source line no one image.png?quality=high
explainer_video An animated explainer composed for 16:9 yes one animation.mp4?size=landscape

A job with a preset carries recommended_download, the download its preset offers first, and animation.* without size uses the preset's frame.

curl https://app.diagramkit.ai/api/v1/diagrams \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "How SSH sets up an encrypted session", "preset": "social", "wait": 180}'

Wait for it

Send wait to hold the request open, up to 300 seconds. If the diagram is not done by then you get 202 with the job; call GET /api/v1/diagrams/:id?wait=60 until status is succeeded or failed. Set your HTTP client's timeout above wait (wait plus 30 seconds). A job that runs longer than 15 minutes fails; a set of pages has more time, about 6 minutes for every 4 pages after the first 4. A failed request leaves the diagram as it was (a new one has no version); send it again with a new Idempotency-Key.

Parameter On Values Default What it does
wait GET /api/v1/diagrams/:id whole seconds, 0 to 300 0 Answer when the job finishes or after this long, whichever is first.
limit GET /api/v1/diagrams 1 to 100 20 How many recent diagrams to list.

A finished job:

{
  "id": "c73141db-d6c0-4e4b-94ee-0aa757886498",
  "status": "succeeded",
  "title": "How the water cycle works",
  "description": "The sun drives evaporation from the ocean; vapour condenses into clouds, falls as rain and collects in rivers. It assumes a simplified cycle without groundwater.",
  "version": 1,
  "animated": false,
  "progress": null,
  "image_url": "https://app.diagramkit.ai/api/v1/diagrams/c73141db-…/image.png",
  "downloads": {
    "png": "https://app.diagramkit.ai/api/v1/diagrams/c73141db-…/image.png",
    "jpg": "…/image.jpg", "svg": "…/image.svg", "pdf": "…/image.pdf"
  },
  "error": null,
  "created_at": "2026-10-15T14:49:58.000Z"
}
Field Type Meaning
id string (UUID) The diagram. Use it in every other call.
status queued, running, succeeded, failed The latest request on this diagram: the first one or the latest change.
title string or null The diagram's title, once it has one.
description string or null What the diagram shows and any assumptions it made. Read it: it says when something was ambiguous.
version number How many versions it has. 0 until the first is made; each change adds one.
animated true, false Whether the latest version is animated, so animation.mp4 and .gif work.
preset string or null The diagram's preset, or null.
progress string or null While queued or running, what is happening ("Drawing the diagram").
image_url string or null The PNG of the latest version, once the job has succeeded.
downloads object or null Links to every format of the latest version: png, jpg, svg, pdf, and mp4 and gif when animated. null until there is a version. Send your key with them.
recommended_download {"format", "url", "options"} With a preset, once there is a version: the download it offers first (options are the query parameters in url). Left out without a preset.
error {"type", "message"} or null Why a failed job failed. type is no_credits (the balance cannot pay for the planned pages), too_many_pages (a set longer than max_pages, or than 100 pages), or generation_failed. The first two are refused before anything is drawn or charged; message says how many pages were planned and what they cost.
created_at ISO 8601 time When the diagram was first requested.

Download it

GET any of these after /api/v1/diagrams/:id/, with your key. The response is the file, with its Content-Type. Files are made on request: a picture takes a few seconds, a recording 20 to 60; the same request again is answered from cache. Downloads are always of the latest version. SVG and PDF are on paid plans: on the free plan they answer 403 with error.type plan_required, which a retry will not change; PNG and JPEG work on every plan.

Path Content-Type Parameters What you get
image.png image/png device, quality, width_in A sharp picture for slides, docs and the web.
image.jpg image/jpeg device, quality, width_in A smaller picture for email and social posts.
image.svg image/svg+xml device Vector, every label editable text, for Figma, Illustrator and the web. Paid plans.
image.pdf application/pdf device, pages Vector, for print and layout tools; pages=all makes one PDF of every page of a multi-page diagram. Paid plans.
animation.mp4 video/mp4 quality, size, page A video, up to 90 seconds. Only for an animated diagram. A multi-page diagram plays every page in order, with a short fade between pages, unless page picks one; a long one holds each still page for less so it fits (at least 1.5 seconds), and one too long even so is refused with 422 too_long.
animation.gif image/gif quality, size, page A looping GIF, up to 60 seconds. Only for an animated diagram. Pages as for animation.mp4.

Parameters go in the query string, for example image.png?quality=print&width_in=3.25&device=phone:

Parameter For Values Default What it does
device image.* desktop, phone desktop Which layout: desktop is drawn 900 px wide, phone 390 px wide. Every diagram has both.
quality image.png, image.jpg web, high, print web web is 1× (900 px wide) and instant; high is 2× (1800 px); print is 300 dpi at width_in inches, with the dpi written into the file.
page image.* 1 to 100 1 Which page of a multi-page diagram.
pages image.pdf all none Every page as one PDF, a page each.
width_in image.png, image.jpg with quality=print a number from 1 to 20 (inches, decimals allowed) 6.5 The printed width. 6.5 fills a textbook page (1950 px); 3.25 fits one column (975 px). Ignored for other qualities.
quality animation.* low, medium, high medium MP4: low 675 px at up to 24 fps, medium 900 px at 30 fps, high 1800 px at up to 60 fps. GIF: low 675 px at 15 fps, medium 900 px at 20 fps, high 1350 px at 30 fps. Widths are for size=chat; framed sizes scale the same way (low is 0.75×, high up to 2×). Higher is larger and slower to record.
size animation.* chat, square, portrait, story, landscape the preset's frame, or chat The frame: chat is the diagram's own shape; square 1080×1080 (1:1); portrait 1080×1350 (4:5); story 1080×1920 (9:16); landscape 1920×1080 (16:9). The diagram is fitted as large as the frame allows.
page animation.* 1 to 100 none One page of a multi-page diagram. Left out, every page plays in order in one video: an animated page plays its animation, a still page holds for about 3 seconds, or less in a long set so it fits the format's length (never under 1.5 seconds). About 27 still pages fit a GIF and 41 an MP4; for more, download image.pdf?pages=all or one page at a time.

Animate a diagram

An animation is the same diagram building itself up: parts arrive in order, arrows draw from start to end, labels appear with what they describe, and it ends on the finished, readable picture. diagramkit plans the motion from what the diagram explains; most run 5 to 10 seconds. There are two ways to get one.

Animated from the start. Add "animate": true when you make it. One request, one to two minutes.

curl https://app.diagramkit.ai/api/v1/diagrams \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "How a heat pump moves heat from outside air into a house", "animate": true, "wait": 300}'

Animate one you already have. POST /api/v1/diagrams/:id/animate keeps the drawing and adds motion as the next version. Use "prompt" to steer it, for example "one step at a time", "slower", "highlight the cache miss path last" or "loop the data flow at the end".

# 1. Animate a diagram you already made (it becomes the next version)
curl -X POST https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/animate \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" -H "Content-Type: application/json" \
  -d '{"prompt": "Build it up one step at a time, then loop the refrigerant around the circuit", "wait": 300}'

# 2. If it is not done yet, wait for it: "animated": true means it is ready
curl "https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID?wait=120" -H "Authorization: Bearer $DIAGRAMKIT_KEY"

# 3. Download it, framed for where it goes
curl "https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/animation.mp4?quality=high&size=landscape" -H "Authorization: Bearer $DIAGRAMKIT_KEY" -o explainer.mp4
curl "https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/animation.gif?quality=medium&size=portrait" -H "Authorization: Bearer $DIAGRAMKIT_KEY" -o post.gif

A job is ready to record when it shows "animated": true; its downloads then include mp4 and gif. The image.* downloads keep giving the finished still.

Where it goes Download
Slides, a course video, YouTube animation.mp4?size=landscape&quality=high
YouTube Shorts, Reels, TikTok animation.mp4?size=story
LinkedIn and Instagram posts animation.gif?size=portrait or animation.mp4?size=portrait
X and other square feeds animation.gif?size=square
Docs, READMEs, Slack, email animation.gif?quality=low (small, and loops)

Change a diagram

POST /api/v1/diagrams/:id/revisions. The change keeps everything you did not mention and becomes the next version; downloads give it from then on. One change at a time: a second one while the first runs gets 409 conflict.

curl https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/revisions \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Label the arrows with the time each step takes", "wait": 180}'
Parameter In Values Default What it does
prompt revisions body text, 1 to 4,000 characters required What to change: "label the arrows with the time each step takes", "use metric units", "make it fit a narrow column".
images revisions body as for making one [] Pictures to work from for the change.
preset revisions body a preset, or null for none the diagram's own Make this change, and later ones, for another destination.
prompt animate body text, up to 1,000 characters none How it should move, if you have a preference.
wait both bodies whole seconds, 0 to 300 0 Hold the request open until the new version is done.
Idempotency-Key header any text, up to 200 characters none As for making one: a key used before returns that job.

Edit without the AI

POST /api/v1/diagrams/:id/edits makes exact changes directly, as the canvas editor makes them: retype a label, recolour a box, a line or its text, change a font or size, hide or show a part, move a part, put it beside another, swap two, line parts up or space them evenly. It does not use the AI, answers in a few seconds, costs no credits, saves the result as the next version, and does not count as a new AI diagram. For anything else (new content, a different layout) ask for a change instead.

Wording is matched by the text itself. Everything else names parts by their ids, from the parts list:

curl "https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/parts?page=1" -H "Authorization: Bearer $DIAGRAMKIT_KEY"

# {"diagram_id": "…", "version": 3, "page": 1, "layouts": {"wide": 900, "compact": 390}, "truncated": false, "inner": 48,
#  "parts": [
#   {"id": "node:api", "kind": "node", "summary": "node \"API\"; to \"Cache\", \"Database\"; 2 in; movable; 6 inner parts",
#    "text": "API", "movable": true, "inner": 6, "wide": [361, 148, 79, 105], "compact": [176, 163, 63, 84]},
#   {"id": "node:db", "kind": "node", "summary": "node \"Database\"; 2 in; movable; 4 inner parts", …},
#   {"id": "link:api->db", "kind": "link", "summary": "link \"API\" -> \"Database\"; 2 inner parts",
#    "link": {"from": "api", "to": "db"}, "wide": [431, 187, 104, 119], "compact": [53, 204, 131, 247]}, …]}

A part's id comes from what it is, so it stays the same through edits, and through a change from the AI that keeps the part:

Id What it names
node:<id> A part diagramkit placed, by the id the diagram gives it (node:api).
link:<from>-><to> A connection, with its line and arrowheads (link:api->db).
note:<words>, text:<words>, box:<words> A note, free-standing words (a title, a label, a caption) or a box, by its words (text:monthly-revenue).
cell:<id>, cell:<row>/<col> A placed cell by the id of the part in it; a table cell by row and column (cell:0/2 is the second header).
mark:<series>/<category> A chart's bar or point (mark:revenue/2024), a whole line by its series, a pie slice by its id.
<owner>/<kind> Something inside one of those: a node's words (node:api/text), an icon's shapes (node:api/shape#2), a link's line and head.

Two parts with the same name are told apart in the order the diagram draws them: text:total, text:total#2. A part's position (0/2/1, its path) is accepted too.

By default the list has what a person points at: nodes (with their words), links, notes, cells, chart marks, titles and labels. Each has its kind (node, cell or note for parts diagramkit placed; link; mark; text, line, shape or box), a one-line summary, its first 40 characters of text, inner (how many parts sit inside it), and its box [left, top, width, height] in pixels in the wide layout (desktop, 900 px wide, as image.png draws it) and, where it shows, the compact layout (phone, 390 px). movable marks the parts that move; hidden marks a hidden part; offset is a moved part's current offset per layout. detail=true lists what sits inside them too, each with its tag, its path and moves_with, the placed part it moves with. A large diagram lists its first 400 parts and says truncated.

curl https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID/edits \
  -H "Authorization: Bearer $DIAGRAMKIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": [{"before": "Cache", "after": "Redis cache"}],
       "place": [{"part": "node:db", "relative_to": "node:cache", "side": "below"}],
       "restyle": [{"part": "link:api->db", "style": {"stroke": "#d92d20"}}]}'

# The job, as GET returns it, with what was applied, what was skipped and where each moved part is now:
# {..., "version": 4,
#  "applied": [{"before": "Cache", "after": "Redis cache", "places": 1}],
#  "skipped": [],
#  "moved": [{"part": "node:db", "wide": [505, 260, 86, 105]}]}
# places is how many times the text appeared and was changed.
Parameter In Values Default What it does
text body up to 30 objects: {"before", "after"} [] Each replaces one label or line of text. before is the text exactly as it appears in the diagram (up to 600 characters); after is what it should say. Also accepted as edits.
restyle body up to 100 objects: {"part", "style"} [] style sets any of fill, stroke, color, background-color, font-family, font-size (4 to 400 px), font-weight (normal, bold, 100 to 900) and visibility. fill colours a shape, or a box's background; color its text. On a link, stroke (or color) colours its line and arrowheads; on a chart mark, fill its shape; on a node, color its words. Values are plain CSS: colours (#0b5fff, rgb(), hsl(), var(--…)), lengths and font names; anything else is refused. An empty value takes back an earlier change. A fill that leaves text on it hard to read re-colours that text from the diagram's own colours, as the canvas does.
hide body up to 100 part ids [] Hide parts.
show body up to 100 part ids [] Show parts hidden earlier with hide.
nudge body up to 100 objects: {"part", "dx", "dy", "layout"} [] Shift a movable part (or the one a part sits in) dx, dy pixels from where it is now; right and down are positive.
place body up to 100 objects: {"part", "relative_to", "side", "gap", "layout"} [] Put a movable part left, right, above or below another part, gap pixels away (default 40), centred on it across that side. The other part need not move.
swap body up to 50 objects: {"a", "b", "layout"} [] Exchange two movable parts' positions, centre for centre.
move body up to 100 objects: {"part", "layout", "dx", "dy"} [] Set a movable part's offset from where diagramkit placed it, -4000 to 4000 pixels. 0, 0 puts it back.
align body {"parts", "to", "layout"} none Line up two or more placed parts with each other: to is left, center or right (across) or top, middle or bottom (down).
distribute body {"parts", "axis", "layout"} none Space three or more placed parts evenly between the first and the last: axis is horizontal or vertical.
page body 1 to 100 1 Which page of a multi-page diagram; part ids are per page.
styles, moves body the canvas editor's own form [] What the web app's canvas sends: styles by element position, moves by part key. Prefer the fields above.

layout is wide (desktop, the default), compact (phone) or both (each layout the part has). Lines follow a part that moves. Send at least one edit. Moves apply in this order: move, nudge, place, swap, align, distribute, each measuring the parts where the earlier ones left them, and the result's moved gives each moved part's new box per layout, so there is no need to list the parts again. A layout a diagram does not have (one drawn the same on desktop and phone has only wide) is refused with a message saying which to use.

Edits apply to the latest version, and wording is matched exactly, case included. Text the diagram works out from data, such as a chart's axis values or a computed total, cannot be retyped: change the data with a revision. An unknown part id (the message lists ids of that kind), a part that cannot move, a part placed beside itself, or a change that would break the diagram answers 422 not_applied naming the part and the reason, and the diagram is left as it was. A diagram being made or changed answers 409 conflict.

List and delete

curl "https://app.diagramkit.ai/api/v1/diagrams?limit=5" -H "Authorization: Bearer $DIAGRAMKIT_KEY"
# {"diagrams": [{"id", "title", "status", "versions", "origin", "created_at", "updated_at"}, …]}

curl -X DELETE https://app.diagramkit.ai/api/v1/diagrams/DIAGRAM_ID -H "Authorization: Bearer $DIAGRAMKIT_KEY"
# {"deleted": true}

Deleting removes the diagram, every version and every file made from it. It cannot be undone.

Full examples

Make a diagram, wait for it, and save it, in JavaScript:

// Node 18 or later. Run on your server: the key must stay secret.
import { writeFile } from 'node:fs/promises'

const BASE = 'https://app.diagramkit.ai/api/v1'
const headers = { Authorization: 'Bearer ' + process.env.DIAGRAMKIT_KEY, 'Content-Type': 'application/json' }

async function call(path, init = {}) {
  const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers }, signal: AbortSignal.timeout(330_000) })
  if (!res.ok) throw new Error((await res.json()).error.message)
  return res
}

// 1. Make it, waiting up to three minutes.
let job = await (await call('/diagrams', {
  method: 'POST',
  headers: { 'Idempotency-Key': 'book-7-figure-3.2' },
  body: JSON.stringify({ prompt: 'The water cycle for a grade 7 science textbook', wait: 180 }),
})).json()

// 2. Not done yet? Keep waiting, 60 seconds at a time.
while (job.status === 'queued' || job.status === 'running') {
  job = await (await call('/diagrams/' + job.id + '?wait=60')).json()
}
if (job.status === 'failed') throw new Error(job.error.message)
console.log(job.title, '-', job.description)

// 3. Download it for print, one column wide.
const png = await call('/diagrams/' + job.id + '/image.png?quality=print&width_in=3.25')
await writeFile('figure-3-2.png', Buffer.from(await png.arrayBuffer()))

Work from a sketch and save an editable SVG, in Python:

# Python 3 with requests. Run on your server: the key must stay secret.
import base64, os, requests

BASE = "https://app.diagramkit.ai/api/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["DIAGRAMKIT_KEY"]}

# Work from a sketch: images are base64 without a data: prefix.
with open("sketch.png", "rb") as f:
    sketch = base64.b64encode(f.read()).decode()

job = requests.post(f"{BASE}/diagrams", headers={**HEADERS, "Idempotency-Key": "ch4-fig2"}, timeout=330, json={
    "prompt": "Redraw this sketch of the heart's four chambers, labeled, for a nursing course",
    "images": [{"name": "sketch.png", "mime": "image/png", "data_base64": sketch}],
    "wait": 180,
}).json()

while job["status"] in ("queued", "running"):
    job = requests.get(f"{BASE}/diagrams/{job['id']}", headers=HEADERS, params={"wait": 60}, timeout=90).json()
if job["status"] == "failed":
    raise RuntimeError(job["error"]["message"])

svg = requests.get(f"{BASE}/diagrams/{job['id']}/image.svg", headers=HEADERS, timeout=120)
svg.raise_for_status()
open("ch4-fig2.svg", "wb").write(svg.content)

Use the MCP from your own agent

An agent you build can use diagramkit as a tool through its MCP server instead of calling these endpoints. Connect it to https://mcp.diagramkit.ai/mcp and send the same API key as the bearer token: Authorization: Bearer aid_…. No sign-in window opens; the key decides whose diagrams and credits it uses, and revoking the key cuts the agent off within a minute. (People connecting their own Claude or ChatGPT use the connector's sign-in instead.)

The MCP is another way in to the same diagramkit agent as these endpoints, with the same features: create_diagram sends a request, with the same animate and preset, and returns the finished image and ids (one to three minutes), revise_diagram and animate_diagram ask for changes, edit_diagram makes the edits above without the AI (with the part ids get_diagram lists when you pass parts: true, and detail: true for what sits inside them), get_diagram, list_diagrams and delete_diagram manage diagrams, and download_diagram returns PNG, JPG, SVG, PDF, MP4 or GIF. Where the host displays MCP Apps, render_visual shows the diagram in the chat. Credits are the same as the API's.

With Claude, through the Messages API's MCP connector:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: mcp-client-2025-11-20" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 8000,
    "messages": [{"role": "user", "content": "Draw how a CDN serves a cached image, and give me the PNG."}],
    "mcp_servers": [{"type": "url", "url": "https://mcp.diagramkit.ai/mcp", "name": "diagramkit", "authorization_token": "'"$DIAGRAMKIT_KEY"'"}],
    "tools": [{"type": "mcp_toolset", "mcp_server_name": "diagramkit"}]
  }'

With OpenAI, through the Responses API's remote MCP tool:

import os
from openai import OpenAI

response = OpenAI().responses.create(
    model="gpt-5.2",
    tools=[{
        "type": "mcp",
        "server_label": "diagramkit",
        "server_url": "https://mcp.diagramkit.ai/mcp",
        "authorization": os.environ["DIAGRAMKIT_KEY"],
        "require_approval": "never",
    }],
    input="Draw how a CDN serves a cached image, and give me the PNG.",
)
print(response.output_text)

With any MCP client, such as the TypeScript SDK in your own agent loop:

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

const client = new Client({ name: 'my-agent', version: '1.0.0' })
await client.connect(new StreamableHTTPClientTransport(new URL('https://mcp.diagramkit.ai/mcp'), {
  requestInit: { headers: { Authorization: 'Bearer ' + process.env.DIAGRAMKIT_KEY } },
}))
const { tools } = await client.listTools() // hand these to your model

A key that is not valid, or was revoked, is refused with 401 and the message to make a new one; the key is never sent to the model, only to diagramkit.

Responses and errors

Every error has the same shape; issues appears on invalid_request.

{
  "error": {
    "type": "invalid_request",
    "message": "Send {\"prompt\": \"...\"} with optional images, animate (true or false), preset (docs, blog, social, carousel, ad, data_story, explainer_video) and wait in seconds (0-300).",
    "issues": ["wait: Too big: expected number to be <=300"]
  }
}
Status Meaning
200 Done: the body is the finished job, or the file you asked for.
202 Accepted and still being made: the body is the job, with its id. Wait for it.
400 invalid_request A parameter is missing or out of range. message says which; issues lists each problem.
401 unauthorized No key, a malformed one, or a revoked one.
402 no_credits The balance cannot pay for this request. message says how many credits it needs and when plan credits return. Add credits, or turn on automatic top-up, at https://app.diagramkit.ai/settings/api.
403 plan_required The account's plan does not include this download: SVG and PDF are on paid plans. Do not retry; upgrade, or download PNG or JPEG.
404 not_found No diagram with that id on this account, or it has no picture yet.
409 conflict A change is already being made to this diagram. Wait for it, then send the next.
409 not_animated You asked for an animation from a diagram that has none. Animate it first.
422 too_long A video of every page would be too long for the format even at 1.5 seconds a still page. message says how many pages fit (pages_fit) and offers an MP4 or the all-pages PDF.
422 not_applied An edit could not be made: no label matched before, an unknown part id, a part that cannot move, nothing changed, or the change would break the diagram (it is left as it was). message says why.
429 rate_limited 50 requests are already in progress on this account. Wait for some to finish.
503 unavailable The file could not be made right now. Retry shortly.

All endpoints

Method Path What it does
POST /api/v1/diagrams Make a diagram. Add "animate": true for an animated one.
GET /api/v1/diagrams/:id The job: its status and download links. Add ?wait= to wait for it.
POST /api/v1/diagrams/:id/revisions Ask for a change. It becomes the next version.
POST /api/v1/diagrams/:id/animate Animate a diagram you already made.
GET /api/v1/diagrams/:id/parts The editable parts of the latest version, with their ids, summaries and boxes (detail=true for every part).
POST /api/v1/diagrams/:id/edits Edit directly, without the AI: wording, colours, fonts, hide and show, nudge, place, swap, move, align, distribute. Instant, free.
GET /api/v1/diagrams/:id/image.png The picture. Also image.jpg, image.svg and image.pdf.
GET /api/v1/diagrams/:id/animation.mp4 The animation as a video. Also animation.gif.
GET /api/v1/diagrams Your recent diagrams, newest first.
DELETE /api/v1/diagrams/:id Delete a diagram and every version of it.