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.
- 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" - Envie uma imagem
Envie os bytes brutos da imagem como corpo da requisição e passe as opções na query string:
Ou envie um formuláriocurl -sS "https://shapesoda.com/v1/vectorize?colors=auto" \ -H "Authorization: Bearer $SHAPESODA_KEY" \ --data-binary @logo.png \ -D - -o logo.svgmultipart/form-datacom o arquivo no campoimagee 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 - Leia a resposta
Em caso de sucesso, você recebe
200com 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_4f1c2a9e0b7d4c3a8e21Cabeçalho Significado X-Credits-ChargedCréditos usados por esta requisição: 1para uma vetorização bem-sucedida (qualquer que seja o número de formatos),0no modo de teste e para repetições gratuitas. Requisições com falha não são cobradas.X-Modelive, outestno 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.
GETeDELETE /v1/results/{id}usam o token de resultado, não a chave de API. Envie-o comoAuthorization: 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-Typenão é usado. - Multipart —
Content-Type: multipart/form-datacom o arquivo no campoimage; 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_urlcom 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âmetro | Valores | Descrição |
|---|---|---|
| Básico | ||
preset | web print cut laser embroidery | Configurações prontas para o trabalho; padrão web. Veja Presets. |
format | svg pdf eps dxf png | Formato 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_type | auto artwork artwork_sharp photo | Tipo 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. |
quality | auto high medium low | Qualidade da entrada. Use low para imagens JPEG desfocadas, ruidosas ou muito comprimidas. |
detail | auto low medium high | Nível de detalhe na saída. Aplica-se a fotos. |
mode | live test preview | test — 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). |
retain | false true | true — também armazena um resultado em formato único por 24 horas. Veja Armazenamento de resultados. |
retention_days | 1 – 30 | Mantenha o resultado armazenado por este número de dias em vez de 24 horas; gratuito. Veja Retenção. |
image_url | URL | Um endereço de imagem público em vez de um arquivo. Veja URL da imagem (em implementação). |
| Processamento | ||
colors | auto unlimited 2 – 32 | Nú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_colors | 2 – 256 | Número máximo de cores; o mecanismo pode usar menos. Não é combinado com colors ou palette. |
palette | 2–256 × #rrggbb | Cores 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_tolerance | 0 – 100 | Com 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_area | 0 – 10000 | Menor forma mantida, em px²; manchas menores são descartadas. Substitui o valor do preset, também para fotos. |
max_pixels | 10000 – 4200000 | Reduza 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_tolerance | 0.01 – 10 | Tolerâ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. |
smoothness | 1 – 10 | Suavização de contorno: maior é mais suave com menos nós. |
corners | sharp smooth | Mantenha cantos nítidos ou arredonde-os. Padrão da análise automática. |
denoise | 0 – 3 | Redução de ruído antes do rastreamento (artefatos JPEG, granulação): 0 desligado, 3 mais forte. |
| Desenho | ||
draw | fill stroke stroke_edges | fill — 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. |
layers | stacked cutouts cutouts_grouped | Formas empilhadas umas sobre as outras, recortadas umas das outras (sem sobreposições), ou recortadas e agrupadas por cor. Padrão do preset. |
group_by | none color layer | Grupos 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 | ||
curves | all lines lines_arcs | Segmentos 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_width | 0.01 – 100 | Largura 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 | #rrggbb | Cor do traço em vez da cor de cada forma. Mesmo requisito que stroke_width. |
stroke_scaling | fixed scale | fixed (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_filler | off on | on — 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_width | 0 – 10 | Largura do contorno da camada inferior em pixels de origem (padrão 1). Requer gap_filler=on. |
| Tamanho | ||
width, height | número > 0 | Tamanho 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. |
unit | px pt mm cm in | Unidade de width / height; padrão px. |
aspect | fit fill stretch | Com 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_y | 0 – 1 | Posicionamento do desenho na folha com aspect=fit ou fill; padrão 0.5 (centralizado). |
scale | >0 – 100 | Tamanho de saída como múltiplo do tamanho de origem. Não combinado com width / height. |
dpi | 1 – 2400 | Pixels por polegada da saída PNG quando o tamanho está em pt, mm, cm ou in; padrão 300. |
| Específico do formato | ||
svg_version | 1.1 1.0 tiny1.2 | Variante SVG. tiny1.2 é o perfil SVG Tiny para plotters e visualizadores embarcados: sem opacidade, sem transformações de gradiente. |
svg_fixed_size | true false | true (padrão) — width e height no elemento raiz; false — apenas um viewBox, o SVG escala para o seu contêiner. |
adobe_compat | false true | true — SVG compatível com Adobe Illustrator: tamanho sempre definido, larguras de traço em coordenadas de desenho. |
dxf_curves | lines arcs splines | Curvas DXF como polilinhas (padrão), polilinhas com arcos ou entidades SPLINE. curves=lines ou lines_arcs vence. |
png_antialias | on off | Anti-aliasing em PNG; padrão on. |
As opções são verificadas entre si; um conflito retorna 400 bad_request:
palettenão pode ser combinado comcolorsoumax_colors, nemmax_colorscomcolors.palette_toleranceprecisa depalette.stroke_width,stroke_color,stroke_scalingprecisam dedraw=strokeoustroke_edges(também quando o preset define isso, ex.:laser).gap_filler=onprecisa dedraw=fill;gap_filler_widthprecisa degap_filler=on.unitprecisa dewidthouheight.scalenão pode ser combinado comwidth/height.aspect,align_x,align_yprecisam dewidtheheight(o alinhamento não tem efeito comaspect=stretch).dpiprecisa de um tamanho empt,mm,cmouin.retention_daysfunciona apenas commode=livee não comretain=false.mode=previewretorna apenaspng.
Limites de entrada
| Limite | Valor | Erro |
|---|---|---|
| Formatos | PNG, JPEG, WebP, BMP (sem compressão, 24/32 bits), GIF (primeiro quadro), TIFF (primeira página) | 415 unsupported_format |
| Tamanho do arquivo | 10 MB | 413 too_large |
| Resolução | Até 40 megapixels. Imagens maiores que 4,2 MP ou 4096 px por lado são reduzidas automaticamente; o fator vem em X-Input-Scale | 422 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 OKcom o arquivo em si como corpo e oContent-Typedo formato (veja Formatos de saída). SVG é servidoinline, outros formatos como anexovector.<format>. Nada é armazenado a menos que você passeretain=trueouretention_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çalho | Significado |
|---|---|
X-Credits-Charged, X-Credits-Remaining | O que a solicitação custou e o que resta (respostas de arquivo; JSON tem o mesmo em campos). |
X-Mode | live, test ou preview. |
X-Input-Scale | Apenas 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-Expires | Apenas quando o resultado é armazenado (retain=true, retention_days, mode=preview): o resultado, seu token secreto e quando é excluído. |
X-Request-Id | ID ú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.
| Preset | Formato padrão | O que faz | Uso para |
|---|---|---|---|
web | SVG | Cores automáticas, formas empilhadas umas sobre as outras. | Sites, aplicativos, Figma |
print | Formas recortadas umas das outras, sem sobreposições. | Gráficas, pôsteres, documentos | |
cut | SVG | 8 cores; formas recortadas e agrupadas por cor — um grupo por cor de vinil. | Cortadoras de vinil, adesivos, Cricut, Silhouette |
laser | SVG | 2 cores, apenas contornos (draw=stroke). | Corte e gravação a laser; adicione format=dxf para software CAD |
embroidery | SVG | 12 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.
| Formato | Content-Type | Com width / height | Bom para |
|---|---|---|---|
svg | image/svg+xml | Atributos width / height na unidade, mesmo viewBox | Web, Figma, Illustrator, Cricut Design Space |
pdf | application/pdf | Tamanho da página (MediaBox) | Impressão, documentos, Illustrator |
eps | application/postscript | BoundingBox | RIPs de impressão, CorelDRAW, software de design antigo |
dxf | application/dxf | Coordenadas na unidade e $INSUNITS | Software CAD, laser e CNC (curvas viram polilinhas) |
png | image/png | Pixels: mm e in a 300 DPI, no máximo 4,2 MP | Uma 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: 0eX-Mode: test. - Presets, formatos e opções funcionam como no modo ao vivo, com duas exceções:
retention_dayseimage_urlsão rejeitados com400 bad_request(teste com um arquivo e semretention_days). - Resultados de teste podem ser armazenados (vários formatos,
retain=trueouAccept: 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_limitcomRetry-Afteraté 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: 0eX-Mode: preview. Apenasformat=png; não comretention_daysouimage_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_limitcomRetry-Afteraté 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 comAccept: application/json). - Upgrade:
GET /v1/results/{id}?format=svg(oupdf,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, adicioneupgrade=true:?format=png&upgrade=true.?format=pngsimples retorna a pré-visualização gratuita novamente. - Após o upgrade, o resultado é um resultado
livenormal (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ém402 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ção | Gratuita por | Limite |
|---|---|---|
| Exatamente a mesma solicitação: mesmos bytes de imagem, mesmos parâmetros | 24 horas | Nenhum — novas tentativas seguras |
| A mesma imagem com outras opções ou formatos | 30 dias após ter sido paga | 20 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 enviamapplication/json, */*ainda recebem o arquivo; - você passa
retain=trueouretention_days— você ainda recebe o arquivo, e o link vem nos cabeçalhosX-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"
}
}
| Campo | Significado |
|---|---|
result_id, result_url | O resultado armazenado e seu endereço. |
result_token | Token secreto para download; mostrado uma vez. É também a chave de descriptografia — não podemos recuperá-lo. |
files | Um link de download por formato solicitado. |
expires_at | Quando o resultado é excluído (24 horas, ou retention_days dias). |
width, height, input_scale | Tamanho em pixels da imagem vetorizada e o fator de redução (1 se não reduzida). |
credits_charged, credits_remaining, mode | O 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_daysapós esse número de dias (vejaexpires_at).DELETEremove imediatamente e retorna204. - 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ósRetry-Afterou solicite um formato), eretain=trueouretention_daysretornam o arquivo sem os cabeçalhosX-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 comretain=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 comretention_days. Se o armazenamento ainda estiver cheio, você recebe503 storage_full(não cobrado). - Formatos extras criados a partir de um resultado armazenado são gratuitos durante todo o período.
DELETEremove 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
httpouhttps, 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 gera422 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
}
}
| Campo | Significado |
|---|---|
available | Imagens que você pode processar agora: quota_left + balance, limitado pelo que resta do seu limite de gastos. |
balance | Créditos comprados, compartilhados com o site. Inclui subscription_balance. |
subscription_balance | Cré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_left | Créditos mensais incluídos, se seu plano tiver (eles são usados primeiro). 0 para contas pré-pagas. |
spent_month | Créditos usados neste mês civil (UTC). |
spend_limit | Seu 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"
}
}
| HTTP | Código | Significado | O que fazer |
|---|---|---|---|
| 400 | bad_request | Parâ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á. |
| 400 | empty | A solicitação não tem imagem. | Envie o arquivo como corpo ou no campo image. |
| 400 | url_invalid | image_url não é uma URL http(s) na porta 80 ou 443, tem credenciais ou é maior que 2048 caracteres. | Corrija a URL. |
| 401 | unauthorized | Chave de API ausente, malformada ou revogada. | Verifique o cabeçalho Authorization: Bearer; crie uma nova chave se necessário. |
| 402 | no_credits | Sem créditos restantes na conta (também ao desbloquear uma pré-visualização). | Compre um pacote de créditos. Não tente novamente. |
| 402 | spend_limit | Seu limite mensal de gastos foi atingido. | Aumente ou remova o limite no menu da conta, ou aguarde o próximo mês. |
| 404 | not_found | Caminho desconhecido, ou um link de upload/resultado desconhecido, expirado ou com token errado. | Verifique a URL. |
| 405 | method_not_allowed | Método HTTP errado para este caminho. | Use o método desta referência. |
| 413 | too_large | O arquivo (ou a imagem em image_url) é maior que 10 MB. | Reduza ou recomprima a imagem. |
| 415 | unsupported_format | Não é um arquivo PNG, JPEG, WebP, BMP descomprimido, GIF ou TIFF (detectado pelo conteúdo). | Converta a imagem para PNG primeiro. |
| 422 | invalid_image | O arquivo está danificado, truncado ou não pode ser decodificado. | Reexporte a imagem. |
| 422 | too_many_pixels | Mais de 40 MP, ou a imagem precisa de muita memória. | Reduza a imagem. |
| 422 | url_blocked | image_url aponta para um endereço privado, local ou interno. | Use uma URL pública. Não tente novamente. |
| 422 | url_unreachable | A imagem em image_url não pôde ser baixada. | Verifique a URL, ou envie o arquivo. |
| 429 | rate_limited | Muitas solicitações para esta chave (ou muitas falhas de login do seu IP). | Aguarde Retry-After segundos e tente novamente. |
| 429 | daily_limit | A chave atingiu seu limite diário. | Aguarde até 00:00 UTC, ou use outra chave. |
| 429 | preview_limit | A conta fez 200 pré-visualizações (mode=preview) hoje. | Aguarde até 00:00 UTC (Retry-After), ou use mode=live. |
| 429 | test_limit | O limite diário de modo de teste da chave ou conta foi atingido. | Aguarde até 00:00 UTC (Retry-After), ou use mode=live. |
| 500 | internal | Algo 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. |
| 503 | busy | Todos os workers estão ocupados e a fila está cheia. | Tente novamente após Retry-After segundos (geralmente 5). |
| 503 | timeout | O processamento levou mais de 60 segundos. Você não é cobrado. | Tente uma imagem menor ou menos cores. |
| 503 | storage_full | O armazenamento de resultados está cheio. Você não é cobrado. | Tente novamente após Retry-After segundos, ou solicite um único formato. |
| 503 | url_unavailable | Entrada por URL está desativada ou temporariamente indisponível. | Envie o arquivo em vez disso. |
| 504 | url_timeout | O 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
| Limite | Padrão | Erro |
|---|---|---|
| Solicitações por chave | 60 por minuto, com rajadas de até 10 de uma vez | 429 rate_limited + Retry-After |
| Solicitações concorrentes por conta | 5 de uma vez (em processamento ou na fila); envie até 5 em paralelo | 429 rate_limited + Retry-After: 2, não cobrado |
| Imagens por chave por dia | 5.000, repetições gratuitas com outras opções incluídas (reinicia às 00:00 UTC) | 429 daily_limit + Retry-After |
| Autenticação falha por IP | 30 por minuto | 429 rate_limited + Retry-After |
Pré-visualizações (mode=preview) | 200 por conta por dia | 429 preview_limit + Retry-After |
| Chamadas em modo de teste | 100 por chave e 100 por conta por dia | 429 test_limit + Retry-After |
| Repetições gratuitas de uma imagem paga com outras opções | 20 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_limitedebusy(após o cabeçalhoRetry-After), einternaluma vez com um pequeno intervalo. Outros erros 4xx falharão novamente. Retry-Afteré um número inteiro de segundos, nunca uma data HTTP. Vem comrate_limited(o tempo até uma solicitação ser permitida novamente),busyetimeout(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_limitednão usa um token, então esperar porRetry-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
200eContent-Typefor o que você pediu (image/svg+xml,application/pdf, …, ouapplication/jsonpara vários formatos). Erros são JSON — nunca os salve como um.svg. - Respeite
Retry-After(segundos); tente novamente apenasrate_limited,busye, 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
Authorizationem vez de em?token=, e mantenha links de resultado fora de logs e análises. - Desenvolvimento: use
mode=testenquanto desenvolve (gratuito, com marca d'água, 100 chamadas por dia); mude paralivequando publicar. - Suporte: registre
X-Request-Idem 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 comretention_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 depreview_file()eupgrade(). - JavaScript / TypeScript:
npm install @shapesoda/sdk(npm).new Shapesoda(key).vectorizeFile("logo.png", { formats: ["svg","pdf"], preset: "print" }), além depreviewFile()eupgrade(). Funciona comimporterequire, Node 18+. - Go, PHP, Ruby, Java, C#: clientes gerados estão na pasta
clients/sdkdo 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:
- create_upload
retorna
upload_ide um segredoupload_url. - 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. - 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_urlpara o primeiro, o SVG inline quando tem menos de 60 KB, o tamanho da imagem,credits_chargedecredits_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.