Saltar al contenido

Documentaciónde la clave al primer clip

Todo lo que necesitas para una primera generación: una clave, una solicitud, la tarea y el webhook.

El acceso a la API para desarrolladores requiere aprobación previa.

Descripción general

REST sobre HTTPS, con JSON de entrada y de salida. Cada generación es asíncrona.

URL base
https://api.pershot.app/v1
Autenticación
Authorization: Bearer seguido de tu clave. Las claves empiezan por sk_test_ o sk_live_ y se muestran una sola vez; solo guardamos un hash.
Permisos de las claves
Cada clave tiene permisos y, si configuras una en el estudio, una lista de modelos y herramientas permitidos. Una clave con lista de permitidos solo puede usar lo que está en ella, incluidas las herramientas: ampliación de imágenes, eliminación de fondo, Mejorar, sincronización labial, expansión de imagen y el editor de imágenes. Todo lo demás responde 403 forbidden_model. 1080p o 4K con Mejorar necesita tanto el modelo de video como Mejorar.
Forma de los errores
{ "error": { "code", "message", "param", "request_id" } }
Identificadores
Con prefijo según el tipo: job_, qt_, key_, up_.
Para agentes de IA
Texto plano para agentes de IA: entradas, valores admitidos, cada precio y un ejemplo de solicitudseedance-2.0-fastseedance-2.0-miniqwen-image-2512-lightningqwen-image-2512qwen-image-edit-2511-lightningqwen-image-edit-2511

Las generaciones reales aún no están habilitadas. Deja tu correo en la página de un modelo y te escribiremos una sola vez, cuando se habiliten.

Inicio rápido

Cuatro pasos, cada uno con la respuesta que deberías ver.

  1. 01

    Consigue una clave

    El acceso a la API para desarrolladores requiere aprobación previa: pídelo en el estudio, en Claves de API. Cuando te lo aprueben, crea ahí una clave y cópiala: la clave completa se muestra una sola vez.

    Shell
    export API_KEY="sk_test_…"
  2. 02

    Envía una solicitud

    Envía la toma por POST con un Idempotency-Key y un límite max_credits. Recibes un 202 y una tarea en cola de inmediato, y los créditos se retienen.

    POST /videos/generations
    curl https://api.pershot.app/v1/videos/generations \
      -H "Authorization: Bearer $API_KEY" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "seedance-2.0-fast",
        "prompt": "Perfume mist in hard side light. Slow push-in, black background, grain.",
        "duration": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "generate_audio": true,
        "max_credits": 34
      }'
    Deberías ver
    {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "queued",
      "money_status": "reserved",
      "queue_position": 2,
      "requested_model": "seedance-2.0-fast",
      "credits_quoted": 34,
      "credits_charged": null
    }
  3. 03

    Revisa la tarea

    Haz GET de la tarea hasta que su estado sea succeeded. La URL de salida está firmada y caduca; descarga lo que quieras conservar.

    GET /jobs/{id}
    curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \
      -H "Authorization: Bearer $API_KEY"
    Deberías ver
    {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "succeeded",
      "money_status": "settled",
      "requested_model": "seedance-2.0-fast",
      "resolved_model": "seedance-2.0-fast",
      "credits_quoted": 34,
      "credits_charged": 34,
      "outputs": [
        {
          "kind": "video",
          "url": "https://…/job_01J9ZK4T8W.mp4?sig=…",
          "width": 1280,
          "height": 720,
          "duration_s": 5
        }
      ],
      "error": null
    }
  4. 04

    Recibe el webhook

    Pasa webhook_url y te enviaremos por POST cada cambio de estado, firmado con una marca de tiempo. Responde con 2xx; las entregas fallidas se reintentan con espera progresiva.

    POST → webhook_url
    {
      "id": "evt_01J9ZKB1QX",
      "type": "job.succeeded",
      "created_at": "2026-09-29T14:02:31Z",
      "data": {
        "job": {
          "id": "job_01J9ZK4T8W",
          "object": "job",
          "status": "succeeded",
          "money_status": "settled",
          "credits_charged": 34,
          "outputs": [
            {
              "kind": "video",
              "url": "https://…/job_01J9ZK4T8W.mp4?sig=…",
              "width": 1280,
              "height": 720,
              "duration_s": 5
            }
          ]
        }
      }
    }

Endpoints

Todas las rutas son relativas a la URL base.

