{
  "openapi": "3.1.0",
  "info": {
    "title": "SVGProof API",
    "version": "1.0.0",
    "summary": "Logos and illustrations to clean SVG in one synchronous call.",
    "description": "Send an image, get the vector back in the same response. No job polling, no SDK needed.\n\nAuthenticate with an API key: `Authorization: Bearer svp_live_...` (charged: one credit per delivered image) or `svp_test_...` (free, watermarked output). Calls without a key are limited per client address and, depending on the server, refused (`key_required`), watermarked, or free during the beta."
  },
  "x-rate-limits": {
    "trace_per_minute": 60,
    "trace_burst": 20,
    "max_parallel": 4,
    "view_per_minute": 1200,
    "view_burst": 300,
    "key_per_minute": 600,
    "key_burst": 100,
    "key_max_parallel": 8,
    "max_batch": 100
  },
  "servers": [
    {
      "url": "http://localhost:8787",
      "description": "Local (make server). The docs page substitutes the deployed URL."
    }
  ],
  "paths": {
    "/v1/vectorize": {
      "post": {
        "operationId": "vectorize",
        "summary": "Vectorize an image",
        "description": "Traces the image and returns the result as a file (SVG by default). Logos, icons, wordmarks and flat illustrations work best; photos are rejected with `photo_like`.\n\nSend `Accept: application/json` to get the SVG inside a JSON body together with stats, the palette and the background.",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "Output format.",
            "schema": {
              "type": "string",
              "enum": [
                "svg",
                "png"
              ],
              "default": "svg"
            }
          },
          {
            "name": "background",
            "in": "query",
            "description": "`auto`: a baked-in checkerboard (fake transparency) is removed, a solid background is kept. `keep`: everything as in the image. `remove`: transparent background; shapes of the same color on top of it (letter counters) stay. A hex color (`ffffff`, or `%23ffffff`): a solid background in that color, replacing the traced one or added under transparent art.",
            "schema": {
              "type": "string",
              "default": "auto",
              "examples": [
                "auto",
                "keep",
                "remove",
                "ffffff"
              ]
            }
          },
          {
            "name": "max_colors",
            "in": "query",
            "description": "Merge the palette down to at most this many flat colors. Omit for the automatic palette.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "palette",
            "in": "query",
            "description": "Brand colors, comma-separated hex. Every color of the result is replaced by the nearest one of these (gradients become flat). At most 256.",
            "schema": {
              "type": "string",
              "examples": [
                "141155,03517b,ffffff"
              ]
            }
          },
          {
            "name": "scale",
            "in": "query",
            "description": "PNG only: output size relative to the uploaded image. The longest side is capped at 4096 px.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 16,
              "default": 1
            }
          },
          {
            "name": "width",
            "in": "query",
            "description": "PNG only: output width in pixels (overrides `scale`; the height follows the aspect ratio).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4096
            }
          },
          {
            "name": "knockout",
            "in": "query",
            "description": "With a `background` edit, shapes inside the art in the background's color (letter counters) go too. `false` keeps them.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`test`: free output with a watermark, for integrating. Test keys and (on servers that say so) calls without a key are always `test`.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ],
              "default": "live"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Up to 128 visible ASCII characters. A retry with the same key (same API key, within 24 h) gets the result again without being charged again.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The image (PNG, JPEG, WEBP, GIF or BMP; at most 32 MB, 17 megapixels, 8192 px per side). Either a multipart form with the file in the `image` field, or the raw bytes as the body.",
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "image"
                ],
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            },
            "image/*": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The vector (or PNG) file.",
            "headers": {
              "Vz-Paths": {
                "description": "Number of paths.",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Nodes": {
                "description": "Path segments (lines + curves): how simple the file is.",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Colors": {
                "description": "Colors in the result.",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Ms": {
                "description": "Engine time in milliseconds.",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Bytes": {
                "description": "SVG size in bytes.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Size of your request bucket (the burst).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the bucket.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the bucket is full again.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "Refill rate, `<requests>;w=60` (requests per 60 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "Vz-Mode": {
                "description": "`live` or `test` (watermarked, free).",
                "schema": {
                  "type": "string"
                }
              },
              "Vz-Credits-Charged": {
                "description": "With an API key: credits this request cost (0 in test mode or for a replayed `Idempotency-Key`).",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Credits-Left": {
                "description": "With an API key: the balance after this request (absent for unlimited keys).",
                "schema": {
                  "type": "integer"
                }
              },
              "Vz-Request-Id": {
                "description": "On every response: quote it when you contact support.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Result"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/Decode"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "422": {
            "$ref": "#/components/responses/PhotoLike"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Busy"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/NoCredits"
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "openapi",
        "summary": "This document",
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 description of the API.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/v1/batch": {
      "post": {
        "operationId": "batch",
        "summary": "Vectorize many images",
        "description": "Up to `max_batch` images (and at most your rate-limit burst) in one multipart request, every file in a field named `image`. The query parameters of `/v1/vectorize` apply to all of them. Images are traced in parallel; each one succeeds or fails on its own, and only delivered images are charged. A batch costs one rate-limit request per image.\n\nThe answer is a zip with one file per delivered image and `manifest.json` (per image: `ok`, `file` or `error`, `stats`). With `Accept: application/json` it is one JSON document with the same items and each SVG inline (SVG only).",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "Output format.",
            "schema": {
              "type": "string",
              "enum": [
                "svg",
                "png"
              ],
              "default": "svg"
            }
          },
          {
            "name": "background",
            "in": "query",
            "description": "`auto`: a baked-in checkerboard (fake transparency) is removed, a solid background is kept. `keep`: everything as in the image. `remove`: transparent background; shapes of the same color on top of it (letter counters) stay. A hex color (`ffffff`, or `%23ffffff`): a solid background in that color, replacing the traced one or added under transparent art.",
            "schema": {
              "type": "string",
              "default": "auto",
              "examples": [
                "auto",
                "keep",
                "remove",
                "ffffff"
              ]
            }
          },
          {
            "name": "max_colors",
            "in": "query",
            "description": "Merge the palette down to at most this many flat colors. Omit for the automatic palette.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "palette",
            "in": "query",
            "description": "Brand colors, comma-separated hex. Every color of the result is replaced by the nearest one of these (gradients become flat). At most 256.",
            "schema": {
              "type": "string",
              "examples": [
                "141155,03517b,ffffff"
              ]
            }
          },
          {
            "name": "scale",
            "in": "query",
            "description": "PNG only: output size relative to the uploaded image. The longest side is capped at 4096 px.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 16,
              "default": 1
            }
          },
          {
            "name": "width",
            "in": "query",
            "description": "PNG only: output width in pixels (overrides `scale`; the height follows the aspect ratio).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4096
            }
          },
          {
            "name": "knockout",
            "in": "query",
            "description": "With a `background` edit, shapes inside the art in the background's color (letter counters) go too. `false` keeps them.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`test`: free output with a watermark, for integrating. Test keys and (on servers that say so) calls without a key are always `test`.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ],
              "default": "live"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Up to 128 visible ASCII characters. A retry with the same key (same API key, within 24 h) gets the result again without being charged again.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "multipart/form-data with one `image` field per file (64 MB in total).",
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "image"
                ],
                "properties": {
                  "image": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The zip (or JSON). Headers as for `/v1/vectorize` where they apply: `Vz-Mode`, `Vz-Credits-Charged`, `Vz-Credits-Left`, `RateLimit-*`.",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/NoCredits"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Busy"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "account",
        "summary": "Your key's balance, usage and limits",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The calling key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Result": {
        "type": "object",
        "description": "Returned with `Accept: application/json`.",
        "required": [
          "svg",
          "width",
          "height",
          "stats",
          "palette",
          "background"
        ],
        "properties": {
          "svg": {
            "type": "string",
            "description": "The SVG document."
          },
          "width": {
            "type": "number",
            "description": "viewBox width (the coordinate space of the paths)."
          },
          "height": {
            "type": "number"
          },
          "src_width": {
            "type": "number",
            "description": "Width of the uploaded image (the SVG's width attribute)."
          },
          "src_height": {
            "type": "number"
          },
          "stats": {
            "type": "object",
            "properties": {
              "paths": {
                "type": "integer"
              },
              "nodes": {
                "type": "integer"
              },
              "colors": {
                "type": "integer"
              },
              "bytes": {
                "type": "integer"
              },
              "ms": {
                "type": "number"
              }
            }
          },
          "palette": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaletteEntry"
            }
          },
          "background": {
            "$ref": "#/components/schemas/Background"
          }
        }
      },
      "PaletteEntry": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "hex": {
            "type": "string",
            "description": "The color (a gradient's middle stop)."
          },
          "gradient": {
            "type": [
              "array",
              "null"
            ],
            "description": "Stops `[offset, hex]` when this color is a gradient.",
            "items": {
              "type": "array",
              "prefixItems": [
                {
                  "type": "number"
                },
                {
                  "type": "string"
                }
              ]
            }
          },
          "paths": {
            "type": "integer",
            "description": "Shapes of this color (not counting the background)."
          },
          "area": {
            "type": "number",
            "description": "Share of the picture, 0 to 1 (background not included)."
          },
          "removed": {
            "type": "boolean"
          },
          "background_only": {
            "type": "boolean",
            "description": "The background is this color's only shape."
          }
        }
      },
      "Background": {
        "type": "object",
        "properties": {
          "traced": {
            "type": "string",
            "enum": [
              "solid",
              "gradient",
              "none"
            ],
            "description": "What the trace found under the art."
          },
          "checker_removed": {
            "type": "boolean",
            "description": "A baked-in checkerboard was turned into transparency."
          },
          "palette_index": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Palette entry of the traced background."
          },
          "edit": {
            "type": "string",
            "description": "keep, remove, or the hex color it was set to."
          },
          "hex": {
            "type": [
              "string",
              "null"
            ],
            "description": "The background now (null = transparent)."
          },
          "gradient": {
            "type": [
              "array",
              "null"
            ]
          },
          "area": {
            "type": "number",
            "description": "Share of the picture covered only by the background."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code.",
                "enum": [
                  "bad_request",
                  "unsupported_format",
                  "decode",
                  "too_large",
                  "photo_like",
                  "rate_limited",
                  "busy",
                  "timeout",
                  "not_found",
                  "internal",
                  "unavailable",
                  "invalid_key",
                  "key_required",
                  "no_credits",
                  "too_many_images"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable detail; may change."
              },
              "retry_after": {
                "type": "integer",
                "description": "Only on `rate_limited` and `busy`: seconds to wait before retrying (also the `Retry-After` header)."
              }
            }
          }
        }
      },
      "BatchResult": {
        "type": "object",
        "properties": {
          "images": {
            "type": "integer"
          },
          "succeeded": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "credits_charged": {
            "type": "integer"
          },
          "credits_left": {
            "type": [
              "integer",
              "null"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "ok": {
                  "type": "boolean"
                },
                "svg": {
                  "type": "string"
                },
                "file": {
                  "type": "string",
                  "description": "zip only: the entry name."
                },
                "stats": {
                  "type": "object"
                },
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "live",
                  "test"
                ]
              },
              "created_at": {
                "type": "integer"
              }
            }
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null = unlimited."
          },
          "usage": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "month": {
                  "type": "string",
                  "examples": [
                    "2026-10"
                  ]
                },
                "images": {
                  "type": "integer"
                },
                "credits": {
                  "type": "integer"
                }
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "per_minute": {
                "type": "integer"
              },
              "burst": {
                "type": "integer"
              },
              "max_parallel": {
                "type": "integer"
              },
              "max_batch": {
                "type": "integer"
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "`bad_request` (a parameter is invalid; the message names it) or `unsupported_format`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooLarge": {
        "description": "`too_large`: over 17 megapixels or 8192 px on a side (checked from the header, before decoding).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Decode": {
        "description": "`decode`: not an image we can read.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "`rate_limited`: too many requests, or too many at once. Wait `Retry-After` seconds (the body repeats it as `error.retry_after`).",
        "headers": {
          "Retry-After": {
            "description": "Seconds until you may retry.",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Busy": {
        "description": "`busy`: every core is taken and the waiting line is full. Retry after `Retry-After` seconds.",
        "headers": {
          "Retry-After": {
            "description": "Seconds until you may retry.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Timeout": {
        "description": "`timeout`: the request (upload included) took longer than 60 seconds.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PhotoLike": {
        "description": "`photo_like`: a photo or textured image. We vectorize logos and illustrations.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "`invalid_key` (unknown or revoked key) or `key_required` (this server needs a key for this endpoint).",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NoCredits": {
        "description": "`no_credits`: the key's balance does not cover the request. Nothing was charged. Add credits, or send `mode=test`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key: `svp_live_...` (charged) or `svp_test_...` (free, watermarked)."
      }
    }
  },
  "security": [
    {},
    {
      "bearer": []
    }
  ]
}
