Shapesoda

Vetorize imagens: PNG/JPG/WebP para SVG, PDF, EPS ou DXF limpos, com pré-visualização de antes/depois e controles de cor. Funciona sem conta.

Servidor MCP hospedado

npx add-mcp 'https://shapesoda.com/mcp'

Instala no Claude Code, Codex, Cursor e outros

Documentação

Início rápido

Três passos do zero até seu primeiro SVG. A URL base é https://shapesoda.com; todas as requisições usam HTTPS.

  1. Obtenha uma chave de API Abra Chaves de API na sua conta (faça login com seu e-mail ou Google, se solicitado) e escolha Nova chave. A chave começa com ssk_ e é exibida apenas uma vez — copie-a para seu cofre de segredos. Contas novas recebem um crédito gratuito para testar.
    export SHAPESODA_KEY="ssk_your_key_here"
    
  2. Envie uma imagem Envie os bytes brutos da imagem como corpo da requisição e passe as opções na query string:
    curl -sS "https://shapesoda.com/v1/vectorize?colors=auto" \
      -H "Authorization: Bearer $SHAPESODA_KEY" \
      --data-binary @logo.png \
      -D - -o logo.svg
    
    Ou envie um formulário multipart/form-data com o arquivo no campo image e as opções como campos normais:
    curl -sS https://shapesoda.com/v1/vectorize \
      -H "Authorization: Bearer $SHAPESODA_KEY" \
      -F [email protected] \
      -F colors=8 \
      -F image_type=artwork \
      -D - -o logo.svg
    
  3. Leia a resposta Em caso de sucesso, você recebe 200 com o SVG como corpo (image/svg+xml). -D - exibe os cabeçalhos, que informam o custo da requisição:
    HTTP/1.1 200 OK
    Content-Type: image/svg+xml; charset=utf-8
    Content-Disposition: inline; filename="vector.svg"
    X-Credits-Charged: 1
    X-Mode: live
    X-Credits-Remaining: 99
    X-Request-Id: req_4f1c2a9e0b7d4c3a8e21
    
    CabeçalhoSignificado
    X-Credits-ChargedCréditos usados por esta requisição: 1 para uma vetorização bem-sucedida (qualquer que seja o número de formatos), 0 no modo de teste e para repetições gratuitas. Requisições com falha não são cobradas.
    X-Modelive, ou test no modo de teste.
    X-Credits-RemainingImagens que você ainda pode processar agora (saldo mais quaisquer créditos incluídos, limitado pelo seu limite de gastos).
    X-Request-IdID único da requisição. Ele também é retornado em erros — inclua-o ao contatar o suporte.

Autenticação

Requisições de conta e vetorização (POST /v1/vectorize, GET /v1/account, /mcp) devem incluir sua chave de API como um token bearer:

Authorization: Bearer ssk_…
  • Crie, liste e revogue chaves na página Chaves de API da sua conta (até 10 chaves ativas por conta). A revogação tem efeito imediato.
  • Uma chave ausente, malformada ou revogada retorna 401 unauthorized. Tentativas falhas repetidas de um mesmo endereço IP são limitadas por taxa.
  • Mantenha a chave em segredo e apenas no seu servidor. A API não envia cabeçalhos CORS, portanto não foi feita para ser chamada a partir de uma página web: nunca chame a API de um navegador ou aplicativo móvel com sua chave secreta, e nunca a inclua em código front-end nem a envie para um repositório. Se uma chave vazar, revogue-a.
  • Resultados armazenados usam uma credencial diferente. GET e DELETE /v1/results/{id} usam o token de resultado, não a chave de API. Envie-o como Authorization: Bearer <result_token> (recomendado). ?token= na URL também é aceito, mas um token em uma URL pode vazar para logs de acesso, histórico do navegador e ferramentas de análise — não o use onde URLs são registradas ou compartilhadas. Consulte Armazenamento de resultados.
  • Links de upload de uso único do fluxo de upload do MCP carregam seu próprio segredo e não precisam de autenticação adicional. GET /v1/plans é público.
  • Todas as chaves de uma conta compartilham um único saldo de créditos — o mesmo saldo que você usa no site.

Exemplos de código

Exemplos mínimos e com poucas dependências: vetorize logo.png em logo.svg, obtenha vários formatos com um preset, exiba erros da API e imprima os créditos restantes. Sua escolha de linguagem é lembrada em toda a página.

Vetorizar uma imagem

# --fail-with-body: exit non-zero on HTTP errors (the JSON error is written to logo.svg)
curl -sS --fail-with-body "https://shapesoda.com/v1/vectorize?colors=auto&image_type=artwork" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png \
  -D headers.txt -o logo.svg

grep -i x-credits-remaining headers.txt
// vectorize.mjs — Node.js 18+ (built-in fetch), no dependencies
import { readFile, writeFile } from "node:fs/promises";

