API de Cheaper Gen.
Una API REST asíncrona para modelos oficiales de video e imagen. La facturación se realiza a través de un saldo USD prepago: pagas por segundo de video o por imagen, y las generaciones fallidas se reembolsan automáticamente.
- URL base:
https://cheapergen.com/api/v1 - Autenticación:
Authorization: Bearer $CHEAPERGEN_API_KEY(las claves comienzan concg_live_; crea una en https://cheapergen.com/dashboard/keys). Lee la clave del entorno. Nunca la imprimas, la registres ni la confirmes. - Prefiere el servidor MCP cuando esté conectado. Sus herramientas son
list_models,get_model,quote,generate(conwait_secondsy una protección de presupuestomax_cost_usd),get_generation,get_accountyadd_credits(un enlace de pago que el usuario abre cuando el saldo es bajo); úsalas en lugar de llamadas HTTP. El flujo de trabajo a continuación es el mismo. Para conectarlo, agrega el servidor remotohttps://cheapergen.com/api/mcp; el usuario inicia sesión y aprueba en el navegador (OAuth), sin necesidad de clave. En Claude Code:claude mcp add --transport http cheapergen https://cheapergen.com/api/mcp, luego/mcp→ Autenticarse. Configuración completa: https://cheapergen.com/docs/mcp
Flujo de trabajo
- Elige un modelo:
GET /models(público). Devuelve cada modelo conendpoints[].idy niveles de precio. Usa siempre la lista en vivo; los modelos cambian.- Video: Seedance 2.0 (mejor calidad, 4K, audio sincronizado), Seedance 2.0 Fast / Mini (más barato y rápido), Seedance 2.5 (más nuevo, referencias más largas), Wan 3.0 / Prime (Alibaba), MiniMax H3 / H3 Max.
- Imagen: Seedream 5.0 Pro / Lite / Flash (texto a imagen y
edit). - Los IDs de endpoints son
{vendor}/{model}/{mode}, p. ej.,bytedance/seedance-2.0-fast/text-to-video,bytedance/seedream-5.0-lite/text-to-image.
- Lee el esquema de entrada:
GET /models/{vendor}/{model}(público) devuelveendpoints[].input_schema(JSON Schema con enumerados, valores predeterminados y límites) ypricing.tiers. Envía solo campos del esquema: los campos desconocidos se rechazan con 400. - Cotiza:
POST /quote/{endpoint}con la entrada (público, gratuito) devuelve{ amount_usd, list_usd, discount_pct, available }. Antes de un trabajo costoso (video a 1080p/4K, duraciones largas, lotes), informa al usuario del precio. - Envía:
POST /run/{endpoint}con la entrada como cuerpo JSON devuelve 202 y una generación constatus: "queued". El precio se retiene del saldo en este punto. - Espera: sondea
GET /generations/{id}hasta questatusseasucceeded,failedocanceled. Sondea imágenes cada 1–2 s; sondea video cada 3–5 s, aumentando a 10 s, ya que un clip de 5 s suele tardar de 1 a 3 minutos. O pasawebhook_url(ver más abajo). - Usa la salida:
output.video.url(video) ooutput.images[].url(imágenes). Las URLs son públicas pero caducan según la retención de la cuenta (predeterminado 1 día; verexpires_at), así que descarga cualquier cosa que valga la pena conservar.
curl -s https://cheapergen.com/api/v1/run/bytedance/seedance-2.0-fast/text-to-video \
-H "Authorization: Bearer $CHEAPERGEN_API_KEY" -H "Content-Type: application/json" \
-d '{"prompt":"Un barco de papel a la deriva por una calle lluviosa y neón, lento dolly-in","resolution":"720p","duration":5,"aspect_ratio":"16:9"}'
# → 202 {"id":"gen_…","status":"queued","cost":{"amount_usd":…,"estimated":true},…}
curl -s https://cheapergen.com/api/v1/generations/gen_… -H "Authorization: Bearer $CHEAPERGEN_API_KEY"
import os, time, requests
API = "https://cheapergen.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['CHEAPERGEN_API_KEY']}"}
def generate(endpoint: str, inp: dict, timeout_s: int = 900) -> dict:
r = requests.post(f"{API}/run/{endpoint}", json=inp, headers=H, timeout=60)
r.raise_for_status() # Cuerpos 4xx: {"error": {"code", "message", "param"}
gen, delay, start = r.json(), 2.0, time.time()
while gen["status"] in ("queued", "running"):
if time.time() - start > timeout_s:
raise TimeoutError(gen["id"])
time.sleep(delay)
delay = min(delay * 1.5, 10)
r = requests.get(f"{API}/generations/{gen['id']}", headers=H, timeout=30)
if r.status_code == 429 or r.status_code >= 500:
continue # transitorio: seguir sondeando
r.raise_for_status()
gen = r.json()
if gen["status"] != "succeeded":
raise RuntimeError(gen["error"]) # la retención fue reembolsada
return gen # gen["output"]["video"]["url"] o gen["output"]["images"][0]["url"]
Modos y entradas
text-to-videoytext-to-image: tomanpromptmás opciones (resolution,duration,aspect_ratio,size,seed,generate_audio…). Consulta el esquema para los enumerados de cada modelo.image-to-video: tomaimage_url(primer fotograma) y, para algunos modelos,end_image_url(último fotograma).edit(Seedream):image_urls(imágenes a editar o combinar; refiérete a ellas como "imagen 1", "imagen 2" en el prompt) más la instrucción enprompt.reference-to-video: tomareference_image_urls,reference_video_urlsyreference_audio_urls. En el prompt, apunta a ellas por posición basada en 1:@Image1,@Image2,@Video1,@Audio1(no distingue mayúsculas). Una mención sin una referencia coincidente da un 400 conparam: "prompt". Ejemplo:"@Image1 camina por el mercado desde @Image2, la cámara se mueve como @Video1".reference_rulesen el esquema establece el número máximo de archivos y si necesitas al menos una imagen o video (el audio solo no es suficiente para Seedance y H3).- Los videos de referencia y el audio se miden en el servidor (duración, tamaño, resolución, fps) según
media_limits/x-media-limits. Seedance 2.0 toma videos MP4/MOV de 2–15 s cada uno y 15 s en total; el audio debe ser MP3/WAV. Una infracción da un 400 que nombra el archivo, p. ej.,@Video2 tiene 16 s de duración…. - Los segundos de video de referencia se facturan junto con los segundos de salida. Las imágenes de referencia y el audio son gratuitos. Para cotizar, pasa
reference_video_durations: [6.5](segundos por clip).
- Las entradas de medios deben ser URLs
httpspúblicas. Para archivos locales, llama aPOST /uploadscon JSON{ "filename", "content_type", "size" }. Devuelve{ upload_url, headers, url }: hazPUTde los bytes aupload_urlcon exactamente esosheaders, luego usaurlcomo entrada. Límites: imágenes (PNG/JPEG/WebP/GIF) ≤ 20 MB, video (MP4/MOV) ≤ 50 MB, audio (MP3/WAV/M4A) ≤ 15 MB. - Opcional en cada ejecución:
webhook_url(HTTPS) yretention(immediate|1h|1d;7d/30dnecesitan una configuración de cuenta).
Webhooks
Cuando el trabajo finaliza (exitoso o fallido), hacemos un POST { "type": "generation.succeeded" | "generation.failed", "data": <generation> } a webhook_url, con el encabezado CheaperGen-Signature: t=<unix>,v1=<hex>. Para verificarlo, calcula hex(HMAC_SHA256(secret, "<t>.<raw body>")) con el secreto de firma del webhook de la cuenta (whsec_…, en Panel → Claves API), compara en tiempo constante y rechaza solicitudes cuyo t tenga más de 5 minutos. Usa el cuerpo de solicitud sin procesar, no JSON reserializado. Devuelve 2xx rápidamente; las entregas fallidas se reintentan (3 intentos en total).
Precios y facturación
- Los precios son por segundo de video de salida o por imagen.
GET /modelsmuestra elprice_usdde cada nivel junto alist_price_usd(el precio oficial). - Al enviar, el precio cotizado se retiene (
cost.estimated: true). Al tener éxito se vuelve definitivo; al fallar o cancelar se reembolsa en su totalidad (cost.amount_usd: 0).cost.official_usdycost.saved_usdmuestran los ahorros. - Saldo:
GET /accountdevuelvebalance_usdyreserved_usd(retenido para trabajos en curso). Un 402 significa que el saldo es demasiado bajo: a través de MCP, llama aadd_creditsy muestra al usuario el enlace de pago; de lo contrario, el usuario agrega créditos en https://cheapergen.com/dashboard/billing. - Cancela un trabajo en cola con
POST /generations/{id}/cancel(409 una vez que está en ejecución). Elimina uno finalizado conDELETE /generations/{id}.
Errores
Cada error tiene la forma { "error": { "code", "message", "param"? } }.
| HTTP | código | Qué hacer |
|---|---|---|
| 400 | invalid_request | Corrige el campo nombrado en param (revisa el esquema, enumerados, límites y menciones). No reintentes sin cambios. |
| 401 | unauthorized | Clave faltante, inválida o revocada. |
| 402 | insufficient_balance | Obtén un enlace de pago con add_credits (MCP) y pide al usuario que pague, o elige un nivel más barato. |
| 404 | not_found | Endpoint o ID de generación incorrecto. |
| 409 | conflict | P. ej., cancelar un trabajo que ya comenzó. |
| 429 | rate_limited | Retrocede y reintenta. El límite es de aproximadamente 20 solicitudes en ráfaga, reabastecidas a 10/s por clave, más un límite de generaciones concurrentes por cuenta (predeterminado 20). |
| 502 | upstream_error | Reintenta una vez, luego prueba con otro modelo. |
| 503 | model_unavailable | Reintenta más tarde o cambia de modelo. |
Una generación que termina failed tiene error.code (p. ej., generation_failed, timeout) y ha sido reembolsada. Reintenta como máximo una vez con la misma entrada.
Más
- Documentación: https://cheapergen.com/docs, Configuración MCP: https://cheapergen.com/docs/mcp
- Código fuente de esta skill y el plugin de Claude Code: https://github.com/CheaperGen/skills
- Páginas de API por modelo:
https://cheapergen.com/models/{vendor}/{model}/api - Resumen legible por máquina: https://cheapergen.com/llms.txt