Shapesoda
Vectoriza imágenes: PNG/JPG/WebP a SVG, PDF, EPS o DXF limpios, con vista previa antes/después y controles de color. Funciona sin cuenta.
Servidor MCP alojado
npx add-mcp 'https://shapesoda.com/mcp'Se instala en Claude Code, Codex, Cursor y más
Documentación
Inicio rápido
Tres pasos desde cero hasta tu primer SVG. La URL base es https://shapesoda.com; todas las solicitudes se realizan a través de HTTPS.
- Obtén una clave de API
Abre Claves de API en tu cuenta (inicia sesión con tu correo electrónico o Google si se te solicita) y elige Nueva clave. La clave comienza con
ssk_y se muestra solo una vez — cópiala en tu almacén de secretos. Las cuentas nuevas reciben un crédito gratuito para probar las cosas.export SHAPESODA_KEY="ssk_your_key_here" - Envía una imagen
Publica los bytes de la imagen sin procesar como cuerpo de la solicitud y pasa las opciones en la cadena de consulta:
O envía un formulariocurl -sS "https://shapesoda.com/v1/vectorize?colors=auto" \ -H "Authorization: Bearer $SHAPESODA_KEY" \ --data-binary @logo.png \ -D - -o logo.svgmultipart/form-datacon el archivo en el campoimagey las opciones como campos regulares: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 - Lee la respuesta
En caso de éxito, obtienes
200con el SVG como cuerpo (image/svg+xml).-D -imprime los encabezados, que te indican cuánto costó la solicitud: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_4f1c2a9e0b7d4c3a8e21Encabezado Significado X-Credits-ChargedCréditos que usó esta solicitud: 1para una vectorización exitosa (cualquiera que sea el número de formatos),0en modo de prueba y para repeticiones gratuitas. Las solicitudes fallidas no se cobran.X-Modelive, otesten modo de prueba.X-Credits-RemainingImágenes que aún puedes procesar en este momento (saldo más cualquier crédito incluido, limitado por tu límite de gasto). X-Request-IdID único de la solicitud. También se devuelve en los errores — inclúyelo cuando contactes al soporte.
Autenticación
Las solicitudes de cuenta y vectorización (POST /v1/vectorize, GET /v1/account, /mcp) deben llevar tu clave de API como token de portador:
Authorization: Bearer ssk_…
- Crea, lista y revoca claves en la página de Claves de API de tu cuenta (hasta 10 claves activas por cuenta). La revocación tiene efecto de inmediato.
- Una clave faltante, malformada o revocada devuelve
401 unauthorized. Los intentos fallidos repetidos desde una dirección IP están limitados en velocidad. - Mantén la clave en secreto y solo en tu servidor. La API no envía encabezados CORS, por lo que no está destinada a ser llamada desde una página web: nunca llames a la API desde un navegador o una aplicación móvil con tu clave secreta, y nunca la incluyas en código de front-end ni la confirmes en un repositorio. Si una clave se filtra, revócala.
- Los resultados almacenados usan una credencial diferente.
GETyDELETE /v1/results/{id}toman el token de resultado, no la clave de API. Envíalo comoAuthorization: Bearer <result_token>(recomendado).?token=en la URL también se acepta, pero un token en una URL puede filtrarse en registros de acceso, historial del navegador y análisis — no lo uses donde las URL se registren o compartan. Consulta Almacenamiento de resultados. - Los enlaces de carga de una sola vez del flujo de carga MCP llevan su propio secreto y no necesitan autenticación adicional.
GET /v1/planses público. - Todas las claves de una cuenta comparten un saldo de créditos — el mismo saldo que usas en el sitio web.
Ejemplos de código
Ejemplos mínimos y con pocas dependencias: vectoriza logo.png en logo.svg, obtén varios formatos con un ajuste preestablecido, muestra errores de la API, imprime los créditos restantes. Tu elección de idioma se recuerda en toda la página.
Vectorizar una imagen
# --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"))
}
Vectorizar con un ajuste preestablecido
El ajuste preestablecido print: un PDF y un SVG de 210 mm de ancho en una sola llamada, por 1 crédito. Varios formatos se devuelven como JSON, y cada archivo se descarga mediante su enlace con el 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)
}
Consulta tu 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")
}
Vectorizar una imagen
POST/v1/vectorize1 crédito por imagen exitosa
Convierte una imagen rasterizada en SVG, PDF, EPS, DXF o PNG — un formato o varios por 1 crédito. La solicitud es síncrona: la respuesta llega cuando el vector está listo, generalmente en unos pocos segundos (hasta aproximadamente un minuto para las imágenes más grandes).
Cuerpo de la solicitud
Envía la imagen de una de tres maneras:
- Bytes sin procesar — el archivo en sí es el cuerpo; los parámetros van en la cadena de consulta. El encabezado
Content-Typeno se usa. - Multipart —
Content-Type: multipart/form-datacon el archivo en el campoimage; los parámetros pueden ser campos de formulario o parámetros de consulta (los campos de formulario ganan). Cualquier otro campo de archivo se rechaza. - Por URL —
image_urlcon un cuerpo vacío (implementación gradual).
El formato se detecta a partir del contenido del archivo (su firma), no del nombre del archivo ni de Content-Type.
Parámetros
Todos los parámetros son opcionales. Los valores distinguen entre mayúsculas y minúsculas; los parámetros o valores desconocidos y las opciones conflictivas (consulta Combinaciones) devuelven 400 bad_request con el motivo.
Ajuste fino vs. ajustes preestablecidos. Un ajuste preestablecido solo establece valores predeterminados. Cada parámetro a continuación se aplica además de él y siempre gana: preset=cut&max_colors=4&stroke_width=0.2 es el ajuste preestablecido de corte con 4 colores. Sin un ajuste preestablecido, web es el punto de partida.
| Parámetro | Valores | Descripción |
|---|---|---|
| Conceptos básicos | ||
preset | web print cut laser embroidery | Ajustes predefinidos para el trabajo; por defecto web. Consulta Ajustes predefinidos. |
format | svg pdf eps dxf png | Formato de salida, o una lista separada por comas (svg,pdf) por 1 crédito. Por defecto: el formato del ajuste predefinido. Consulta Formatos de salida. |
image_type | auto artwork artwork_sharp photo | Tipo de imagen. artwork — logotipos, iconos, ilustraciones con bordes suaves (antialiasing); artwork_sharp — bordes de píxeles duros sin antialiasing (pixel art, capturas de pantalla); photo — fotografías e imágenes complejas. |
quality | auto high medium low | Calidad de la entrada. Usa low para imágenes JPEG borrosas, con ruido o muy comprimidas. |
detail | auto low medium high | Nivel de detalle en la salida. Se aplica a fotografías. |
mode | live test preview | test — resultado gratuito con marca de agua para crear una integración (Modo de prueba). preview — vista previa PNG gratuita de hasta 1 MP; los vectores se desbloquean después por 1 crédito (Vista previa). |
retain | false true | true — también almacena un resultado de un solo formato durante 24 horas. Consulta Almacenamiento de resultados. |
retention_days | 1 – 30 | Conserva el resultado almacenado esta cantidad de días en lugar de 24 horas; gratis. Consulta Retención. |
image_url | URL | Una dirección de imagen pública en lugar de un archivo. Consulta URL de imagen (implementación gradual). |
| Procesamiento | ||
colors | auto unlimited 2 – 32 | Número de colores (la opción original). Un entero limita la paleta (2 da un resultado de dos colores); unlimited conserva todos los colores que encuentre el motor. Por defecto auto. Para un límite más fino usa max_colors. |
max_colors | 2 – 256 | Número máximo de colores; el motor puede usar menos. No se combina con colors ni con palette. |
palette | 2–256 × #rrggbb | Colores fijos, separados por comas; el resultado usa solo estos. En la cadena de consulta escribe # como %23. No se combina con colors ni con max_colors. |
palette_tolerance | 0 – 100 | Con palette: qué tan lejos (CIE76 ΔE) puede estar un color de imagen de un color de paleta para ajustarse a él; los colores más lejanos conservan su propio valor. |
min_area | 0 – 10000 | Forma más pequeña conservada, en px²; las motas más pequeñas se descartan. Anula el valor del ajuste predefinido, también para fotografías. |
max_pixels | 10000 – 4200000 | Reduce la escala de la entrada a como máximo esta cantidad de píxeles antes del trazado: más rápido, menos nodos. El factor llega en X-Input-Scale. |
curve_tolerance | 0.01 – 10 | Tolerancia de ajuste de curvas en píxeles de origen (por defecto alrededor de 0.5). Un valor más bajo sigue el contorno más de cerca con más nodos; un valor más alto da menos nodos. También establece smoothness a menos que lo pases explícitamente. |
smoothness | 1 – 10 | Suavizado de contorno: un valor más alto es más suave con menos nodos. |
corners | sharp smooth | Mantener las esquinas afiladas o redondearlas. Por defecto, según el análisis automático. |
denoise | 0 – 3 | Reducción de ruido antes del trazado (artefactos JPEG, grano): 0 desactivado, 3 más fuerte. |
| Dibujo | ||
draw | fill stroke stroke_edges | fill — formas rellenas; stroke — contorno de cada forma, sin relleno (láser, grabado); stroke_edges — solo los bordes entre formas, cada uno una vez, para que un borde compartido no se corte dos veces. Por defecto, según el ajuste predefinido. |
layers | stacked cutouts cutouts_grouped | Formas apiladas unas sobre otras, recortadas unas de otras (sin superposiciones), o recortadas y agrupadas por color. Por defecto, según el ajuste predefinido. |
group_by | none color layer | Grupos en la salida: color — un grupo por color (las formas se recortan unas de otras); layer — por profundidad de anidamiento (layer_0 es el fondo). SVG , capas PDF, bloques EPS, capas DXF. Por defecto, SVG agrupa por color solo para cutouts_grouped, DXF siempre usa capas de color, PDF y EPS no tienen grupos. |
| Curvas y trazo | ||
curves | all lines lines_arcs | Segmentos permitidos: all — curvas y líneas; lines — solo segmentos rectos (para máquinas sin curvas); lines_arcs — DXF obtiene líneas y arcos, otros formatos igual que lines. |
stroke_width | 0.01 – 100 | Ancho de trazo; requiere draw=stroke o stroke_edges. En unidades de salida, o en píxeles de origen con stroke_scaling=scale. Por defecto: un hairline. |
stroke_color | #rrggbb | Color del trazo en lugar del color de cada forma. Mismo requisito que stroke_width. |
stroke_scaling | fixed scale | fixed (predeterminado) — el ancho permanece en unidades de salida cuando el dibujo se redimensiona; scale — el ancho está en píxeles de origen y se escala con el dibujo. |
| Relleno de huecos | ||
gap_filler | off on | on — una capa subyacente debajo de los rellenos: cada forma delineada en su propio color, para que no se vean huecos de luz entre vecinos. Requiere draw=fill. Predeterminado off. |
gap_filler_width | 0 – 10 | Ancho del contorno de la capa subyacente en píxeles de origen (predeterminado 1). Requiere gap_filler=on. |
| Tamaño | ||
width, height | número > 0 | Tamaño de salida en unit. Uno de ellos — el otro sigue la proporción de aspecto; ambos — una hoja de ese tamaño, el dibujo se coloca según aspect. Hasta 10 m. |
unit | px pt mm cm in | Unidad de width / height; predeterminado px. |
aspect | fit fill stretch | Con ambos width y height: fit (predeterminado) — todo el dibujo dentro de la hoja; fill — cubre la hoja, el exceso se recorta; stretch — escalado por eje al tamaño exacto. |
align_x, align_y | 0 – 1 | Colocación del dibujo en la hoja con aspect=fit o fill; predeterminado 0.5 (centrado). |
scale | >0 – 100 | Tamaño de salida como múltiplo del tamaño de origen. No se combina con width / height. |
dpi | 1 – 2400 | Píxeles por pulgada de la salida PNG cuando el tamaño está en pt, mm, cm o in; predeterminado 300. |
| Específico del formato | ||
svg_version | 1.1 1.0 tiny1.2 | Variante de SVG. tiny1.2 es el perfil SVG Tiny para plotters y visores embebidos: sin opacidad, sin transformaciones de degradado. |
svg_fixed_size | true false | true (predeterminado) — width y height en el elemento raíz; false — solo un viewBox, el SVG se escala a su contenedor. |
adobe_compat | false true | true — SVG compatible con Adobe Illustrator: tamaño siempre establecido, anchos de trazo en coordenadas de dibujo. |
dxf_curves | lines arcs splines | Curvas DXF como polilíneas (predeterminado), polilíneas con arcos o entidades SPLINE. curves=lines o lines_arcs gana. |
png_antialias | on off | Anti-aliasing en PNG; predeterminado on. |
Las opciones se verifican entre sí; un conflicto devuelve 400 bad_request:
paletteno se puede combinar concolorsnimax_colors, nimax_colorsconcolors.palette_tolerancenecesitapalette.stroke_width,stroke_color,stroke_scalingnecesitandraw=strokeostroke_edges(también cuando el preajuste lo establece, p. ej.laser).gap_filler=onnecesitadraw=fill;gap_filler_widthnecesitagap_filler=on.unitnecesitawidthoheight.scaleno se puede combinar conwidth/height.aspect,align_x,align_ynecesitan tantowidthcomoheight(la alineación no tiene efecto conaspect=stretch).dpinecesita un tamaño enpt,mm,cmoin.retention_daysfunciona solo conmode=livey no conretain=false.mode=previewdevuelvepngúnicamente.
Límites de entrada
| Límite | Valor | Error |
|---|---|---|
| Formatos | PNG, JPEG, WebP, BMP (sin comprimir, 24/32 bits), GIF (primer fotograma), TIFF (primera página) | 415 unsupported_format |
| Tamaño de archivo | 10 MB | 413 too_large |
| Resolución | Hasta 40 megapíxeles. Las imágenes de más de 4,2 MP o 4096 px por lado se reducen automáticamente; el factor aparece en X-Input-Scale | 422 too_many_pixels |
La reducción de escala no cambia el tamaño físico que solicitas con width / height. Las imágenes más grandes no producen mejores vectores: unos 2000 px en el lado largo dan los resultados más rápidos.
Respuesta
Lo que se devuelve depende de los formatos que solicites:
- Un formato (el predeterminado) —
200 OKcon el archivo como cuerpo y elContent-Typedel formato (consulta Formatos de salida). El SVG se sirveinline, otros formatos como adjuntovector.<format>. No se almacena nada a menos que pasesretain=trueoretention_days. - Varios formatos, o
Accept: application/json— JSON con un enlace de descarga por formato; el resultado se almacena durante 24 horas. Consulta Almacenamiento de resultados.
| Cabecera | Significado |
|---|---|
X-Credits-Charged, X-Credits-Remaining | Lo que costó la solicitud y lo que queda (respuestas de archivo; el JSON tiene lo mismo en campos). |
X-Mode | live, test o preview. |
X-Input-Scale | Solo cuando la entrada se redujo: el factor aplicado a cada lado, p. ej. 0.5. |
X-Result-Id, X-Result-Url, X-Result-Token, X-Result-Expires | Solo cuando el resultado se almacena (retain=true, retention_days, mode=preview): el resultado, su token secreto y cuándo se elimina. |
X-Request-Id | ID único de la solicitud. |
En cualquier error, el cuerpo es JSON — consulta Errores.
Preajustes
Un preajuste es un conjunto de opciones ya preparado para el trabajo. Pasa preset y ajústalo con opciones explícitas si es necesario — siempre ganan al preajuste. Sin preset obtienes web: exactamente el SVG que la API siempre ha devuelto.
| Preajuste | Formato predeterminado | Qué hace | Para qué usarlo |
|---|---|---|---|
web | SVG | Colores automáticos, formas apiladas unas sobre otras. | Sitios web, aplicaciones, Figma |
print | Formas recortadas entre sí, sin superposiciones. | Imprentas, carteles, documentos | |
cut | SVG | 8 colores; formas recortadas y agrupadas por color — un grupo por color de vinilo. | Cortadoras de vinilo, pegatinas, Cricut, Silhouette |
laser | SVG | 2 colores, solo contornos (draw=stroke). | Corte y grabado láser; añade format=dxf para software CAD |
embroidery | SVG | 12 colores, formas recortadas entre sí, motas diminutas eliminadas. | Digitalización de bordados |
Terminal · una llamada por preajuste
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
Los ajustes detrás de print, cut, laser y embroidery pueden afinarse con el tiempo; web se mantiene como está. La misma llamada en cinco idiomas: Vectorizar con un preajuste.
Formatos de salida
Elige el formato con format. Un valor devuelve el archivo en sí; una lista separada por comas (svg,pdf,png) devuelve JSON con un enlace de descarga por formato — todos por 1 crédito.
| Formato | Content-Type | Con width / height | Bueno para |
|---|---|---|---|
svg | image/svg+xml | Atributos width / height en la unidad, mismo viewBox | Web, Figma, Illustrator, Cricut Design Space |
pdf | application/pdf | Tamaño de página (MediaBox) | Impresión, documentos, Illustrator |
eps | application/postscript | BoundingBox | RIP de impresión, CorelDRAW, software de diseño antiguo |
dxf | application/dxf | Coordenadas en la unidad y $INSUNITS | CAD, software láser y CNC (las curvas se convierten en polilíneas) |
png | image/png | Píxeles: mm y in a 300 DPI, como máximo 4,2 MP | Una re-renderización limpia del vector a cualquier tamaño |
Terminal · una llamada 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
Tamaño físico: establece width o height junto con unit — px (predeterminado), pt, mm, cm o in. Con un lado, el otro sigue la proporción de aspecto; con ambos obtienes una hoja de exactamente ese tamaño y aspect (fit, fill, stretch) con align_x / align_y deciden cómo se coloca el dibujo sobre ella. El máximo es 10 m. Sin tamaño, la salida tiene el tamaño en píxeles de la entrada (después de cualquier reducción), multiplicado por scale si se indica. PNG obtiene píxeles: unidades físicas a dpi (predeterminado 300).
Modo de prueba
Construye y prueba tu integración gratis con mode=test. La respuesta tiene el mismo formato, cabeceras y estructura que una real, pero el resultado lleva una marca de agua: un patrón denso tejido en las propias formas, por lo que no se puede eliminar borrando una capa.
- No se necesitan créditos ni suscripción:
X-Credits-Charged: 0yX-Mode: test. - Los preajustes, formatos y opciones funcionan como en modo real, con dos excepciones:
retention_daysyimage_urlse rechazan con400 bad_request(prueba con un archivo y sinretention_days). - Los resultados de prueba se pueden almacenar (varios formatos,
retain=trueoAccept: application/json), pero solo durante 1 hora en lugar de 24, y permanecen con marca de agua en todos los formatos. Cuentan para el almacenamiento de la cuenta y se eliminan primero cuando el espacio se agota. - Límite: 100 llamadas de prueba por clave y 100 por cuenta por día UTC, luego
429 test_limitconRetry-Afterhasta las 00:00 UTC. Las llamadas de prueba no cuentan para el límite diario de imágenes.
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
Vista previa
Muestra a tus usuarios un resultado antes de que paguen. mode=preview es gratis y devuelve un PNG rasterizado de hasta 1 megapíxel (las imágenes más grandes se reducen) — sin vectores. Cuando el usuario esté satisfecho, desbloquea los vectores desde el mismo enlace por 1 crédito, sin volver a subir la imagen.
- Gratis, sin marca de agua:
X-Credits-Charged: 0yX-Mode: preview. Soloformat=png; no conretention_daysoimage_url. Todas las demás opciones se aplican, por lo que la vista previa muestra los colores y formas reales. - Límite: 200 vistas previas por cuenta por día UTC, luego
429 preview_limitconRetry-Afterhasta las 00:00 UTC. Las vistas previas no cuentan para el límite diario de imágenes ni para el límite del modo de prueba. - Una vista previa siempre se almacena durante 24 horas. El enlace viene en las cabeceras
X-Result-*(o como JSON conAccept: application/json). - Mejora:
GET /v1/results/{id}?format=svg(opdf,eps,dxf) con el token del resultado. Cobra 1 crédito una vez (0 si la misma imagen ya se pagó en los últimos 30 días, consulta Repeticiones gratuitas). Para el PNG a tamaño completo en lugar de la vista previa de 1 MP, añadeupgrade=true:?format=png&upgrade=true. Un?format=pngsimple devuelve la vista previa gratuita de nuevo. - Después de la mejora, el resultado es un resultado
livenormal (X-Mode: live): todos los demás formatos son gratuitos y se aplican las reglas de retención de Almacenamiento de resultados. Si la construcción falla, el crédito se devuelve y el resultado sigue siendo una vista previa. Sin créditos obtienes402 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
Repeticiones gratuitas
Pagas por una imagen una vez. Las repeticiones son gratuitas en dos casos, y la respuesta indica X-Credits-Charged: 0:
| Repetición | Gratis durante | Límite |
|---|---|---|
| Exactamente la misma solicitud: mismos bytes de imagen, mismos parámetros | 24 horas | Ninguno — reintentos seguros |
| La misma imagen con otras opciones o formatos | 30 días después de haberse pagado | 20 por imagen por día; más allá, una repetición cuesta 1 crédito |
- La imagen se reconoce por un hash SHA-256 de sus bytes, el mismo que usa el sitio web: una imagen pagada con un crédito en shapesoda.com se puede rehacer a través de la API gratis, y viceversa. Un archivo redimensionado o re-guardado es una imagen nueva.
- Las repeticiones con otras opciones cuentan para el límite diario de imágenes de la clave.
- Las llamadas en modo de prueba y las imágenes hechas con la suscripción ilimitada del sitio web no inician la ventana de 30 días.
- Las vistas previas (
mode=preview) son gratuitas y no inician la ventana de 30 días; desbloquear los vectores desde una vista previa es una llamada de pago ordinaria (1 crédito, o una repetición gratuita si la imagen se pagó en los últimos 30 días).
Almacenamiento de resultados
GET/v1/results/{id}gratis · token de resultado
DELETE/v1/results/{id}token de resultado
Por defecto no se almacena nada: una solicitud de un solo formato devuelve el archivo y olvida la imagen. Un resultado se conserva durante 24 horas solo cuando lo necesitas más tarde:
- solicitas varios formatos, o envías
Accept: application/json— exactamente ese valor; los clientes que envíanapplication/json, */*siguen recibiendo el archivo; - pasas
retain=trueoretention_days— sigues recibiendo el archivo, y el enlace viene en las cabecerasX-Result-*; - usas
mode=preview(siempre almacenado, consulta Vista previa); - la llamada llega a travé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 | El resultado almacenado y su dirección. |
result_token | Token secreto para descargar; se muestra una vez. También es la clave de descifrado — no podemos recuperarlo. |
files | Un enlace de descarga por formato solicitado. |
expires_at | Cuándo se elimina el resultado (24 horas, o retention_days días). |
width, height, input_scale | Tamaño en píxeles de la imagen vectorizada y el factor de reducción (1 si no se redujo). |
credits_charged, credits_remaining, mode | Lo mismo que las cabeceras X-Credits-* y X-Mode de una respuesta de archivo. |
Descarga con el token de resultado, no con tu clave API: Authorization: Bearer <result_token> (recomendado). ?token=<result_token> también se acepta, pero un token en una URL puede filtrarse en registros de acceso, historial del navegador y análisis. Cualquiera que tenga el token puede descargar el resultado, así que trata el enlace como el propio archivo. Sin format obtienes el primer formato de la solicitud original: el primero en tu lista format, en el orden en que lo escribiste (los duplicados se ignoran), o el formato del preajuste si no pasaste format. El objeto files de la respuesta JSON lista los formatos en el mismo orden. Un formato que no solicitaste se construye una vez desde la fuente almacenada con exactamente el mismo preajuste, opciones y modo — gratis y sin una nueva subida. La construcción está limitada por la tasa de solicitudes de la cuenta que posee el resultado (60 por minuto, 429 rate_limited con Retry-After); los formatos ya almacenados no lo están.
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"
Con retain=true (o retention_days) y un formato, el archivo viene como de costumbre más el enlace:
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
- Cifrado. La imagen fuente y cada formato están cifrados con AES-256-GCM bajo una clave derivada del token. Solo conservamos un hash del token, por lo que un resultado almacenado no puede leerse sin el enlace.
- Eliminado después de 24 horas (las vistas previas también), los resultados en modo de prueba después de 1 hora, los resultados con
retention_daysdespués de esa cantidad de días (verexpires_at).DELETElo elimina de inmediato y devuelve204. - Un id desconocido, un token incorrecto y un resultado caducado devuelven
404 not_found. - Si el almacenamiento está lleno, las solicitudes JSON reciben
503 storage_full(sin cargo; reintente después deRetry-Aftero solicite un solo formato), yretain=trueoretention_daysdevuelven el archivo sin los encabezadosX-Result-*.
Retención
Por defecto, un resultado almacenado dura 24 horas. Si sus usuarios vuelven por sus archivos, solicite más tiempo con retention_days — de 1 a 30 días, sin costo. Almacena el resultado incluso para un solo formato (como lo hace retain=true) y devuelve el enlace en los encabezados X-Result-*; expires_at / X-Result-Expires muestran la fecha límite.
- Solo para
mode=live, y no junto conretain=false. Los resultados de prueba duran 1 hora, las vistas previas 24 horas. - Los resultados almacenados cuentan para la cuota de almacenamiento de la cuenta. Cuando el espacio se agota, los resultados más antiguos se eliminan primero en este orden: resultados de prueba, vistas previas, resultados en vivo sin
retention_days, y por último los resultados conretention_days. Si el almacenamiento sigue lleno, recibe503 storage_full(sin cargo). - Los formatos adicionales creados a partir de un resultado almacenado son gratuitos durante todo el período.
DELETEelimina un resultado en cualquier momento.
Terminal · conservar durante 30 días
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 imagen Implementación gradual
Implementación gradual. La entrada por URL se está activando gradualmente. Hasta que esté activa, una solicitud con image_url recibe 503 url_unavailable — suba el archivo en su lugar.
En lugar de un archivo, pase la dirección de una imagen pública en image_url (parámetro de consulta o campo de formulario) y deje el cuerpo vacío. Todo lo demás funciona igual que con un archivo.
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
httpohttps, puerto 80 o 443, sin nombre de usuario ni contraseña, hasta 2048 caracteres — de lo contrario400 url_invalid.- La imagen se descarga a través de un proxy de salida aislado: las direcciones privadas, de bucle local, de enlace local y de metadatos de nube están bloqueadas, también después de redirecciones (
422 url_blocked). - Máximo 3 redirecciones, 10 segundos (
504 url_timeout) y 10 MB (413 too_large). Un servidor que no responde o devuelve un error da422 url_unreachable. - Envíe un archivo o
image_url, no ambos. No disponible en modo de prueba.
Cuenta y créditos
GET/v1/accountfree
Devuelve la cuenta a la que pertenece la clave y sus créditos. Los créditos se cuentan por mes calendario en 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 | Imágenes que puede procesar ahora: quota_left + balance, limitado por lo que queda de su límite de gasto. |
balance | Créditos comprados, compartidos con el sitio web. Incluye subscription_balance. |
subscription_balance | Créditos de su plan de API (se usan primero). Los no utilizados se transfieren hasta 5× su cantidad mensual y caducan cuando termina el plan. |
quota, quota_used, quota_left | Créditos mensuales incluidos, si su plan los tiene (se usan primero). 0 para cuentas de pago por uso. |
spent_month | Créditos usados este mes calendario (UTC). |
spend_limit | Su propio límite mensual en créditos, o null para sin límite. Configúrelo en el menú de la cuenta. |
Lista de precios
GET/v1/planspúblico · sin clave
Los mismos precios que en Precios, como JSON, sin autenticación. Devuelve api_plans (planes de crédito mensuales con credits, price y per_image), subscriptions (planes del sitio web), credits_never_expire, redownload_free_days, result_retention_hours, signup_credits, enterprise_contact y payments_enabled / billing (datos de pago cuando los pagos están activos para el visitante).
Errores
Los errores usan códigos de estado HTTP estándar y un cuerpo JSON con un code estable y legible por máquina. Ramifique según code, no según message: el mensaje es una pista legible por humanos y puede cambiar o localizarse.
{
"error": {
"code": "too_many_pixels",
"message": "Human-readable description",
"request_id": "req_4f1c2a9e0b7d4c3a8e21"
}
}
| HTTP | Código | Significado | Qué hacer |
|---|---|---|---|
| 400 | bad_request | Parámetro o valor desconocido, opciones conflictivas, formulario multiparte malformado, un archivo en un campo distinto de image, o tanto un archivo como image_url. | Corrija la solicitud. No reintente tal cual. |
| 400 | empty | La solicitud no tiene imagen. | Envíe el archivo como cuerpo o en el campo image. |
| 400 | url_invalid | image_url no es una URL http(s) en el puerto 80 o 443, tiene credenciales o es más larga de 2048 caracteres. | Corrija la URL. |
| 401 | unauthorized | Clave de API faltante, malformada o revocada. | Verifique el encabezado Authorization: Bearer; cree una nueva clave si es necesario. |
| 402 | no_credits | No quedan créditos en la cuenta (también al desbloquear una vista previa). | Compre un paquete de créditos. No reintente. |
| 402 | spend_limit | Se alcanzó su límite de gasto mensual. | Aumente o elimine el límite en el menú de la cuenta, o espere al próximo mes. |
| 404 | not_found | Ruta desconocida, o un enlace de subida/resultado desconocido, caducado o con token incorrecto. | Verifique la URL. |
| 405 | method_not_allowed | Método HTTP incorrecto para esta ruta. | Use el método de esta referencia. |
| 413 | too_large | El archivo (o la imagen en image_url) es más grande de 10 MB. | Reduzca la escala o vuelva a comprimir la imagen. |
| 415 | unsupported_format | No es un archivo PNG, JPEG, WebP, BMP sin comprimir, GIF o TIFF (detectado por contenido). | Convierta la imagen a PNG primero. |
| 422 | invalid_image | El archivo está dañado, truncado o no se puede decodificar. | Vuelva a exportar la imagen. |
| 422 | too_many_pixels | Más de 40 MP, o la imagen necesita demasiada memoria. | Reduzca la escala de la imagen. |
| 422 | url_blocked | image_url apunta a una dirección privada, local o interna. | Use una URL pública. No reintente. |
| 422 | url_unreachable | La imagen en image_url no se pudo descargar. | Verifique la URL o suba el archivo. |
| 429 | rate_limited | Demasiadas solicitudes para esta clave (o demasiados inicios de sesión fallidos desde su IP). | Espere Retry-After segundos y luego reintente. |
| 429 | daily_limit | La clave alcanzó su límite diario. | Espere hasta las 00:00 UTC o use otra clave. |
| 429 | preview_limit | La cuenta hizo 200 vistas previas (mode=preview) hoy. | Espere hasta las 00:00 UTC (Retry-After) o use mode=live. |
| 429 | test_limit | Se alcanzó el límite diario de modo de prueba de la clave o la cuenta. | Espere hasta las 00:00 UTC (Retry-After) o use mode=live. |
| 500 | internal | Algo salió mal en nuestro lado. No se le cobra. | Reintente una vez después de una pausa breve; si persiste, envíenos por correo el request_id. |
| 503 | busy | Todos los trabajadores están ocupados y la cola está llena. | Reintente después de Retry-After segundos (generalmente 5). |
| 503 | timeout | El procesamiento tardó más de 60 segundos. No se le cobra. | Pruebe con una imagen más pequeña o menos colores. |
| 503 | storage_full | El almacenamiento de resultados está lleno. No se le cobra. | Reintente después de Retry-After segundos o solicite un solo formato. |
| 503 | url_unavailable | La entrada por URL está desactivada o temporalmente no disponible. | Suba el archivo en su lugar. |
| 504 | url_timeout | La descarga de image_url tardó más de 10 segundos. No se le cobra. | Suba el archivo en su lugar. |
Los créditos se reservan cuando comienza el procesamiento y se reembolsan automáticamente si falla, por lo que solo paga por los resultados que recibe.
Límites de velocidad
| Límite | Predeterminado | Error |
|---|---|---|
| Solicitudes por clave | 60 por minuto, con ráfagas de hasta 10 a la vez | 429 rate_limited + Retry-After |
| Solicitudes concurrentes por cuenta | 5 a la vez (en procesamiento o en cola); envíe hasta 5 en paralelo | 429 rate_limited + Retry-After: 2, sin cargo |
| Imágenes por clave por día | 5,000, repeticiones gratuitas con otras opciones incluidas (se restablece a las 00:00 UTC) | 429 daily_limit + Retry-After |
| Autenticación fallida por IP | 30 por minuto | 429 rate_limited + Retry-After |
Vistas previas (mode=preview) | 200 por cuenta por día | 429 preview_limit + Retry-After |
| Llamadas en modo de prueba | 100 por clave y 100 por cuenta por día | 429 test_limit + Retry-After |
| Repeticiones gratuitas de una imagen pagada con otras opciones | 20 por imagen por día, luego 1 crédito cada una | — |
El presupuesto por clave se comparte entre la API REST y el servidor MCP. GET /v1/account no cuenta contra él. ¿Necesita más? Envíe un correo a [email protected].
Reintentos e idempotencia
- Reintente solo
rate_limitedybusy(después del encabezadoRetry-After), yinternaluna vez con una breve espera. Otros errores 4xx fallarán de nuevo. Retry-Afteres un número entero de segundos, nunca una fecha HTTP. Viene conrate_limited(el tiempo hasta que se permita una solicitud nuevamente),busyytimeout(5),storage_full(60) y los límites diarios (los segundos restantes hasta las 00:00 UTC).- Una solicitud rechazada no cuesta nada contra el límite. El límite por minuto es un cubo de tokens: una solicitud respondida con
429 rate_limitedno usa un token, por lo que esperarRetry-Afteres suficiente. - Los reintentos son gratuitos: repetir la misma solicitud (mismos bytes de imagen y mismos parámetros) dentro de 24 horas no se cobra de nuevo — la respuesta tiene
X-Credits-Charged: 0. La misma imagen con otras opciones es gratuita durante 30 días también, dentro de un límite diario — ver Repeticiones gratuitas. - Use un tiempo de espera de cliente generoso — al menos 120 segundos — para que una solicitud lenta no se abandone después de que la imagen ya se haya procesado y cobrado.
- Para lotes, ejecute unas pocas solicitudes en paralelo (2–4) en lugar de docenas: las solicitudes más allá de la ráfaga se rechazan con
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})\`);
}
Lista de verificación de producción
- Tiempo de espera: configure el tiempo de espera del cliente a al menos 120 segundos.
- Verifique antes de guardar: escriba el cuerpo en un archivo solo cuando el estado sea
200yContent-Typesea lo que solicitó (image/svg+xml,application/pdf, …, oapplication/jsonpara varios formatos). Los errores son JSON — nunca los guarde como un.svg. - Respete
Retry-After(segundos); reintente solorate_limited,busyy, una vez,internal. Ver Reintentos. - Mantenga la clave de API en secreto y solo en su servidor: no en un navegador, una aplicación móvil o un repositorio. Revoque una clave filtrada.
- Tokens de resultado: envíelos en el encabezado
Authorizationen lugar de en?token=, y mantenga los enlaces de resultados fuera de registros y análisis. - Desarrollo: use
mode=testmientras construye (gratis, con marca de agua, 100 llamadas al día); cambie alivecuando publique. - Soporte: registre
X-Request-Idcon cada fallo.
Seguridad y privacidad
- Las claves se almacenan con hash. Guardamos únicamente un hash con clave (HMAC-SHA-256) y un prefijo corto de cada clave, por lo que la clave se muestra una vez y no se puede recuperar; solo se puede revocar y reemplazar.
- Tú estableces el límite de gasto. Un tope mensual en el menú de la cuenta se aplica a cada clave, al servidor MCP y al sitio web: ni un agente ni una clave filtrada pueden gastar más de lo que permitas.
- Las imágenes se almacenan solo cuando las necesitas. Una solicitud de un solo formato se procesa en memoria en un proceso de trabajo aislado y se descarta; no se escribe nada en disco ni en una base de datos. Varios formatos,
retain=true,retention_days, las vistas previas y los resultados MCP se conservan durante 24 horas (hasta 30 días conretention_days), cifrados con una clave que solo posee el titular del token de resultado, y luego se eliminan; cualquier persona con el enlace y el token puede descargarlos. Las subidas MCP permanecen en memoria durante un máximo de 10 minutos. Las imágenes nunca se utilizan para entrenar modelos. Consulta Almacenamiento de resultados y la Política de privacidad. - La salida SVG está saneada. Cada resultado se verifica contra una lista blanca estricta (trazados, grupos y degradados con valores de color simples) antes de devolverse: sin scripts, manejadores de eventos, referencias externas ni contenido incrustado. PDF, EPS y DXF se generan únicamente a partir de la geometría vectorial y los colores.
- Las entradas se verifican. Los formatos se detectan por contenido, la resolución se comprueba antes de decodificar (protección contra bombas de descompresión) y cada trabajo se ejecuta en su propio proceso con un límite de tiempo. Las imágenes por URL se descargan en un proceso separado a través de un proxy de salida que bloquea direcciones internas.
SDKs y CLI
Las bibliotecas de cliente oficiales envuelven la misma API con opciones tipadas, reintentos automáticos después de 429 / 503 y descargas de resultados. Se generan a partir de la especificación OpenAPI, por lo que cada parámetro de esta página está disponible.
- Python:
pip install shapesoda(PyPI).Shapesoda(key).vectorize_file("logo.png", formats=["svg","pdf"], preset="print"), además depreview_file()yupgrade(). - JavaScript / TypeScript:
npm install @shapesoda/sdk(npm).new Shapesoda(key).vectorizeFile("logo.png", { formats: ["svg","pdf"], preset: "print" }), además depreviewFile()yupgrade(). Funciona conimportyrequire, Node 18+. - Go, PHP, Ruby, Java, C#: los clientes generados están en la carpeta
clients/sdkdel repositorio; la publicación en pkg.go.dev, Packagist, RubyGems, Maven Central y NuGet está en curso. Hasta entonces, genera uno a partir de la especificación (ver más abajo) o usa los ejemplos HTTP simples anteriores. - CLI:
npx shapesoda ./in ./out --preset cut --format svg,dxf(npm, Node 18+). Carpeta de entrada, carpeta de salida, reintentos y omisión de lo ya hecho integrados;--preview,--test,shapesoda account.
Especificación OpenAPI
Una descripción OpenAPI 3.1 legible por máquina de la API: impórtala en Postman, Insomnia o Bruno, o genera un 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 aplicaciones y agentes de IA
El Protocolo de Contexto de Modelo permite que las aplicaciones y agentes de IA llamen a herramientas externas. El servidor MCP de Shapesoda convierte imágenes en SVG, PDF, EPS, DXF o PNG directamente en ChatGPT, Claude, Cursor, Claude Code y cualquier otro cliente MCP. Una cuenta de Shapesoda se conecta con un inicio de sesión (OAuth); los agentes y scripts aún pueden usar una clave de API con los mismos créditos y límites que la API REST.
POSThttps://shapesoda.com/mcpStreamable HTTP · OAuth o clave Bearer
Gratis: 3 vectorizaciones al día sin cuenta, 10 al día con una cuenta conectada (OAuth o clave), luego créditos. REST y el sitio web no se ven afectados. Un plan personal desde el sitio web es ilimitado en chats cuando la cuenta está conectada con un inicio de sesión; las llamadas con una clave de API usan créditos. Cambiar la configuración de la misma imagen en la ventana de Shapesoda es gratis durante 24 horas, por lo que una imagen se cobra una sola vez. El modo de prueba, las vistas previas, retention_days y vectorize_url requieren una cuenta. Los límites se restablecen a las 00:00 UTC; get_usage y free_remaining_today en cada resultado muestran lo que queda. No se vende nada dentro de los chats: los precios y el pago están en el sitio web.
Conectar en ChatGPT, Claude, Cursor o Claude Code
URL del servidor: https://shapesoda.com/mcp. Cuando las imágenes gratuitas se agotan, el chat ofrece conectar tu cuenta de Shapesoda: inicia sesión con Google o un código de correo electrónico y presiona Permitir. Las aplicaciones conectadas se enumeran en tu cuenta, donde puedes desconectarlas.
ChatGPT
Encuentra Shapesoda en el directorio de aplicaciones de ChatGPT, o agrega la URL del servidor como una aplicación personalizada en modo desarrollador. Adjunta una imagen y escribe @Shapesoda o "convierte esto en SVG": ChatGPT pregunta una vez para compartir el archivo (la entrada image de vectorize), y la ventana de Shapesoda muestra un control deslizante de antes/después, configuración, descargas en cada formato y Copiar SVG.
Claude
En claude.ai o Claude Desktop abre Configuración → Conectores y agrega Shapesoda desde el directorio, o "Agregar conector personalizado" con la URL del servidor. Claude no pasa archivos adjuntos a las aplicaciones, así que pídele que vectorice una imagen: llama a open_editor, y tú eliges el archivo en la ventana de Shapesoda.
Claude Code
Agrega el servidor, luego ejecuta /mcp, elige shapesoda y Autenticar para conectar tu cuenta en el navegador:
claude mcp add --transport http shapesoda https://shapesoda.com/mcp
O usa una clave de API en lugar del inicio de sesión:
claude mcp add --transport http shapesoda https://shapesoda.com/mcp \
--header "Authorization: Bearer $SHAPESODA_KEY"
Cursor
Instala el plugin de Shapesoda desde el mercado de Cursor, o agrega el servidor a ~/.cursor/mcp.json (o .cursor/mcp.json en un proyecto). Cursor ofrece conectar la cuenta en el primer uso.
{
"mcpServers": {
"shapesoda": { "url": "https://shapesoda.com/mcp" }
}
}
Con una clave de API, Cursor lee ${env:SHAPESODA_KEY} de tu entorno, por lo que la clave permanece fuera del archivo:
{
"mcpServers": {
"shapesoda": {
"url": "https://shapesoda.com/mcp",
"headers": { "Authorization": "Bearer ${env:SHAPESODA_KEY}" }
}
}
}
Otros clientes que admiten servidores MCP remotos (Streamable HTTP) funcionan de la misma manera: URL https://shapesoda.com/mcp, inicio de sesión OAuth 2.1 (descubierto desde /.well-known/oauth-protected-resource) o el encabezado Authorization: Bearer <key>. El servidor responde con JSON (sin flujo SSE) y admite las versiones de protocolo 2025-11-25, 2025-06-18 y 2025-03-26.
Herramientas
vectorize
Convierte una imagen en SVG, PDF, EPS, DXF o PNG. La imagen llega exactamente de una manera: image (un archivo adjunto al mensaje de ChatGPT), upload_id (desde create_upload) o un pequeño image_base64. Acepta las mismas opciones que la API REST: preset, format como lista, draw, layers, palette, transparency, width / height / unit, mode=test, mode=preview (vista previa PNG gratuita; abre su enlace con format=svg en lugar de format=png para desbloquear los vectores por 1 crédito), retention_days y el resto: consulta Parámetros. Devuelve un enlace de descarga por formato, válido durante 24 horas, y muestra la ventana de Shapesoda en los chats que lo admiten. Usa 1 crédito para todos los formatos.
open_editor
Abre la ventana de Shapesoda sin una imagen: el usuario elige el archivo allí. Para Claude, que no pasa adjuntos a las aplicaciones. No usa créditos.
create_upload
Crea un enlace de subida de un solo uso para un archivo local (válido 10 minutos, hasta 10 MB) y devuelve el comando curl para subirlo. Para agentes con terminal.
revectorize
Usada solo por la ventana de Shapesoda (oculta del modelo): vectoriza la misma imagen nuevamente con otra configuración, gratis durante 24 horas después de la primera vectorización, hasta 30 cambios al día.
vectorize_url
Descarga una imagen pública por URL y la vectoriza como vectorize. Se lista solo cuando la entrada por URL está habilitada (implementación gradual); sin modo de prueba.
get_usage
Muestra las vectorizaciones gratuitas restantes hoy, créditos disponibles, saldo, uso mensual y tu límite de gasto. Gratis.
Cómo un agente envía un archivo
Los agentes generalmente no pueden adjuntar archivos binarios a una llamada de herramienta, por lo que las imágenes grandes pasan por un enlace de subida de corta duración:
- create_upload
devuelve
upload_idy un secretoupload_url. - Subir con curl
El agente ejecuta el comando devuelto:
curl -sS -X PUT --data-binary @image.png "<upload_url>". El enlace acepta un archivo PNG, JPEG, WebP, BMP, GIF o TIFF y no necesita otra autenticación. - vectorize con upload_id
El resultado tiene un enlace por formato solicitado en
files(válido 24 horas; el token está en el enlace),download_urlpara el primero, el SVG en línea cuando tiene menos de 60 KB, el tamaño de la imagen,credits_chargedycredits_remaining.
Para imágenes pequeñas (hasta 2 MB), el agente puede omitir la subida y pasar el archivo directamente como image_base64.
El enlace de subida es servido por PUT /v1/uploads/{upload_id}. Es parte del flujo de subida MCP y está listado en la especificación OpenAPI por completitud; no lo llamas tú mismo en una integración REST.
Precios
Suscripción mensual con la cantidad de créditos que elijas. Un crédito es una imagen vectorizada con éxito, en cada formato que solicites en esa llamada, a través de la API, MCP o el sitio web. Cancela cuando quieras.
- Los créditos no utilizados se transfieren (hasta 5 meses de cantidad)
- Cancela cuando quieras; los créditos duran hasta que termine el plan
- Saldo compartido con el sitio web
- Las solicitudes fallidas son gratuitas
50
créditos / mes
$3.99 / mes
$0.08 por imagen
200
créditos / mes
$9.99 / mes
$0.05 por imagen
5,000
créditos / mes
$74.99 / mes
$0.015 por imagen
20,000
créditos / mes
$199.99 / mes
$0.01 por imagen
100,000
créditos / mes
$499.99 / mes
$0.005 por imagen
¿Empresa o más de 100k imágenes al mes? Envía un correo a [email protected] para precios por volumen y límites más altos.