MétodoRutaQué hace
GET/v1/modelsModelos, capacidades y estado disponibles para tu cuenta
GET/v1/pricingPrecios públicos; con sesión iniciada, los precios de tu cuenta
POST/v1/quotesPlan, partidas, cargo máximo y vencimiento (10 minutos)
POST/v1/videos/generationsGenerar un video; 202 y una tarea
POST/v1/images/generationsGenerar o editar una imagen; 202 y una tarea
GET/v1/jobs/{id}Estado, resultados como enlaces firmados, costo real
GET/v1/jobsTus tareas, de la más reciente a la más antigua
POST/v1/jobs/{id}/cancelSolicitar la cancelación; los créditos retenidos se reembolsan salvo que el modelo ya haya aceptado la tarea
GET/v1/balanceCréditos disponibles y retenidos, por tipo
GET/v1/usageUso por día o por modelo, en JSON o CSV
POST/v1/uploadsSubir un JPEG, PNG o WebP de hasta 10 MB

Cotizaciones y límites de precio

Cada generación necesita un quote_id o un límite max_credits, nunca ambos. Con un quote_id, la tarea se ejecuta con esa cotización; con max_credits, se ejecuta solo si el precio es igual o menor que el límite.

model: "auto" siempre necesita max_credits. Una cotización es válida durante 10 minutos; después recibes quote_expired.

Idempotencia

Envía un encabezado Idempotency-Key (de 1 a 255 caracteres ASCII visibles) en cada solicitud de generación.

La misma clave dentro de 24 horas devuelve la misma tarea con HTTP 200 en lugar de 202, así que reintentar después de un tiempo de espera agotado nunca crea ni cobra una segunda tarea.

Webhooks

Pasa webhook_url en una generación (o en un lote) para recibir sus cambios de estado. Cada evento es un POST cuyo cuerpo JSON tiene id, type, created_at y data: data.job para los eventos de tarea, data.batch para batch.completed.

Eventos

  • job.queued
  • job.processing
  • job.succeeded
  • job.failed
  • job.cancelled
  • batch.completed
  • Cada entrega tiene un id de evento, una marca de tiempo y una firma. Verifícala antes de confiar en el cuerpo.
  • Una entrega cuenta cuando tu endpoint responde 2xx en 10 segundos. Si no, se reintenta después de 1 min, 5 min, 30 min, 2 h y 6 h, y luego se marca como fallida; soporte puede reenviarla. No se siguen las redirecciones.
  • Las URL de webhook deben ser https. Se rechazan los hosts con direcciones privadas, de loopback o reservadas, tanto al enviar como al conectarnos.

Encabezados

X-Signature
t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>
X-Event-Id
El id del evento, también en el cuerpo. Los reintentos y reenvíos lo conservan, así que guárdalo y omite un id que ya hayas procesado.
Secreto de firma
Un secreto whsec_… por cuenta, en el estudio, en Claves de API. La clave HMAC es el secreto completo, incluido el prefijo.
Rotación
Después de rotarlo, el secreto anterior sigue firmando durante 24 horas como una segunda entrada v1. Acepta una entrega si alguna v1 coincide.

Calcula el HMAC sobre el cuerpo sin procesar, no sobre una copia vuelta a serializar, compara en tiempo constante y rechaza las marcas de tiempo de más de cinco minutos.

Verificar X-Signature
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received, before any JSON parsing.
export function verifyWebhook(header, rawBody, secret, toleranceSec = 300) {
  let t = null;
  const signatures = [];
  for (const item of (header ?? "").split(",")) {
    const [key, value] = item.split("=", 2);
    if (key === "t") t = Number(value);
    if (key === "v1") signatures.push(Buffer.from(value, "hex"));
  }
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return signatures.some((s) => s.length === expected.length && timingSafeEqual(s, expected));
}

// verifyWebhook(req.headers["x-signature"], rawBody, process.env.WEBHOOK_SECRET)
// Answer 2xx quickly, and skip an X-Event-Id you have already handled.

Errores

Los códigos y los estados HTTP son fijos; el mensaje es para las personas.

