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.

  1. 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"
    
  2. 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:
    curl -sS "https://shapesoda.com/v1/vectorize?colors=auto" \
      -H "Authorization: Bearer $SHAPESODA_KEY" \
      --data-binary @logo.png \
      -D - -o logo.svg
    
    O envía un formulario multipart/form-data con el archivo en el campo image y 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
    
  3. Lee la respuesta En caso de éxito, obtienes 200 con 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_4f1c2a9e0b7d4c3a8e21
    
    EncabezadoSignificado
    X-Credits-ChargedCréditos que usó esta solicitud: 1 para una vectorización exitosa (cualquiera que sea el número de formatos), 0 en modo de prueba y para repeticiones gratuitas. Las solicitudes fallidas no se cobran.
    X-Modelive, o test en 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. GET y DELETE /v1/results/{id} toman el token de resultado, no la clave de API. Envíalo como Authorization: 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/plans es 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-Type no se usa.
  • Multipart — Content-Type: multipart/form-data con el archivo en el campo image; 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_url con 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ámetroValoresDescripción
Conceptos básicos
presetweb print cut laser embroideryAjustes predefinidos para el trabajo; por defecto web. Consulta Ajustes predefinidos.
formatsvg pdf eps dxf pngFormato 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_typeauto artwork artwork_sharp photoTipo 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.
qualityauto high medium lowCalidad de la entrada. Usa low para imágenes JPEG borrosas, con ruido o muy comprimidas.
detailauto low medium highNivel de detalle en la salida. Se aplica a fotografías.
modelive test previewtest — 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).
retainfalse truetrue — también almacena un resultado de un solo formato durante 24 horas. Consulta Almacenamiento de resultados.
retention_days1 – 30Conserva el resultado almacenado esta cantidad de días en lugar de 24 horas; gratis. Consulta Retención.
image_urlURLUna dirección de imagen pública en lugar de un archivo. Consulta URL de imagen (implementación gradual).
Procesamiento
colorsauto unlimited 2 – 32Nú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_colors2 – 256Número máximo de colores; el motor puede usar menos. No se combina con colors ni con palette.
palette2–256 × #rrggbbColores 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_tolerance0 – 100Con 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_area0 – 10000Forma 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_pixels10000 – 4200000Reduce 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_tolerance0.01 – 10Tolerancia 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.
smoothness1 – 10Suavizado de contorno: un valor más alto es más suave con menos nodos.
cornerssharp smoothMantener las esquinas afiladas o redondearlas. Por defecto, según el análisis automático.
denoise0 – 3Reducción de ruido antes del trazado (artefactos JPEG, grano): 0 desactivado, 3 más fuerte.
Dibujo
drawfill stroke stroke_edgesfill — 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.
layersstacked cutouts cutouts_groupedFormas apiladas unas sobre otras, recortadas unas de otras (sin superposiciones), o recortadas y agrupadas por color. Por defecto, según el ajuste predefinido.
group_bynone color layerGrupos 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
curvesall lines lines_arcsSegmentos 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_width0.01 – 100Ancho 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#rrggbbColor del trazo en lugar del color de cada forma. Mismo requisito que stroke_width.
stroke_scalingfixed scalefixed (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_filleroff onon — 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_width0 – 10Ancho del contorno de la capa subyacente en píxeles de origen (predeterminado 1). Requiere gap_filler=on.
Tamaño
width, heightnúmero > 0Tamañ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.
unitpx pt mm cm inUnidad de width / height; predeterminado px.
aspectfit fill stretchCon 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_y0 – 1Colocación del dibujo en la hoja con aspect=fit o fill; predeterminado 0.5 (centrado).
scale>0 – 100Tamaño de salida como múltiplo del tamaño de origen. No se combina con width / height.
dpi1 – 2400Pí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_version1.1 1.0 tiny1.2Variante de SVG. tiny1.2 es el perfil SVG Tiny para plotters y visores embebidos: sin opacidad, sin transformaciones de degradado.
svg_fixed_sizetrue falsetrue (predeterminado) — width y height en el elemento raíz; false — solo un viewBox, el SVG se escala a su contenedor.
adobe_compatfalse truetrue — SVG compatible con Adobe Illustrator: tamaño siempre establecido, anchos de trazo en coordenadas de dibujo.
dxf_curveslines arcs splinesCurvas DXF como polilíneas (predeterminado), polilíneas con arcos o entidades SPLINE. curves=lines o lines_arcs gana.
png_antialiason offAnti-aliasing en PNG; predeterminado on.

Las opciones se verifican entre sí; un conflicto devuelve 400 bad_request:

  • palette no se puede combinar con colors ni max_colors, ni max_colors con colors. palette_tolerance necesita palette.
  • stroke_width, stroke_color, stroke_scaling necesitan draw=stroke o stroke_edges (también cuando el preajuste lo establece, p. ej. laser). gap_filler=on necesita draw=fill; gap_filler_width necesita gap_filler=on.
  • unit necesita width o height. scale no se puede combinar con width / height. aspect, align_x, align_y necesitan tanto width como height (la alineación no tiene efecto con aspect=stretch). dpi necesita un tamaño en pt, mm, cm o in.
  • retention_days funciona solo con mode=live y no con retain=false. mode=preview devuelve png únicamente.

Límites de entrada

LímiteValorError
FormatosPNG, JPEG, WebP, BMP (sin comprimir, 24/32 bits), GIF (primer fotograma), TIFF (primera página)415 unsupported_format
Tamaño de archivo10 MB413 too_large
ResoluciónHasta 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-Scale422 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 OK con el archivo como cuerpo y el Content-Type del formato (consulta Formatos de salida). El SVG se sirve inline, otros formatos como adjunto vector.<format>. No se almacena nada a menos que pases retain=true o retention_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.
CabeceraSignificado
X-Credits-Charged, X-Credits-RemainingLo que costó la solicitud y lo que queda (respuestas de archivo; el JSON tiene lo mismo en campos).
X-Modelive, test o preview.
X-Input-ScaleSolo 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-ExpiresSolo cuando el resultado se almacena (retain=true, retention_days, mode=preview): el resultado, su token secreto y cuándo se elimina.
X-Request-IdID ú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.

PreajusteFormato predeterminadoQué hacePara qué usarlo
webSVGColores automáticos, formas apiladas unas sobre otras.Sitios web, aplicaciones, Figma
printPDFFormas recortadas entre sí, sin superposiciones.Imprentas, carteles, documentos
cutSVG8 colores; formas recortadas y agrupadas por color — un grupo por color de vinilo.Cortadoras de vinilo, pegatinas, Cricut, Silhouette
laserSVG2 colores, solo contornos (draw=stroke).Corte y grabado láser; añade format=dxf para software CAD
embroiderySVG12 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.

FormatoContent-TypeCon width / heightBueno para
svgimage/svg+xmlAtributos width / height en la unidad, mismo viewBoxWeb, Figma, Illustrator, Cricut Design Space
pdfapplication/pdfTamaño de página (MediaBox)Impresión, documentos, Illustrator
epsapplication/postscriptBoundingBoxRIP de impresión, CorelDRAW, software de diseño antiguo
dxfapplication/dxfCoordenadas en la unidad y $INSUNITSCAD, software láser y CNC (las curvas se convierten en polilíneas)
pngimage/pngPíxeles: mm y in a 300 DPI, como máximo 4,2 MPUna 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: 0 y X-Mode: test.
  • Los preajustes, formatos y opciones funcionan como en modo real, con dos excepciones: retention_days y image_url se rechazan con 400 bad_request (prueba con un archivo y sin retention_days).
  • Los resultados de prueba se pueden almacenar (varios formatos, retain=true o Accept: 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_limit con Retry-After hasta 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: 0 y X-Mode: preview. Solo format=png; no con retention_days o image_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_limit con Retry-After hasta 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 con Accept: application/json).
  • Mejora: GET /v1/results/{id}?format=svg (o pdf, 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ñade upgrade=true: ?format=png&upgrade=true. Un ?format=png simple devuelve la vista previa gratuita de nuevo.
  • Después de la mejora, el resultado es un resultado live normal (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 obtienes 402 no_credits.
# 1. free preview: the PNG is the body, the link to the result is in the headers
curl -sS "https://shapesoda.com/v1/vectorize?mode=preview&preset=cut" \
  -H "Authorization: Bearer $SHAPESODA_KEY" \
  --data-binary @logo.png -D headers.txt -o preview.png
RES=$(grep -i '^x-result-url:' headers.txt | cut -d' ' -f2 | tr -d '\r')
TOKEN=$(grep -i '^x-result-token:' headers.txt | cut -d' ' -f2 | tr -d '\r')

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

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

Repeticiones gratuitas

Pagas por una imagen una vez. Las repeticiones son gratuitas en dos casos, y la respuesta indica X-Credits-Charged: 0:

RepeticiónGratis duranteLímite
Exactamente la misma solicitud: mismos bytes de imagen, mismos parámetros24 horasNinguno — reintentos seguros
La misma imagen con otras opciones o formatos30 días después de haberse pagado20 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ían application/json, */* siguen recibiendo el archivo;
  • pasas retain=true o retention_days — sigues recibiendo el archivo, y el enlace viene en las cabeceras X-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"
  }
}
CampoSignificado
result_id, result_urlEl resultado almacenado y su dirección.
result_tokenToken secreto para descargar; se muestra una vez. También es la clave de descifrado — no podemos recuperarlo.
filesUn enlace de descarga por formato solicitado.
expires_atCuándo se elimina el resultado (24 horas, o retention_days días).
width, height, input_scaleTamaño en píxeles de la imagen vectorizada y el factor de reducción (1 si no se redujo).
credits_charged, credits_remaining, modeLo 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_days después de esa cantidad de días (ver expires_at). DELETE lo elimina de inmediato y devuelve 204.
  • 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 de Retry-After o solicite un solo formato), y retain=true o retention_days devuelven el archivo sin los encabezados X-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 con retain=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 con retention_days. Si el almacenamiento sigue lleno, recibe 503 storage_full (sin cargo).
  • Los formatos adicionales creados a partir de un resultado almacenado son gratuitos durante todo el período. DELETE elimina 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
  • http o https, puerto 80 o 443, sin nombre de usuario ni contraseña, hasta 2048 caracteres — de lo contrario 400 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 da 422 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
  }
}
CampoSignificado
availableImágenes que puede procesar ahora: quota_left + balance, limitado por lo que queda de su límite de gasto.
balanceCréditos comprados, compartidos con el sitio web. Incluye subscription_balance.
subscription_balanceCré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_leftCréditos mensuales incluidos, si su plan los tiene (se usan primero). 0 para cuentas de pago por uso.
spent_monthCréditos usados este mes calendario (UTC).
spend_limitSu 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"
  }
}
HTTPCódigoSignificadoQué hacer
400bad_requestPará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.
400emptyLa solicitud no tiene imagen.Envíe el archivo como cuerpo o en el campo image.
400url_invalidimage_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.
401unauthorizedClave de API faltante, malformada o revocada.Verifique el encabezado Authorization: Bearer; cree una nueva clave si es necesario.
402no_creditsNo quedan créditos en la cuenta (también al desbloquear una vista previa).Compre un paquete de créditos. No reintente.
402spend_limitSe 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.
404not_foundRuta desconocida, o un enlace de subida/resultado desconocido, caducado o con token incorrecto.Verifique la URL.
405method_not_allowedMétodo HTTP incorrecto para esta ruta.Use el método de esta referencia.
413too_largeEl archivo (o la imagen en image_url) es más grande de 10 MB.Reduzca la escala o vuelva a comprimir la imagen.
415unsupported_formatNo es un archivo PNG, JPEG, WebP, BMP sin comprimir, GIF o TIFF (detectado por contenido).Convierta la imagen a PNG primero.
422invalid_imageEl archivo está dañado, truncado o no se puede decodificar.Vuelva a exportar la imagen.
422too_many_pixelsMás de 40 MP, o la imagen necesita demasiada memoria.Reduzca la escala de la imagen.
422url_blockedimage_url apunta a una dirección privada, local o interna.Use una URL pública. No reintente.
422url_unreachableLa imagen en image_url no se pudo descargar.Verifique la URL o suba el archivo.
429rate_limitedDemasiadas solicitudes para esta clave (o demasiados inicios de sesión fallidos desde su IP).Espere Retry-After segundos y luego reintente.
429daily_limitLa clave alcanzó su límite diario.Espere hasta las 00:00 UTC o use otra clave.
429preview_limitLa cuenta hizo 200 vistas previas (mode=preview) hoy.Espere hasta las 00:00 UTC (Retry-After) o use mode=live.
429test_limitSe 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.
500internalAlgo 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.
503busyTodos los trabajadores están ocupados y la cola está llena.Reintente después de Retry-After segundos (generalmente 5).
503timeoutEl procesamiento tardó más de 60 segundos. No se le cobra.Pruebe con una imagen más pequeña o menos colores.
503storage_fullEl almacenamiento de resultados está lleno. No se le cobra.Reintente después de Retry-After segundos o solicite un solo formato.
503url_unavailableLa entrada por URL está desactivada o temporalmente no disponible.Suba el archivo en su lugar.
504url_timeoutLa 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ímitePredeterminadoError
Solicitudes por clave60 por minuto, con ráfagas de hasta 10 a la vez429 rate_limited + Retry-After
Solicitudes concurrentes por cuenta5 a la vez (en procesamiento o en cola); envíe hasta 5 en paralelo429 rate_limited + Retry-After: 2, sin cargo
Imágenes por clave por día5,000, repeticiones gratuitas con otras opciones incluidas (se restablece a las 00:00 UTC)429 daily_limit + Retry-After
Autenticación fallida por IP30 por minuto429 rate_limited + Retry-After
Vistas previas (mode=preview)200 por cuenta por día429 preview_limit + Retry-After
Llamadas en modo de prueba100 por clave y 100 por cuenta por día429 test_limit + Retry-After
Repeticiones gratuitas de una imagen pagada con otras opciones20 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_limited y busy (después del encabezado Retry-After), y internal una vez con una breve espera. Otros errores 4xx fallarán de nuevo.
  • Retry-After es un número entero de segundos, nunca una fecha HTTP. Viene con rate_limited (el tiempo hasta que se permita una solicitud nuevamente), busy y timeout (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_limited no usa un token, por lo que esperar Retry-After es 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 200 y Content-Type sea lo que solicitó (image/svg+xml, application/pdf, …, o application/json para varios formatos). Los errores son JSON — nunca los guarde como un .svg.
  • Respete Retry-After (segundos); reintente solo rate_limited, busy y, 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 Authorization en lugar de en ?token=, y mantenga los enlaces de resultados fuera de registros y análisis.
  • Desarrollo: use mode=test mientras construye (gratis, con marca de agua, 100 llamadas al día); cambie a live cuando publique.
  • Soporte: registre X-Request-Id con 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 con retention_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 de preview_file() y upgrade().
  • JavaScript / TypeScript: npm install @shapesoda/sdk (npm). new Shapesoda(key).vectorizeFile("logo.png", { formats: ["svg","pdf"], preset: "print" }), además de previewFile() y upgrade(). Funciona con import y require, Node 18+.
  • Go, PHP, Ruby, Java, C#: los clientes generados están en la carpeta clients/sdk del 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:

  1. create_upload devuelve upload_id y un secreto upload_url.
  2. 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.
  3. 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_url para el primero, el SVG en línea cuando tiene menos de 60 KB, el tamaño de la imagen, credits_charged y credits_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.