const res = await fetch("https://shapesoda.com/v1/vectorize?colors=auto", {
  method: "POST",
  headers: { Authorization: \`Bearer ${process.env.SHAPESODA_KEY}\` },
  body: await readFile("logo.png"),
});

if (!res.ok) {
  const { error } = await res.json();
  throw new Error(\`${res.status} ${error.code}: ${error.message} (${error.request_id})\`);
}

await writeFile("logo.svg", await res.text());
console.log("Credits left:", res.headers.get("X-Credits-Remaining"));
# vectorize.py — pip install requests
import os
import requests

with open("logo.png", "rb") as f:
    r = requests.post(
        "https://shapesoda.com/v1/vectorize",
        headers={"Authorization": f"Bearer {os.environ['SHAPESODA_KEY']}"},
        files={"image": f},
        data={"colors": "auto"},
        timeout=120,
    )

if r.status_code != 200:
    e = r.json()["error"]
    raise SystemExit(f"{r.status_code} {e['code']}: {e['message']} ({e['request_id']})")

with open("logo.svg", "w", encoding="utf-8") as out:
    out.write(r.text)
print("Credits left:", r.headers["X-Credits-Remaining"])
<?php
// vectorize.php — PHP 8 with the curl extension
$remaining = null;
$ch = curl_init("https://shapesoda.com/v1/vectorize");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SHAPESODA_KEY")],
    CURLOPT_POSTFIELDS => ["image" => new CURLFile("logo.png"), "colors" => "auto"],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 120,
    CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$remaining) {
        if (stripos($line, "X-Credits-Remaining:") === 0) $remaining = trim(substr($line, 20));
        return strlen($line);
    },
]);
$body = curl_exec($ch);
if ($body === false) {
    fwrite(STDERR, "Network error: " . curl_error($ch) . PHP_EOL);
    exit(1);
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($status !== 200) {
    $e = json_decode($body, true)["error"];
    fwrite(STDERR, "$status {$e['code']}: {$e['message']} ({$e['request_id']})" . PHP_EOL);
    exit(1);
}

file_put_contents("logo.svg", $body);
echo "Credits left: $remaining", PHP_EOL;
// main.go — standard library only: go run main.go
package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "time"
)

func main() {
    img, err := os.ReadFile("logo.png")
    if err != nil {
        log.Fatal(err)
    }
    req, err := http.NewRequest("POST", "https://shapesoda.com/v1/vectorize?colors=auto", bytes.NewReader(img))
    if err != nil {
        log.Fatal(err)
    }
    req.Header.Set("Authorization", "Bearer "+os.Getenv("SHAPESODA_KEY"))

    client := &http.Client{Timeout: 120 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    if resp.StatusCode != http.StatusOK {
        log.Fatalf("%d: %s", resp.StatusCode, body) // {"error":{"code","message","request_id"}}
    }
    if err := os.WriteFile("logo.svg", body, 0o644); err != nil {
        log.Fatal(err)
    }
    fmt.Println("Credits left:", resp.Header.Get("X-Credits-Remaining"))
}

Vetorizar com um preset

O preset print: um PDF e um SVG de 210 mm de largura em uma única chamada, por 1 crédito. Vários formatos retornam como JSON, e cada arquivo é baixado pelo seu link com o token de resultado.

# preset=print, two formats in one call (1 credit) → JSON with a link per format (needs jq)
curl -sS --fail-with-body "https://shapesoda.com/v1/vectorize?preset=print&format=pdf,svg&width=210&unit=mm" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png -o result.json

# download each file with the result token (not the API key)
TOKEN=$(jq -r .result_token result.json)
for f in pdf svg; do
  curl -sS --fail-with-body "$(jq -r .files.$f result.json)" \
    -H "Authorization: Bearer $TOKEN" -o "logo.$f"
done
// preset.mjs — Node.js 18+ (built-in fetch), no dependencies
import { readFile, writeFile } from "node:fs/promises";

const url = "https://shapesoda.com/v1/vectorize?preset=print&format=pdf,svg&width=210&unit=mm";
const res = await fetch(url, {
  method: "POST",
  headers: { Authorization: \`Bearer ${process.env.SHAPESODA_KEY}\` },
  body: await readFile("logo.png"),
});
if (!res.ok) {
  const { error } = await res.json();
  throw new Error(\`${res.status} ${error.code}: ${error.message} (${error.request_id})\`);
}

// several formats → JSON with a link per format, stored for 24 hours
const result = await res.json();
for (const [format, link] of Object.entries(result.files)) {
  const file = await fetch(link, { headers: { Authorization: \`Bearer ${result.result_token}\` } });
  if (!file.ok) throw new Error(\`download ${format}: ${file.status}\`);
  await writeFile(\`logo.${format}\`, Buffer.from(await file.arrayBuffer()));
}
console.log("Credits left:", result.credits_remaining);
# preset.py — pip install requests
import os
import requests

with open("logo.png", "rb") as f:
    r = requests.post(
        "https://shapesoda.com/v1/vectorize",
        headers={"Authorization": f"Bearer {os.environ['SHAPESODA_KEY']}"},
        files={"image": f},
        data={"preset": "print", "format": "pdf,svg", "width": "210", "unit": "mm"},
        timeout=120,
    )

if r.status_code != 200:
    e = r.json()["error"]
    raise SystemExit(f"{r.status_code} {e['code']}: {e['message']} ({e['request_id']})")

# several formats → JSON with a link per format, stored for 24 hours
result = r.json()
for fmt, link in result["files"].items():
    file = requests.get(link, headers={"Authorization": f"Bearer {result['result_token']}"}, timeout=120)
    file.raise_for_status()
    with open(f"logo.{fmt}", "wb") as out:
        out.write(file.content)
print("Credits left:", result["credits_remaining"])
<?php
// preset.php — PHP 8 with the curl extension
function http(string $url, array $opts): array {
    $ch = curl_init($url);
    curl_setopt_array($ch, $opts + [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 120]);
    $body = curl_exec($ch);
    if ($body === false) throw new RuntimeException(curl_error($ch));
    return [curl_getinfo($ch, CURLINFO_RESPONSE_CODE), $body];
}

[$status, $body] = http("https://shapesoda.com/v1/vectorize", [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SHAPESODA_KEY")],
    CURLOPT_POSTFIELDS => ["image" => new CURLFile("logo.png"), "preset" => "print", "format" => "pdf,svg", "width" => "210", "unit" => "mm"],
]);
if ($status !== 200) {
    $e = json_decode($body, true)["error"];
    fwrite(STDERR, "$status {$e['code']}: {$e['message']} ({$e['request_id']})" . PHP_EOL);
    exit(1);
}

// several formats → JSON with a link per format, stored for 24 hours
$result = json_decode($body, true);
foreach ($result["files"] as $format => $link) {
    [$status, $file] = http($link, [CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $result["result_token"]]]);
    if ($status !== 200) exit("download $format: $status" . PHP_EOL);
    file_put_contents("logo.$format", $file);
}
echo "Credits left: {$result['credits_remaining']}", PHP_EOL;
// main.go — standard library only: go run main.go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "time"
)

var client = &http.Client{Timeout: 120 * time.Second}

// do sends the request with a bearer token and returns the body of a 200 response.
func do(req *http.Request, token string) []byte {
    req.Header.Set("Authorization", "Bearer "+token)
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    if resp.StatusCode != http.StatusOK {
        log.Fatalf("%d: %s", resp.StatusCode, body) // {"error":{"code","message","request_id"}}
    }
    return body
}

func main() {
    img, err := os.ReadFile("logo.png")
    if err != nil {
        log.Fatal(err)
    }
    url := "https://shapesoda.com/v1/vectorize?preset=print&format=pdf,svg&width=210&unit=mm"
    req, err := http.NewRequest("POST", url, bytes.NewReader(img))
    if err != nil {
        log.Fatal(err)
    }

    // several formats → JSON with a link per format, stored for 24 hours
    var result struct {
        Token            string            \`json:"result_token"\`
        Files            map[string]string \`json:"files"\`
        CreditsRemaining int               \`json:"credits_remaining"\`
    }
    if err := json.Unmarshal(do(req, os.Getenv("SHAPESODA_KEY")), &result); err != nil {
        log.Fatal(err)
    }
    for format, link := range result.Files {
        get, err := http.NewRequest("GET", link, nil)
        if err != nil {
            log.Fatal(err)
        }
        if err := os.WriteFile("logo."+format, do(get, result.Token), 0o644); err != nil {
            log.Fatal(err)
        }
    }
    fmt.Println("Credits left:", result.CreditsRemaining)
}

Verificar seu saldo

curl -sS https://shapesoda.com/v1/account \
  -H "Authorization: Bearer $SHAPESODA_KEY"
const res = await fetch("https://shapesoda.com/v1/account", {
  headers: { Authorization: \`Bearer ${process.env.SHAPESODA_KEY}\` },
});
const { credits } = await res.json();
console.log(\`${credits.available} images available\`);
import os
import requests

r = requests.get(
    "https://shapesoda.com/v1/account",
    headers={"Authorization": f"Bearer {os.environ['SHAPESODA_KEY']}"},
    timeout=30,
)
r.raise_for_status()
print(r.json()["credits"]["available"], "images available")
<?php
$ch = curl_init("https://shapesoda.com/v1/account");
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SHAPESODA_KEY")],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
echo $data["credits"]["available"], " images available", PHP_EOL;
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    req, _ := http.NewRequest("GET", "https://shapesoda.com/v1/account", nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("SHAPESODA_KEY"))
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    var out struct {
        Credits struct {
            Available int \`json:"available"\`
        } \`json:"credits"\`
    }
    if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
        log.Fatal(err)
    }
    fmt.Println(out.Credits.Available, "images available")
}

Vetorizar uma imagem

POST/v1/vectorize1 crédito por imagem bem-sucedida

Converte uma imagem raster em SVG, PDF, EPS, DXF ou PNG — um formato ou vários por 1 crédito. A requisição é síncrona: a resposta chega quando o vetor está pronto, geralmente em alguns segundos (até cerca de um minuto para as maiores imagens).

Corpo da requisição

Envie a imagem de uma das três formas:

  • Bytes brutos — o próprio arquivo é o corpo; os parâmetros vão na query string. O cabeçalho Content-Type não é usado.
  • Multipart — Content-Type: multipart/form-data com o arquivo no campo image; os parâmetros podem ser campos de formulário ou parâmetros de query (campos de formulário têm prioridade). Qualquer outro campo de arquivo é rejeitado.
  • Por URL — image_url com corpo vazio (em implementação gradual).

O formato é detectado pelo conteúdo do arquivo (sua assinatura), não pelo nome do arquivo ou Content-Type.

Parâmetros

Todos os parâmetros são opcionais. Os valores diferenciam maiúsculas de minúsculas; parâmetros ou valores desconhecidos e opções conflitantes (consulte Combinações) retornam 400 bad_request com o motivo.

Ajuste fino vs. presets. Um preset apenas define padrões. Cada parâmetro abaixo é aplicado por cima dele e sempre vence: preset=cut&max_colors=4&stroke_width=0.2 é o preset de corte com 4 cores. Sem um preset, web é o ponto de partida.

ParâmetroValoresDescrição
Básico
presetweb print cut laser embroideryConfigurações prontas para o trabalho; padrão web. Veja Presets.
formatsvg pdf eps dxf pngFormato de saída, ou uma lista separada por vírgulas (svg,pdf) por 1 crédito. Padrão: o formato do preset. Veja Formatos de saída.
image_typeauto artwork artwork_sharp photoTipo de imagem. artwork — logotipos, ícones, ilustrações com bordas suaves (anti-serrilhadas); artwork_sharp — bordas de pixel nítidas sem anti-serrilhamento (pixel art, capturas de tela); photo — fotografias e imagens complexas.
qualityauto high medium lowQualidade da entrada. Use low para imagens JPEG desfocadas, ruidosas ou muito comprimidas.
detailauto low medium highNível de detalhe na saída. Aplica-se a fotos.
modelive test previewtest — resultado gratuito com marca d'água para construir uma integração (Modo de teste). preview — pré-visualização PNG gratuita de até 1 MP, vetores desbloqueados depois por 1 crédito (Pré-visualização).
retainfalse truetrue — também armazena um resultado em formato único por 24 horas. Veja Armazenamento de resultados.
retention_days1 – 30Mantenha o resultado armazenado por este número de dias em vez de 24 horas; gratuito. Veja Retenção.
image_urlURLUm endereço de imagem público em vez de um arquivo. Veja URL da imagem (em implementação).
Processamento
colorsauto unlimited 2 – 32Número de cores (a opção original). Um inteiro limita a paleta (2 gera um resultado de duas cores); unlimited mantém todas as cores que o mecanismo encontrar. Padrão auto. Para um limite mais fino, use max_colors.
max_colors2 – 256Número máximo de cores; o mecanismo pode usar menos. Não é combinado com colors ou palette.
palette2–256 × #rrggbbCores fixas, separadas por vírgulas; o resultado usa apenas essas. Na string de consulta, escreva # como %23. Não é combinado com colors ou max_colors.
palette_tolerance0 – 100Com palette: quão longe (CIE76 ΔE) uma cor da imagem pode estar de uma cor da paleta para se ajustar a ela; cores mais distantes mantêm seu próprio valor.
min_area0 – 10000Menor forma mantida, em px²; manchas menores são descartadas. Substitui o valor do preset, também para fotos.
max_pixels10000 – 4200000Reduza a escala da entrada para no máximo este número de pixels antes do rastreamento: mais rápido, menos nós. O fator vem em X-Input-Scale.
curve_tolerance0.01 – 10Tolerância de ajuste de curva em pixels de origem (padrão cerca de 0.5). Menor segue o contorno mais de perto com mais nós; maior dá menos nós. Também define smoothness a menos que você o passe.
smoothness1 – 10Suavização de contorno: maior é mais suave com menos nós.
cornerssharp smoothMantenha cantos nítidos ou arredonde-os. Padrão da análise automática.
denoise0 – 3Redução de ruído antes do rastreamento (artefatos JPEG, granulação): 0 desligado, 3 mais forte.
Desenho
drawfill stroke stroke_edgesfill — formas preenchidas; stroke — contorno de cada forma, sem preenchimento (laser, gravação); stroke_edges — apenas as bordas entre formas, cada uma uma vez, para que uma borda compartilhada não seja cortada duas vezes. Padrão do preset.
layersstacked cutouts cutouts_groupedFormas empilhadas umas sobre as outras, recortadas umas das outras (sem sobreposições), ou recortadas e agrupadas por cor. Padrão do preset.
group_bynone color layerGrupos na saída: color — um grupo por cor (as formas são recortadas umas das outras); layer — por profundidade de aninhamento (layer_0 é o fundo). SVG , camadas PDF, blocos EPS, camadas DXF. Por padrão, o SVG agrupa por cor apenas para cutouts_grouped, o DXF sempre usa camadas de cor, PDF e EPS não têm grupos.
Curvas e traço
curvesall lines lines_arcsSegmentos permitidos: all — curvas e linhas; lines — apenas segmentos retos (para máquinas sem curvas); lines_arcs — o DXF recebe linhas e arcos, outros formatos o mesmo que lines.
stroke_width0.01 – 100Largura do traço; requer draw=stroke ou stroke_edges. Em unidades de saída, ou em pixels de origem com stroke_scaling=scale. Padrão: um hairline.
stroke_color#rrggbbCor do traço em vez da cor de cada forma. Mesmo requisito que stroke_width.
stroke_scalingfixed scalefixed (padrão) — a largura permanece em unidades de saída quando o desenho é redimensionado; scale — a largura está em pixels de origem e escala com o desenho.
Preenchimento de lacunas
gap_filleroff onon — uma camada inferior sob os preenchimentos: cada forma contornada em sua própria cor, para que não haja lacunas claras entre vizinhos. Requer draw=fill. Padrão off.
gap_filler_width0 – 10Largura do contorno da camada inferior em pixels de origem (padrão 1). Requer gap_filler=on.
Tamanho
width, heightnúmero > 0Tamanho de saída em unit. Um deles — o outro segue a proporção; ambos — uma folha desse tamanho, o desenho é posicionado por aspect. Até 10 m.
unitpx pt mm cm inUnidade de width / height; padrão px.
aspectfit fill stretchCom ambos width e height: fit (padrão) — todo o desenho dentro da folha; fill — cobre a folha, o excesso é cortado; stretch — escalado por eixo para o tamanho exato.
align_x, align_y0 – 1Posicionamento do desenho na folha com aspect=fit ou fill; padrão 0.5 (centralizado).
scale>0 – 100Tamanho de saída como múltiplo do tamanho de origem. Não combinado com width / height.
dpi1 – 2400Pixels por polegada da saída PNG quando o tamanho está em pt, mm, cm ou in; padrão 300.
Específico do formato
svg_version1.1 1.0 tiny1.2Variante SVG. tiny1.2 é o perfil SVG Tiny para plotters e visualizadores embarcados: sem opacidade, sem transformações de gradiente.
svg_fixed_sizetrue falsetrue (padrão) — width e height no elemento raiz; false — apenas um viewBox, o SVG escala para o seu contêiner.
adobe_compatfalse truetrue — SVG compatível com Adobe Illustrator: tamanho sempre definido, larguras de traço em coordenadas de desenho.
dxf_curveslines arcs splinesCurvas DXF como polilinhas (padrão), polilinhas com arcos ou entidades SPLINE. curves=lines ou lines_arcs vence.
png_antialiason offAnti-aliasing em PNG; padrão on.

As opções são verificadas entre si; um conflito retorna 400 bad_request:

  • palette não pode ser combinado com colors ou max_colors, nem max_colors com colors. palette_tolerance precisa de palette.
  • stroke_width, stroke_color, stroke_scaling precisam de draw=stroke ou stroke_edges (também quando o preset define isso, ex.: laser). gap_filler=on precisa de draw=fill; gap_filler_width precisa de gap_filler=on.
  • unit precisa de width ou height. scale não pode ser combinado com width / height. aspect, align_x, align_y precisam de width e height (o alinhamento não tem efeito com aspect=stretch). dpi precisa de um tamanho em pt, mm, cm ou in.
  • retention_days funciona apenas com mode=live e não com retain=false. mode=preview retorna apenas png.

Limites de entrada

LimiteValorErro
FormatosPNG, JPEG, WebP, BMP (sem compressão, 24/32 bits), GIF (primeiro quadro), TIFF (primeira página)415 unsupported_format
Tamanho do arquivo10 MB413 too_large
ResoluçãoAté 40 megapixels. Imagens maiores que 4,2 MP ou 4096 px por lado são reduzidas automaticamente; o fator vem em X-Input-Scale422 too_many_pixels

A redução não altera o tamanho físico que você solicita com width / height. Imagens maiores não produzem vetores melhores: cerca de 2000 px no lado maior dá os resultados mais rápidos.

Resposta

O que retorna depende dos formatos que você solicita:

  • Um formato (o padrão) — 200 OK com o arquivo em si como corpo e o Content-Type do formato (veja Formatos de saída). SVG é servido inline, outros formatos como anexo vector.<format>. Nada é armazenado a menos que você passe retain=true ou retention_days.
  • Vários formatos, ou Accept: application/json — JSON com um link de download por formato; o resultado é armazenado por 24 horas. Veja Armazenamento de resultados.
CabeçalhoSignificado
X-Credits-Charged, X-Credits-RemainingO que a solicitação custou e o que resta (respostas de arquivo; JSON tem o mesmo em campos).
X-Modelive, test ou preview.
X-Input-ScaleApenas quando a entrada foi reduzida: o fator aplicado a cada lado, ex.: 0.5.
X-Result-Id, X-Result-Url, X-Result-Token, X-Result-ExpiresApenas quando o resultado é armazenado (retain=true, retention_days, mode=preview): o resultado, seu token secreto e quando é excluído.
X-Request-IdID único da solicitação.

Em qualquer erro, o corpo é JSON — veja Erros.

Presets

Um preset é um conjunto pronto de opções para o trabalho. Passe preset e ajuste com opções explícitas se necessário — elas sempre vencem o preset. Sem preset você obtém web: exatamente o SVG que a API sempre retornou.

PresetFormato padrãoO que fazUso para
webSVGCores automáticas, formas empilhadas umas sobre as outras.Sites, aplicativos, Figma
printPDFFormas recortadas umas das outras, sem sobreposições.Gráficas, pôsteres, documentos
cutSVG8 cores; formas recortadas e agrupadas por cor — um grupo por cor de vinil.Cortadoras de vinil, adesivos, Cricut, Silhouette
laserSVG2 cores, apenas contornos (draw=stroke).Corte e gravação a laser; adicione format=dxf para software CAD
embroiderySVG12 cores, formas recortadas umas das outras, pequenos pontos descartados.Digitalização para bordado

Terminal · uma chamada por preset

API="https://shapesoda.com/v1/vectorize"
AUTH="Authorization: Bearer $SHAPESODA_KEY"

# web (default): SVG for sites and apps, stacked shapes — the classic result
curl -sS "$API?preset=web" -H "$AUTH" --data-binary @logo.png -o logo.svg

# print: PDF, shapes cut out of each other; here 210 mm wide (A4)
curl -sS "$API?preset=print&width=210&unit=mm" -H "$AUTH" --data-binary @poster.png -o poster.pdf

# cut: SVG, 8 colors cut out and grouped by color — vinyl, stickers, Cricut
curl -sS "$API?preset=cut&width=4&unit=in" -H "$AUTH" --data-binary @sticker.png -o sticker.svg

# laser: 2-color outlines; DXF 100 mm wide for the laser software
curl -sS "$API?preset=laser&format=dxf&width=100&unit=mm" -H "$AUTH" --data-binary @sign.png -o sign.dxf

# embroidery: 12 colors, no tiny shapes; a fixed thread palette instead of 12 colors
curl -sS "$API?preset=embroidery&palette=%23000000,%23ffffff,%23d62828,%23003049" \
  -H "$AUTH" --data-binary @patch.png -o patch.svg

As configurações por trás de print, cut, laser e embroidery podem ser ajustadas ao longo do tempo; web permanece como está. A mesma chamada em cinco idiomas: Vetorizar com um preset.

Formatos de saída

Escolha o formato com format. Um valor retorna o arquivo em si; uma lista separada por vírgulas (svg,pdf,png) retorna JSON com um link de download por formato — todos por 1 crédito.

FormatoContent-TypeCom width / heightBom para
svgimage/svg+xmlAtributos width / height na unidade, mesmo viewBoxWeb, Figma, Illustrator, Cricut Design Space
pdfapplication/pdfTamanho da página (MediaBox)Impressão, documentos, Illustrator
epsapplication/postscriptBoundingBoxRIPs de impressão, CorelDRAW, software de design antigo
dxfapplication/dxfCoordenadas na unidade e $INSUNITSSoftware CAD, laser e CNC (curvas viram polilinhas)
pngimage/pngPixels: mm e in a 300 DPI, no máximo 4,2 MPUma re-renderização limpa do vetor em qualquer tamanho

Terminal · uma chamada por formato

API="https://shapesoda.com/v1/vectorize"
AUTH="Authorization: Bearer $SHAPESODA_KEY"

curl -sS "$API?format=svg" -H "$AUTH" --data-binary @logo.png -o logo.svg   # image/svg+xml
curl -sS "$API?format=pdf" -H "$AUTH" --data-binary @logo.png -o logo.pdf   # application/pdf
curl -sS "$API?format=eps" -H "$AUTH" --data-binary @logo.png -o logo.eps   # application/postscript
curl -sS "$API?format=dxf" -H "$AUTH" --data-binary @logo.png -o logo.dxf   # application/dxf
curl -sS "$API?format=png&width=2000" -H "$AUTH" --data-binary @logo.png -o logo.png   # image/png

# several at once for 1 credit → JSON with links (see Result storage)
curl -sS "$API?format=svg,pdf,eps,dxf,png" -H "$AUTH" --data-binary @logo.png

Tamanho físico: defina width ou height junto com unit — px (padrão), pt, mm, cm ou in. Com um lado, o outro segue a proporção; com ambos, você obtém uma folha exatamente desse tamanho e aspect (fit, fill, stretch) com align_x / align_y decide como o desenho se posiciona nela. O máximo é 10 m. Sem tamanho, a saída tem o tamanho em pixels da entrada (após qualquer redução), multiplicado por scale se fornecido. PNG obtém pixels: unidades físicas a dpi (padrão 300).

Modo de teste

Construa e teste sua integração gratuitamente com mode=test. A resposta tem o mesmo formato, cabeçalhos e estrutura que uma ao vivo, mas o resultado carrega uma marca d'água: um padrão denso entrelaçado nas próprias formas, então não pode ser removido excluindo uma camada.

  • Sem necessidade de créditos ou assinatura: X-Credits-Charged: 0 e X-Mode: test.
  • Presets, formatos e opções funcionam como no modo ao vivo, com duas exceções: retention_days e image_url são rejeitados com 400 bad_request (teste com um arquivo e sem retention_days).
  • Resultados de teste podem ser armazenados (vários formatos, retain=true ou Accept: application/json), mas apenas por 1 hora em vez de 24, e permanecem com marca d'água em todos os formatos. Eles contam para o armazenamento da conta e são removidos primeiro quando o espaço acaba.
  • Limite: 100 chamadas de teste por chave e 100 por conta por dia UTC, depois 429 test_limit com Retry-After até 00:00 UTC. Chamadas de teste não contam para o limite diário de imagens.
curl -sS "https://shapesoda.com/v1/vectorize?mode=test&preset=cut" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png \
  -D - -o test.svg
HTTP/1.1 200 OK
Content-Type: image/svg+xml; charset=utf-8
X-Credits-Charged: 0
X-Mode: test
X-Credits-Remaining: 99
X-Request-Id: req_4f1c2a9e0b7d4c3a8e21

Pré-visualização

Mostre aos seus usuários um resultado antes de pagarem. mode=preview é gratuito e retorna um PNG raster de até 1 megapixel (imagens maiores são reduzidas) — sem vetores. Quando o usuário estiver satisfeito, desbloqueie os vetores do mesmo link por 1 crédito, sem enviar a imagem novamente.

  • Gratuito, sem marca d'água: X-Credits-Charged: 0 e X-Mode: preview. Apenas format=png; não com retention_days ou image_url. Todas as outras opções se aplicam, então a pré-visualização mostra as cores e formas reais.
  • Limite: 200 pré-visualizações por conta por dia UTC, depois 429 preview_limit com Retry-After até 00:00 UTC. Pré-visualizações não contam para o limite diário de imagens ou o limite do modo de teste.
  • Uma pré-visualização é sempre armazenada por 24 horas. O link vem nos cabeçalhos X-Result-* (ou como JSON com Accept: application/json).
  • Upgrade: GET /v1/results/{id}?format=svg (ou pdf, eps, dxf) com o token do resultado. Isso cobra 1 crédito uma vez (0 se a mesma imagem já foi paga nos últimos 30 dias, veja Repetições gratuitas). Para o PNG em tamanho total em vez da pré-visualização de 1 MP, adicione upgrade=true: ?format=png&upgrade=true. ?format=png simples retorna a pré-visualização gratuita novamente.
  • Após o upgrade, o resultado é um resultado live normal (X-Mode: live): todos os outros formatos são gratuitos e as regras de retenção de Armazenamento de resultados se aplicam. Se a construção falhar, o crédito é devolvido e o resultado permanece uma pré-visualização. Sem créditos, você obtém 402 no_credits.
# 1. free preview: the PNG is the body, the link to the result is in the headers
curl -sS "https://shapesoda.com/v1/vectorize?mode=preview&preset=cut" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png -D headers.txt -o preview.png
RES=$(grep -i '^x-result-url:' headers.txt | cut -d' ' -f2 | tr -d '\r')
TOKEN=$(grep -i '^x-result-token:' headers.txt | cut -d' ' -f2 | tr -d '\r')

# 2. the user is happy: unlock the SVG for 1 credit (the result token, not the API key)
curl -sS "$RES?format=svg" -H "Authorization: Bearer $TOKEN" -o logo.svg

# 3. now every other format is free
curl -sS "$RES?format=pdf" -H "Authorization: Bearer $TOKEN" -o logo.pdf
curl -sS "$RES?format=png&upgrade=true" -H "Authorization: Bearer $TOKEN" -o logo-full.png
HTTP/1.1 200 OK
Content-Type: image/png
X-Credits-Charged: 0
X-Mode: preview
X-Result-Id: res_9b2e41c07f3a58d61e0c4b7a
X-Result-Url: https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a
X-Result-Token: q3v6X0cQ1l2mYp8rT4wZkH9sNfB7aJdE5uGiLoVxCyA
X-Result-Expires: 2026-09-30T12:00:00.000Z

Repetições gratuitas

Você paga por uma imagem uma vez. Repetições são gratuitas em dois casos, e a resposta diz X-Credits-Charged: 0:

RepetiçãoGratuita porLimite
Exatamente a mesma solicitação: mesmos bytes de imagem, mesmos parâmetros24 horasNenhum — novas tentativas seguras
A mesma imagem com outras opções ou formatos30 dias após ter sido paga20 por imagem por dia; além disso, uma repetição custa 1 crédito
  • A imagem é reconhecida por um hash SHA-256 de seus bytes, o mesmo que o site usa: uma imagem paga com crédito em shapesoda.com pode ser refeita pela API gratuitamente, e vice-versa. Um arquivo redimensionado ou re-salvo é uma nova imagem.
  • Repetições com outras opções contam para o limite diário de imagens da chave.
  • Chamadas em modo de teste e imagens feitas com a assinatura ilimitada do site não iniciam a janela de 30 dias.
  • Pré-visualizações (mode=preview) são gratuitas e não iniciam a janela de 30 dias; desbloquear os vetores de uma pré-visualização é uma chamada paga comum (1 crédito, ou uma repetição gratuita se a imagem foi paga nos últimos 30 dias).

Armazenamento de resultados

GET/v1/results/{id}grátis · token de resultado

DELETE/v1/results/{id}token de resultado

Por padrão, nada é armazenado: uma solicitação de formato único retorna o arquivo e esquece a imagem. Um resultado é mantido por 24 horas apenas quando você precisa dele depois:

  • você solicita vários formatos, ou envia Accept: application/json — exatamente esse valor; clientes que enviam application/json, */* ainda recebem o arquivo;
  • você passa retain=true ou retention_days — você ainda recebe o arquivo, e o link vem nos cabeçalhos X-Result-*;
  • você usa mode=preview (sempre armazenado, veja Pré-visualização);
  • a chamada vem através de MCP.
# several formats → JSON (or add -H "Accept: application/json" for a single format)
curl -sS "https://shapesoda.com/v1/vectorize?format=svg,pdf" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png
{
  "result_id": "res_9b2e41c07f3a58d61e0c4b7a",
  "result_url": "https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a",
  "result_token": "q3v6X0cQ1l2mYp8rT4wZkH9sNfB7aJdE5uGiLoVxCyA",
  "width": 1200,
  "height": 800,
  "input_scale": 1,
  "credits_charged": 1,
  "credits_remaining": 99,
  "mode": "live",
  "expires_at": "2026-09-30T12:00:00.000Z",
  "files": {
    "svg": "https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a?format=svg",
    "pdf": "https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a?format=pdf"
  }
}
CampoSignificado
result_id, result_urlO resultado armazenado e seu endereço.
result_tokenToken secreto para download; mostrado uma vez. É também a chave de descriptografia — não podemos recuperá-lo.
filesUm link de download por formato solicitado.
expires_atQuando o resultado é excluído (24 horas, ou retention_days dias).
width, height, input_scaleTamanho em pixels da imagem vetorizada e o fator de redução (1 se não reduzida).
credits_charged, credits_remaining, modeO mesmo que os cabeçalhos X-Credits-* e X-Mode de uma resposta de arquivo.

Baixe com o token de resultado, não sua chave de API: Authorization: Bearer <result_token> (recomendado). ?token=<result_token> também é aceito, mas um token em uma URL pode vazar para logs de acesso, histórico do navegador e análises. Qualquer pessoa com o token pode baixar o resultado, então trate o link como o próprio arquivo. Sem format você obtém o primeiro formato da solicitação original: o primeiro na sua lista format, na ordem em que você escreveu (duplicatas são ignoradas), ou o formato do preset se você não passou format. O objeto files da resposta JSON lista os formatos na mesma ordem. Um formato que você não solicitou é construído uma vez a partir da fonte armazenada com exatamente o mesmo preset, opções e modo — gratuito e sem novo upload. A construção é limitada pela taxa de solicitações da conta que possui o resultado (60 por minuto, 429 rate_limited com Retry-After); formatos já armazenados não são.

RES="https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a"
TOKEN="q3v6X0cQ1l2mYp8rT4wZkH9sNfB7aJdE5uGiLoVxCyA"   # result_token, not the API key

curl -sS "$RES?format=pdf" -H "Authorization: Bearer $TOKEN" -o logo.pdf
# a format you did not ask for is built from the stored source, free of charge
curl -sS "$RES?format=dxf" -H "Authorization: Bearer $TOKEN" -o logo.dxf
# the same with the token in the link (less safe: URLs end up in logs)
curl -sS "$RES?format=eps&token=$TOKEN" -o logo.eps
# delete now instead of waiting 24 hours
curl -sS -X DELETE "$RES" -H "Authorization: Bearer $TOKEN"

Com retain=true (ou retention_days) e um formato, o arquivo vem como de costume mais o link:

HTTP/1.1 200 OK
Content-Type: image/svg+xml; charset=utf-8
X-Credits-Charged: 1
X-Mode: live
X-Result-Id: res_9b2e41c07f3a58d61e0c4b7a
X-Result-Url: https://shapesoda.com/v1/results/res_9b2e41c07f3a58d61e0c4b7a
X-Result-Token: q3v6X0cQ1l2mYp8rT4wZkH9sNfB7aJdE5uGiLoVxCyA
X-Result-Expires: 2026-09-30T12:00:00.000Z
  • Criptografado. A imagem de origem e cada formato são criptografados com AES-256-GCM sob uma chave derivada do token. Mantemos apenas um hash do token, então um resultado armazenado não pode ser lido sem o link.
  • Excluído após 24 horas (pré-visualizações também), resultados em modo de teste após 1 hora, resultados com retention_days após esse número de dias (veja expires_at). DELETE remove imediatamente e retorna 204.
  • Um id desconhecido, um token errado e um resultado expirado retornam 404 not_found.
  • Se o armazenamento estiver cheio, solicitações JSON recebem 503 storage_full (não cobrado; tente novamente após Retry-After ou solicite um formato), e retain=true ou retention_days retornam o arquivo sem os cabeçalhos X-Result-*.

Retenção

Por padrão, um resultado armazenado dura 24 horas. Se seus usuários voltarem para buscar seus arquivos, solicite um período maior com retention_days — 1 a 30 dias, sem custo. Ele armazena o resultado mesmo para um único formato (como retain=true faz) e retorna o link nos cabeçalhos X-Result-*; expires_at / X-Result-Expires mostram o prazo.

  • Apenas para mode=live, e não junto com retain=false. Resultados de teste duram 1 hora, pré-visualizações 24 horas.
  • Resultados armazenados contam para a cota de armazenamento da conta. Quando o espaço acaba, os resultados mais antigos são removidos primeiro nesta ordem: resultados de teste, pré-visualizações, resultados ao vivo sem retention_days, e por último resultados com retention_days. Se o armazenamento ainda estiver cheio, você recebe 503 storage_full (não cobrado).
  • Formatos extras criados a partir de um resultado armazenado são gratuitos durante todo o período. DELETE remove um resultado a qualquer momento.

Terminal · manter por 30 dias

curl -sS "https://shapesoda.com/v1/vectorize?retention_days=30" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png -D - -o logo.svg   # X-Result-Expires: in 30 days

URL de imagemEm implementação

Em implementação. Entrada por URL está sendo ativada gradualmente. Até que esteja ativa, uma solicitação com image_url recebe 503 url_unavailable — envie o arquivo em vez disso.

Em vez de um arquivo, passe o endereço de uma imagem pública em image_url (parâmetro de consulta ou campo de formulário) e deixe o corpo vazio. Todo o resto funciona como com um arquivo.

curl -sS https://shapesoda.com/v1/vectorize \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  -F image_url=https://example.com/logo.png \
  -F preset=cut \
  -o logo.svg
  • http ou https, porta 80 ou 443, sem nome de usuário ou senha, até 2048 caracteres — caso contrário, 400 url_invalid.
  • A imagem é baixada através de um proxy de saída isolado: endereços privados, loopback, link-local e de metadados de nuvem são bloqueados, também após redirecionamentos (422 url_blocked).
  • No máximo 3 redirecionamentos, 10 segundos (504 url_timeout) e 10 MB (413 too_large). Um servidor que não responde ou retorna um erro gera 422 url_unreachable.
  • Envie um arquivo ou image_url, não ambos. Não disponível em modo de teste.

Conta e créditos

GET/v1/accountfree

Retorna a conta à qual a chave pertence e seus créditos. Os créditos são contados por mês civil em UTC.

{
  "account": { "id": "acc_3f9c1e7a2b4d6e80", "name": "[email protected]" },
  "credits": {
    "available": 97,
    "balance": 97,
    "quota": 0,
    "quota_used": 0,
    "quota_left": 0,
    "spent_month": 3,
    "spend_limit": null
  }
}
CampoSignificado
availableImagens que você pode processar agora: quota_left + balance, limitado pelo que resta do seu limite de gastos.
balanceCréditos comprados, compartilhados com o site. Inclui subscription_balance.
subscription_balanceCréditos do seu plano de API (usados primeiro). Não utilizados são acumulados até 5× o valor mensal e expiram quando o plano termina.
quota, quota_used, quota_leftCréditos mensais incluídos, se seu plano tiver (eles são usados primeiro). 0 para contas pré-pagas.
spent_monthCréditos usados neste mês civil (UTC).
spend_limitSeu próprio limite mensal em créditos, ou null para sem limite. Defina no menu da conta.

Lista de preços

GET/v1/planspúblico · sem chave

Os mesmos preços de Preços, em JSON, sem autenticação. Retorna api_plans (planos mensais de crédito com credits, price e per_image), subscriptions (planos do site), credits_never_expire, redownload_free_days, result_retention_hours, signup_credits, enterprise_contact e payments_enabled / billing (dados de checkout quando os pagamentos estão ativos para o visitante).

Erros

Erros usam códigos de status HTTP padrão e um corpo JSON com um code estável e legível por máquina. Use code para decisões, não message: a mensagem é uma dica legível por humanos e pode mudar ou ser localizada.

{
  "error": {
    "code": "too_many_pixels",
    "message": "Human-readable description",
    "request_id": "req_4f1c2a9e0b7d4c3a8e21"
  }
}
HTTPCódigoSignificadoO que fazer
400bad_requestParâmetro ou valor desconhecido, opções conflitantes, formulário multipart malformado, um arquivo em um campo diferente de image, ou ambos arquivo e image_url.Corrija a solicitação. Não tente novamente como está.
400emptyA solicitação não tem imagem.Envie o arquivo como corpo ou no campo image.
400url_invalidimage_url não é uma URL http(s) na porta 80 ou 443, tem credenciais ou é maior que 2048 caracteres.Corrija a URL.
401unauthorizedChave de API ausente, malformada ou revogada.Verifique o cabeçalho Authorization: Bearer; crie uma nova chave se necessário.
402no_creditsSem créditos restantes na conta (também ao desbloquear uma pré-visualização).Compre um pacote de créditos. Não tente novamente.
402spend_limitSeu limite mensal de gastos foi atingido.Aumente ou remova o limite no menu da conta, ou aguarde o próximo mês.
404not_foundCaminho desconhecido, ou um link de upload/resultado desconhecido, expirado ou com token errado.Verifique a URL.
405method_not_allowedMétodo HTTP errado para este caminho.Use o método desta referência.
413too_largeO arquivo (ou a imagem em image_url) é maior que 10 MB.Reduza ou recomprima a imagem.
415unsupported_formatNão é um arquivo PNG, JPEG, WebP, BMP descomprimido, GIF ou TIFF (detectado pelo conteúdo).Converta a imagem para PNG primeiro.
422invalid_imageO arquivo está danificado, truncado ou não pode ser decodificado.Reexporte a imagem.
422too_many_pixelsMais de 40 MP, ou a imagem precisa de muita memória.Reduza a imagem.
422url_blockedimage_url aponta para um endereço privado, local ou interno.Use uma URL pública. Não tente novamente.
422url_unreachableA imagem em image_url não pôde ser baixada.Verifique a URL, ou envie o arquivo.
429rate_limitedMuitas solicitações para esta chave (ou muitas falhas de login do seu IP).Aguarde Retry-After segundos e tente novamente.
429daily_limitA chave atingiu seu limite diário.Aguarde até 00:00 UTC, ou use outra chave.
429preview_limitA conta fez 200 pré-visualizações (mode=preview) hoje.Aguarde até 00:00 UTC (Retry-After), ou use mode=live.
429test_limitO limite diário de modo de teste da chave ou conta foi atingido.Aguarde até 00:00 UTC (Retry-After), ou use mode=live.
500internalAlgo deu errado do nosso lado. Você não é cobrado.Tente novamente uma vez após uma pausa curta; se persistir, envie-nos por e-mail o request_id.
503busyTodos os workers estão ocupados e a fila está cheia.Tente novamente após Retry-After segundos (geralmente 5).
503timeoutO processamento levou mais de 60 segundos. Você não é cobrado.Tente uma imagem menor ou menos cores.
503storage_fullO armazenamento de resultados está cheio. Você não é cobrado.Tente novamente após Retry-After segundos, ou solicite um único formato.
503url_unavailableEntrada por URL está desativada ou temporariamente indisponível.Envie o arquivo em vez disso.
504url_timeoutO download de image_url levou mais de 10 segundos. Você não é cobrado.Envie o arquivo em vez disso.

Créditos são reservados quando o processamento começa e reembolsados automaticamente se falhar, então você só paga pelos resultados que recebe.

Limites de taxa

LimitePadrãoErro
Solicitações por chave60 por minuto, com rajadas de até 10 de uma vez429 rate_limited + Retry-After
Solicitações concorrentes por conta5 de uma vez (em processamento ou na fila); envie até 5 em paralelo429 rate_limited + Retry-After: 2, não cobrado
Imagens por chave por dia5.000, repetições gratuitas com outras opções incluídas (reinicia às 00:00 UTC)429 daily_limit + Retry-After
Autenticação falha por IP30 por minuto429 rate_limited + Retry-After
Pré-visualizações (mode=preview)200 por conta por dia429 preview_limit + Retry-After
Chamadas em modo de teste100 por chave e 100 por conta por dia429 test_limit + Retry-After
Repetições gratuitas de uma imagem paga com outras opções20 por imagem por dia, depois 1 crédito cada—

O orçamento por chave é compartilhado entre a API REST e o servidor MCP. GET /v1/account não conta contra ele. Precisa de mais? Envie e-mail para [email protected].

Tentativas e idempotência

  • Tente novamente apenas rate_limited e busy (após o cabeçalho Retry-After), e internal uma vez com um pequeno intervalo. Outros erros 4xx falharão novamente.
  • Retry-After é um número inteiro de segundos, nunca uma data HTTP. Vem com rate_limited (o tempo até uma solicitação ser permitida novamente), busy e timeout (5), storage_full (60) e os limites diários (os segundos restantes até 00:00 UTC).
  • Uma solicitação rejeitada não custa nada contra o limite. O limite por minuto é um token bucket: uma solicitação respondida com 429 rate_limited não usa um token, então esperar por Retry-After é suficiente.
  • Tentativas são gratuitas: repetir a mesma solicitação (mesmos bytes de imagem e mesmos parâmetros) dentro de 24 horas não é cobrado novamente — a resposta tem X-Credits-Charged: 0. A mesma imagem com outras opções é gratuita por 30 dias também, dentro de um limite diário — veja Repetições gratuitas.
  • Use um timeout de cliente generoso — pelo menos 120 segundos — para que uma solicitação lenta não seja abandonada após a imagem já ter sido processada e cobrada.
  • Para lotes, execute algumas solicitações em paralelo (2–4) em vez de dezenas: solicitações além da rajada são rejeitadas com 429.
async function vectorize(image, attempt = 0, retriedInternal = false) {
  const res = await fetch("https://shapesoda.com/v1/vectorize", {
    method: "POST",
    headers: { Authorization: \`Bearer ${process.env.SHAPESODA_KEY}\` },
    body: image,
    signal: AbortSignal.timeout(120_000),
  });
  if (res.ok) return res.text();
  const { error } = await res.json();
  if (["rate_limited", "busy"].includes(error.code) && attempt < 3) {
    const wait = Number(res.headers.get("Retry-After") ?? 2 ** attempt);
    await new Promise((r) => setTimeout(r, wait * 1000));
    return vectorize(image, attempt + 1, retriedInternal);
  }
  if (error.code === "internal" && !retriedInternal) { // one retry, short backoff
    await new Promise((r) => setTimeout(r, 1000));
    return vectorize(image, attempt, true);
  }
  throw new Error(\`${error.code}: ${error.message} (${error.request_id})\`);
}

Checklist de produção

  • Timeout: defina o timeout do cliente para pelo menos 120 segundos.
  • Verifique antes de salvar: escreva o corpo em um arquivo apenas quando o status for 200 e Content-Type for o que você pediu (image/svg+xml, application/pdf, …, ou application/json para vários formatos). Erros são JSON — nunca os salve como um .svg.
  • Respeite Retry-After (segundos); tente novamente apenas rate_limited, busy e, uma vez, internal. Veja Tentativas.
  • Mantenha a chave de API em segredo e apenas no seu servidor: não em um navegador, aplicativo móvel ou repositório. Revogue uma chave vazada.
  • Tokens de resultado: envie-os no cabeçalho Authorization em vez de em ?token=, e mantenha links de resultado fora de logs e análises.
  • Desenvolvimento: use mode=test enquanto desenvolve (gratuito, com marca d'água, 100 chamadas por dia); mude para live quando publicar.
  • Suporte: registre X-Request-Id em cada falha.

Segurança e privacidade

  • As chaves são criptografadas. Armazenamos apenas um hash com chave (HMAC-SHA-256) e um prefixo curto de cada chave, então a chave é exibida uma única vez e não pode ser recuperada — apenas revogada e substituída.
  • Você define o limite de gastos. Um teto mensal no menu da conta se aplica a todas as chaves, ao servidor MCP e ao site: nem um agente nem uma chave vazada podem gastar mais do que você permite.
  • As imagens são armazenadas apenas quando você precisa delas. Uma solicitação de formato único é processada em memória em um processo de trabalho isolado e descartada — nada é gravado em disco ou em um banco de dados. Vários formatos, retain=true, retention_days, pré-visualizações e resultados MCP são mantidos por 24 horas (até 30 dias com retention_days), criptografados com uma chave que somente o detentor do token de resultado possui, e depois excluídos; qualquer pessoa com o link e o token pode baixá-los. Uploads MCP ficam em memória por no máximo 10 minutos. As imagens nunca são usadas para treinar modelos. Consulte Armazenamento de resultados e a Política de Privacidade.
  • A saída SVG é sanitizada. Cada resultado é verificado contra uma lista de permissões estrita (caminhos, grupos e gradientes com valores de cor simples) antes de ser retornado — sem scripts, manipuladores de eventos, referências externas ou conteúdo incorporado. PDF, EPS e DXF são gerados apenas a partir da geometria vetorial e das cores.
  • As entradas são verificadas. Os formatos são detectados pelo conteúdo, a resolução é verificada antes da decodificação (proteção contra bombas de descompressão) e cada tarefa é executada em seu próprio processo com um limite de tempo. Imagens por URL são baixadas em um processo separado por meio de um proxy de saída que bloqueia endereços internos.

SDKs e CLI

As bibliotecas oficiais de cliente encapsulam a mesma API com opções tipadas, novas tentativas automáticas após 429 / 503 e downloads de resultados. Elas são geradas a partir da especificação OpenAPI, então todos os parâmetros desta página estão disponíveis.

  • Python: pip install shapesoda (PyPI). Shapesoda(key).vectorize_file("logo.png", formats=["svg","pdf"], preset="print"), além de preview_file() e upgrade().
  • JavaScript / TypeScript: npm install @shapesoda/sdk (npm). new Shapesoda(key).vectorizeFile("logo.png", { formats: ["svg","pdf"], preset: "print" }), além de previewFile() e upgrade(). Funciona com import e require, Node 18+.
  • Go, PHP, Ruby, Java, C#: clientes gerados estão na pasta clients/sdk do repositório; a publicação em pkg.go.dev, Packagist, RubyGems, Maven Central e NuGet está em andamento. Até lá, gere um a partir da especificação (veja abaixo) ou use os exemplos HTTP simples acima.
  • CLI: npx shapesoda ./in ./out --preset cut --format svg,dxf (npm, Node 18+). Pasta de entrada, pasta de saída, novas tentativas e pular já concluídos integrados; --preview, --test, shapesoda account.

Especificação OpenAPI

Uma descrição OpenAPI 3.1 legível por máquina da API — importe-a no Postman, Insomnia ou Bruno, ou gere um cliente:

GET https://shapesoda.com/openapi.json

npx @openapitools/openapi-generator-cli generate \
  -i https://shapesoda.com/openapi.json -g typescript-fetch -o ./shapesoda-client

MCP para aplicativos e agentes de IA

O Model Context Protocol permite que aplicativos e agentes de IA chamem ferramentas externas. O servidor MCP do Shapesoda transforma imagens em SVG, PDF, EPS, DXF ou PNG diretamente no ChatGPT, Claude, Cursor, Claude Code e em qualquer outro cliente MCP. Uma conta Shapesoda conecta-se com um login (OAuth); agentes e scripts ainda podem usar uma chave de API com os mesmos créditos e limites da API REST.

POSThttps://shapesoda.com/mcpStreamable HTTP · OAuth ou chave Bearer

Grátis: 3 vetorizações por dia sem conta, 10 por dia com uma conta conectada (OAuth ou chave), depois créditos. REST e o site não são afetados. Um plano pessoal do site é ilimitado em chats quando a conta está conectada com um login; chamadas com chave de API usam créditos. Alterar as configurações da mesma imagem na janela do Shapesoda é grátis por 24 horas, então uma imagem é cobrada uma única vez. Modo de teste, pré-visualizações, retention_days e vectorize_url exigem uma conta. Os limites são redefinidos às 00:00 UTC; get_usage e free_remaining_today em cada resultado mostram o que resta. Nada é vendido dentro dos chats: preços e checkout estão em o site.

Conectar no ChatGPT, Claude, Cursor ou Claude Code

URL do servidor: https://shapesoda.com/mcp. Quando as imagens grátis acabarem, o chat oferece conectar sua conta Shapesoda: faça login com Google ou um código de e-mail e pressione Permitir. Os aplicativos conectados estão listados em sua conta, onde você pode desconectá-los.

ChatGPT

Encontre Shapesoda no diretório de aplicativos do ChatGPT, ou adicione a URL do servidor como um aplicativo personalizado no modo de desenvolvedor. Anexe uma imagem e escreva @Shapesoda ou "transforme isso em SVG": o ChatGPT pede uma vez para compartilhar o arquivo (a entrada image de vectorize), e a janela do Shapesoda mostra um controle deslizante de antes/depois, configurações, downloads em todos os formatos e Copiar SVG.

Claude

No claude.ai ou Claude Desktop, abra Configurações → Conectores e adicione Shapesoda do diretório, ou "Adicionar conector personalizado" com a URL do servidor. O Claude não passa arquivos anexados para aplicativos, então peça para vetorizar uma imagem: ele chama open_editor, e você escolhe o arquivo na janela do Shapesoda.

Claude Code

Adicione o servidor e execute /mcp, escolha shapesoda e Autenticar para conectar sua conta no navegador:

claude mcp add --transport http shapesoda https://shapesoda.com/mcp

Ou use uma chave de API em vez do login:

claude mcp add --transport http shapesoda https://shapesoda.com/mcp \
  --header "Authorization: Bearer $SHAPESODA_KEY"

Cursor

Instale o plugin Shapesoda do marketplace do Cursor, ou adicione o servidor a ~/.cursor/mcp.json (ou .cursor/mcp.json em um projeto). O Cursor oferece conectar a conta no primeiro uso.

{
  "mcpServers": {
    "shapesoda": { "url": "https://shapesoda.com/mcp" }
  }
}

Com uma chave de API, o Cursor lê ${env:SHAPESODA_KEY} do seu ambiente, então a chave fica fora do arquivo:

{
  "mcpServers": {
    "shapesoda": {
      "url": "https://shapesoda.com/mcp",
      "headers": { "Authorization": "Bearer ${env:SHAPESODA_KEY}" }
    }
  }
}

Outros clientes que suportam servidores MCP remotos (Streamable HTTP) funcionam da mesma forma: URL https://shapesoda.com/mcp, login OAuth 2.1 (descoberto de /.well-known/oauth-protected-resource) ou o cabeçalho Authorization: Bearer <key>. O servidor responde com JSON (sem stream SSE) e suporta as versões de protocolo 2025-11-25, 2025-06-18 e 2025-03-26.

Ferramentas

vectorize

Converte uma imagem em SVG, PDF, EPS, DXF ou PNG. A imagem vem exatamente de uma forma: image (um arquivo anexado à mensagem do ChatGPT), upload_id (de create_upload) ou um pequeno image_base64. Aceita as mesmas opções da API REST — preset, format como lista, draw, layers, palette, transparency, width / height / unit, mode=test, mode=preview (pré-visualização PNG grátis; abra o link com format=svg em vez de format=png para desbloquear os vetores por 1 crédito), retention_days e o restante — veja Parâmetros. Retorna um link de download por formato, válido por 24 horas, e mostra a janela do Shapesoda em chats que a suportam. Usa 1 crédito para todos os formatos.

open_editor

Abre a janela do Shapesoda sem uma imagem: o usuário escolhe o arquivo lá. Para o Claude, que não passa anexos para aplicativos. Não usa créditos.

create_upload

Cria um link de upload único para um arquivo local (válido por 10 minutos, até 10 MB) e retorna o comando curl para enviá-lo. Para agentes com terminal.

revectorize

Usado apenas pela janela do Shapesoda (oculto do modelo): vetoriza a mesma imagem novamente com outras configurações, grátis por 24 horas após a primeira vetorização, até 30 alterações por dia.

vectorize_url

Baixa uma imagem pública por URL e a vetoriza como vectorize. Listado apenas quando a entrada por URL está habilitada (em implementação); sem modo de teste.

get_usage

Mostra vetorizações grátis restantes hoje, créditos disponíveis, saldo, uso mensal e seu limite de gastos. Grátis.

Como um agente envia um arquivo

Agentes geralmente não podem anexar arquivos binários a uma chamada de ferramenta, então imagens grandes passam por um link de upload de curta duração:

  1. create_upload retorna upload_id e um segredo upload_url.
  2. Upload com curl O agente executa o comando retornado: curl -sS -X PUT --data-binary @image.png "<upload_url>". O link aceita um arquivo PNG, JPEG, WebP, BMP, GIF ou TIFF e não precisa de outra autenticação.
  3. vectorize com upload_id O resultado tem um link por formato solicitado em files (válido por 24 horas; o token está no link), download_url para o primeiro, o SVG inline quando tem menos de 60 KB, o tamanho da imagem, credits_charged e credits_remaining.

Para imagens pequenas (até 2 MB), o agente pode pular o upload e passar o arquivo diretamente como image_base64.

O link de upload é servido por PUT /v1/uploads/{upload_id}. Ele faz parte do fluxo de upload MCP e está listado na especificação OpenAPI para completude; você não o chama diretamente em uma integração REST.

Preços

Assinatura mensal com o número de créditos que você escolher. Um crédito é uma imagem vetorizada com sucesso, em todos os formatos solicitados nessa chamada, pela API, MCP ou site. Cancele quando quiser.

  • Créditos não usados são acumulados (até 5 meses de valor)
  • Cancele quando quiser; os créditos duram até o fim do plano
  • Saldo compartilhado com o site
  • Solicitações com falha são grátis

50

créditos / mês

$3.99 / mês

$0.08 por imagem

200

créditos / mês

$9.99 / mês

$0.05 por imagem

5.000

créditos / mês

$74.99 / mês

$0.015 por imagem

20.000

créditos / mês

$199.99 / mês

$0.01 por imagem

100.000

créditos / mês

$499.99 / mês

$0.005 por imagem

Empresas ou mais de 100 mil imagens por mês? Envie um e-mail para [email protected] para preços por volume e limites maiores.