CódigoHTTPCuándo
invalid_request400Falta un campo o está fuera de rango
unauthorized401Sin clave, una clave revocada o una firma incorrecta
insufficient_credits402El saldo no cubre la retención
forbidden_model403La lista de permitidos de la clave no incluye este modelo o herramienta
region_not_supported403El modelo no se ofrece en tu región
developer_api_closed403Una clave de desarrollador mientras la API para desarrolladores está cerrada; el acceso requiere aprobación previa
member_connections_paused403Un inicio de sesión de miembro (MCP, código de conexión o línea de comandos) mientras la conexión de tu propio agente de IA está en pausa
not_found404No existe esa tarea, cotización o archivo subido en tu cuenta
quote_expired409La cotización tiene más de 10 minutos
price_changed409El precio subió por encima de tu límite
content_rejected422El contenido se rechazó; no se cobra nada
rate_limited429Demasiadas solicitudes para esta clave
concurrency_limited429Demasiadas tareas en curso para esta cuenta
internal_error500Algo falló de nuestro lado
upstream_unavailable503El modelo no está disponible; vuelve a intentarlo más tarde

Un resultado incierto del modelo no es un error: la tarea informa el estado unknown y se verifica antes de reenviar nada.

Límites

Solicitudes
60 por minuto por clave de forma predeterminada.
Concurrencia
3 tareas a la vez por cuenta de forma predeterminada; puedes pedir que se aumente.
Tope
La capacidad del modelo es un tope estricto. Más claves no agregan concurrencia.

Imágenes

POST /v1/images/generations acepta los mismos encabezados. Elige un id de modelo según el nivel y la operación; los modelos de edición también necesitan image_url, y los de generación lo rechazan.

Nivelgenerareditar
Image MiniEconómico · borradorqwen-image-2512-lightningqwen-image-edit-2511-lightning
Image FastRecomendadoqwen-image-2512qwen-image-edit-2511

Límites por modelo

La página de la API de cada modelo tiene la tabla completa: el valor, la unidad, quién lo verifica y qué pasa si se supera. GET /v1/models devuelve los mismos límites como JSON.

  • AplicadoLo verificamos nosotros y respondemos 400 antes de cotizar o retener nada.
  • ProveedorLo verifica el proveedor. Una tarea que rechaza falla y no se cobra nada.
  • RecomendadoNadie lo verifica. Mantenerse dentro de él da mejores resultados.
ModeloLímites principalesTabla completa
seedance-2.0-fastSeedance 2.0 FastPrompt de 2 a 2000 caracteres · 4 a 15 s · hasta 9 imágenes de referencia · imágenes de 300 a 6000 px por ladoAbrir la tabla
seedance-2.0-miniSeedance 2.0 MiniPrompt de 2 a 2000 caracteres · 4 a 15 s · hasta 9 imágenes de referencia · imágenes de 300 a 6000 px por ladoAbrir la tabla
qwen-image-2512Qwen-Image-2512Prompt de 2 a 2000 caracteres · 1K o 2K · sin imagen de origenAbrir la tabla
qwen-image-2512-lightningQwen-Image-2512 · Lightning 4-stepPrompt de 2 a 2000 caracteres · 1K o 2K · sin imagen de origenAbrir la tabla
qwen-image-edit-2511Qwen-Image-Edit-2511Prompt de 2 a 2000 caracteres · 1K o 2K · una imagen de origen, JPEG, PNG o WebP de hasta 10 MBAbrir la tabla
qwen-image-edit-2511-lightningQwen-Image-Edit-2511 · LightningPrompt de 2 a 2000 caracteres · 1K o 2K · una imagen de origen, JPEG, PNG o WebP de hasta 10 MBAbrir la tabla

Archivos subidos

POST /v1/uploads lee el tipo del propio archivo. Los enlaces de subida duran 7 días.

ArchivoFormatosArchivo más grandeDuración máxima
Imagen: primer fotograma, referencia u origen de ediciónJPEG, PNG, WebP10 MB–
Video: sincronización labialMP450 MB60 s
Audio: sincronización labialMP3, WAV, M4A10 MB60 s

Nunca descargamos imágenes de otros servidores, así que para esos enlaces el proveedor verifica el tamaño, y una tarea que rechaza no se cobra. Los mismos límites se devuelven como JSON: GET /v1/models devuelve un objeto limits para cada modelo.

Si vienes de fal, Replicate o WaveSpeed

No basta con cambiar la URL base y la clave: cada solicitud también necesita el id del modelo en el cuerpo, un encabezado Idempotency-Key y un límite de precio (max_credits o un quote_id). La guía de migración relaciona cada campo de Seedance 2.0 y Qwen-Image y enumera lo que no se traslada.