API and MCP
Send a PNG or JPEG image from a script, a plugin or an AI agent and get an SVG back. The API uses Homotope’s server processing: same engine, same queue, same credits and same image limit as the workshop.
Full description (OpenAPI 3.1): /api/v1/openapi.json.
Getting started in 3 steps
-
Create a key in the workshop, “My account” → “API
keys”. It starts with
hmt_live_and is shown only once: keep it in a secrets manager, never in a repository or a URL. -
Send an image:
POST /api/v1/jobsanswers straight away with202and the job’s identifier, without waiting for it to finish. -
Poll, then download: query
GET /api/v1/jobs/{id}every 2 to 5 seconds until the state isdone, then fetchGET /api/v1/jobs/{id}/svg, orGET /api/v1/jobs/{id}/result?format=pdf(svg,pdf,dxforeps).
Examples with curl
The key goes in the Authorization: Bearer … header, and only there.
export HOMOTOPE_KEY="hmt_live_…"
# Check the key
curl -sS https://homotope.example/api/v1/me \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# Send an image (optional options: preset, name, settings)
curl -sS https://homotope.example/api/v1/jobs \
-H "Authorization: Bearer $HOMOTOPE_KEY" \
-F [email protected] \
-F 'options={"preset":"logo","name":"Logo","options":{"export":{"precision":2}}}'
# → 202 {"id":"…","state":"queued","links":{…}}
# Fixed palette: trace with YOUR colours (2 to 64, distinct), no other colour is invented
curl -sS https://homotope.example/api/v1/jobs \
-H "Authorization: Bearer $HOMOTOPE_KEY" \
-F [email protected] \
-F 'options={"preset":"logo","palette":["#E30613","#0057B8","#FFFFFF"]}'
# Poll until "state": "done"
curl -sS https://homotope.example/api/v1/jobs/ID \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# Download the SVG
curl -sS -o logo.svg https://homotope.example/api/v1/jobs/ID/svg \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# The same result as PDF, EPS or DXF (cutting; tolerance: flattened curves, in px)
curl -sS -OJ "https://homotope.example/api/v1/jobs/ID/result?format=dxf&tolerance=0.05" \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# Separations: one layer per colour (layered SVG, DXF layers; ZIP for PDF and EPS)
curl -sS -OJ "https://homotope.example/api/v1/jobs/ID/result?format=svg&layers=1" \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# … or one file per colour, as a ZIP
curl -sS -OJ "https://homotope.example/api/v1/jobs/ID/result?format=pdf&layers=zip" \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# Ready to cut? Check the DXF (0.3 mm tool, shared borders merged)
curl -sS "https://homotope.example/api/v1/jobs/ID/cutcheck?tool_mm=0.3&merge_shared=true" \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# → {"id":"…","report":{"blocking":0,"check":2,"totals":{…},"issues":[…],…}}
# Latest jobs (20 by default, 100 at most; "nextCursor" for the next page)
curl -sS "https://homotope.example/api/v1/jobs?limit=50" \
-H "Authorization: Bearer $HOMOTOPE_KEY"
# Cancel a queued job, or delete a result
curl -sS -X DELETE https://homotope.example/api/v1/jobs/ID \
-H "Authorization: Bearer $HOMOTOPE_KEY"
Presets: logo, illustration, pixel-art,
line-art, centerline (single stroke: single-pass
stroke paths, for lasers, plotters or embroidery), photo. The
accepted settings and their bounds are those of the workshop’s “Advanced” panel, described
in the OpenAPI document (JobOptions).
Result formats
- Every format is produced on the fly from the job’s SVG: same geometry, same colours, same order. 1 px = 1 pt (1/72 of an inch).
- PDF 1.4 and EPS 3.0: vector, page the size of the image. EPS has no transparency.
-
DXF R12 (laser cutting, plotters, Cricut): closed contours, holes
included, one layer per colour named in hexadecimal (
FF8800), in millimetres. DXF has no fill: the contour is what counts for cutting; prefer the “Cut out” mode (no overlap). The background rectangle is left out (it would make a frame):include_background=truekeeps it.merge_shared=truemerges shared borders: an edge shared by two neighbouring shapes is written, and so cut, only once. -
Separations (
layers):layers=1gives one layer per colour (SVG with Inkscape and Illustrator layers, DXF with layers, a ZIP of one file per colour for PDF and EPS);layers=zipalways gives a ZIP of one file per colour, named<name>-<hex>.<ext>. Each file is the VISIBLE area of its colour (disjoint files): the job must use the “Cut out” mode, otherwise422(stacked_separations). The layered SVG follows the paint order (one layer per run of same-colour shapes). -
Fixed palette (
paletteinoptions): 2 to 64 distinct hexadecimal colours; each pixel takes the nearest one (OKLab). Dithering and coverage threshold:options.quantize.fixedPalette(OpenAPI document). No colour table is supplied: send your own. -
Conversion limited to 4 MB of SVG; beyond that, download the SVG. 60 conversions (PDF,
DXF, EPS) per hour per key, 120 per account;
503(busy) if a conversion for the account is already running.
Ready to cut?
GET /api/v1/jobs/{id}/cutcheck analyses the DXF that
…/result?format=dxf would produce (same tolerance,
include_background, merge_shared), read-only, and returns a JSON
report:
-
Blocking (
blocking): open contour, two contours crossing (crossing), a contour crossing itself (self-intersection), overlapping contours. -
To check (
check): detail finer than the tool (thin,tool_mm, 0.2 mm by default), small island (island, underisland_mm2, 1 mm² by default), contour with more than 1,000 nodes (nodes). -
Each issue has a position (
box, in image px, Y pointing down), a measurement (value, in mm, mm², nodes or points) and a human-readable message in the language of theAccept-Languageheader (English by default). Centreline strokes (centerlinepreset) are open by nature: never reported. -
Information (
info, outsideblockingandcheck): a border shared by two neighbouring shapes cut twice (overlap), fixed bymerge_shared=true. -
Same limits and same slots as conversions (it uses the same thread). A very heavy analysis
stops cleanly:
200withtruncated: true(minimal totals), or422(too_complex).
Entitlements
- No free trial through the API or MCP: the month’s trial jobs are reserved for the workshop.
- Each job uses the month’s Pro quota first, then a credit (see pricing). A job that fails, or is cancelled before it starts, is given back.
-
With no Pro quota or credit left, the upload is refused with
402(payment_required) before the image is read. A free account can create a key: its uploads get402until it has Pro or credits. - Same formats (PNG, JPEG, 80 MB at most) and same pixel limit as the workshop.
Limits
- 60 requests per minute per key (all routes), and 120 requests per minute per account, across all keys.
-
30 jobs per hour per key: beyond that,
429with theRetry-Afterheader (seconds). A refused upload does not count. -
1 job running and 3 queued per key: beyond that,
429(too_many_jobs) withRetry-After: 10; wait for a job to finish. -
30
401refusals per IP address over 15 minutes (missing or invalid key): beyond that,429. Check your key before retrying. - 10 active keys at most per account; a revoked key stays revoked for good.
Errors
Every error has the shape {"error": {"code": "…", "message": "…"}}, with a
human-readable message in the language of the Accept-Language header (English
by default); rely on the code.
| Status | Codes | What to do |
|---|---|---|
| 401 | invalid_api_key |
Key missing, unknown or revoked. |
| 402 | payment_required |
No Pro quota or credit left: buy a pack or upgrade to Pro. |
| 404 | not_found |
Unknown job (or one from another account). |
| 408 | upload_timeout |
Upload too long or too slow: the limit is max(60 s, announced size ÷ 256 KB/s), and the rate must stay at 32 KB/s at least. Check the connection, then try again. |
| 409 | not_ready |
SVG requested too early: poll until done. |
| 410 | gone |
Result deleted (retention or deletion). |
| 413 |
payload_too_large, too_large,
quota_exceeded
|
File too heavy, image too large, or storage full. |
| 415 | unsupported_media_type |
Neither PNG nor JPEG (file signature, never its name or declared type). |
| 422 |
stacked_separations, too_complex,
unconvertible
|
One file per colour requested on a stacked result (choose the “cut out” mode); conversion or cut check too long or too heavy (simplify the trace); result that cannot be converted. Retrying as is will not help. |
| 429 |
rate_limited, too_many_jobs,
too_many_uploads
|
Wait for the Retry-After delay, without looping. |
| 503 | busy, storage_unavailable, unavailable |
Conversion slots taken (busy, with Retry-After), or
service temporarily unavailable: try again later.
|
MCP server (AI agents)
Homotope exposes an MCP (Model Context Protocol) server, a thin front for this API, over the “Streamable HTTP” transport:
-
URL:
https://homotope.example/mcp - Header:
Authorization: Bearer hmt_live_…(your API key) - Same entitlements, same limits and same credits as the API.
Example configuration for an MCP client (the key stays with you):
{
"mcpServers": {
"homotope": {
"type": "http",
"url": "https://homotope.example/mcp",
"headers": { "Authorization": "Bearer hmt_live_…" }
}
}
}
Tools offered to the agent:
-
vectorize_image: an image asimage_base64(about 6 MB at most) or a public httpsimage_url, plus optionalfilename,preset,palette(fixed colours, hexadecimal) andoptions; returns{ job_id, state }straight away, without waiting for the job to finish. get_job: the state of a job (job_id).-
get_svg: the SVG of a finished job (image/svg+xml), or a clear error if it is not ready. -
get_result: the result assvg,pdf,dxforeps(format, andtolerancefor DXF);layers: separations (true, or"zip"for one file per colour); PDF and ZIP arrive base64-encoded. -
check_cut: “ready to cut?” — the DXF check report (tool_mm,island_mm2,merge_shared…), likeGET /api/v1/jobs/{id}/cutcheck. list_vectorizations: the latest jobs (limit).
A tool error carries the API’s status and code (402, 413, 429…) and a human-readable
message. An image_url must be public: https, port 443, no private or local
address, 3 redirects at most, read within 15 seconds.
No OAuth: the API key is enough. No stdio transport: the server is remote. The
server keeps no session between two requests.