Qwen-ImageReferencia de la API
Genera y edita imágenes con los modelos Qwen-Image desde código: un inicio rápido en tres pasos, cada campo, créditos por imagen, webhooks e idempotencia.
El acceso a la API para desarrolladores requiere aprobación previa.
La API de imágenes está en beta y sus campos pueden cambiar. El estudio no se ve afectado.
Inicio rápidoen tres llamadas
Configura una clave, envía la tarea con un límite de precio y luego lee el resultado.
- 01
Configura tu clave
El acceso a la API para desarrolladores requiere aprobación previa: pídelo en el estudio, en Claves de API. La clave se muestra una sola vez, así que guárdala en una variable de entorno.
export API_KEY="sk_test_…" - 02
Envía la tarea
Envía la imagen por POST con un Idempotency-Key y un límite max_credits. Los modelos de edición también aceptan image_url. Recibes un 202 y una tarea en cola, y los créditos se retienen.
curl https://api.pershot.app/v1/images/generations \ -H "Authorization: Bearer $API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen-image-2512", "prompt": "A ceramic mug on a walnut table, soft window light, product photo", "size": "1K", "aspect_ratio": "1:1", "max_credits": 2 }'{ "id": "job_01J9ZK4T8W", "object": "job", "kind": "image", "status": "queued", "money_status": "reserved", "queue_position": 2, "requested_model": "qwen-image-2512", "credits_quoted": 2, "credits_charged": null } - 03
Lee el resultado
Haz GET de la tarea hasta que su estado sea succeeded. outputs[].url está firmado y caduca a las 24 horas, así que descarga lo que quieras conservar.
curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \ -H "Authorization: Bearer $API_KEY"
Parámetrosy límites
Se leen del documento OpenAPI, que se genera a partir de los mismos esquemas con los que valida la API. Los campos desconocidos se rechazan.
| Campo | Tipo | Valores permitidos | Notas |
|---|---|---|---|
modelObligatorio | string | qwen-image-2512-lightning · qwen-image-2512 · qwen-image-edit-2511-lightning · qwen-image-edit-2511 | El id del modelo. |
promptObligatorio | string | 2–2000 caracteres | Qué generar: el sujeto, la cámara y la luz. |
sizePredeterminado: "1K" | string | 1K · 2K | 1K para borradores y primeros fotogramas, 2K para detalle. |
aspect_ratioPredeterminado: "1:1" | string | 16:9 · 9:16 · 1:1 | Forma del encuadre. |
image_urlOpcional | string | URL https | Primer fotograma para video; la imagen de origen para los modelos de edición. Una URL https pública o una URL de subida. |
seedOpcional | integer | 0–2.147.483.647 | Repite un resultado con las mismas entradas. |
quote_idOpcional | string | qt_… | Ejecuta con una cotización de POST /v1/quotes, válida durante 10 minutos. |
max_creditsOpcional | number | > 0 | Lo máximo que puede costar esta tarea. Si se supera, la tarea se rechaza y nunca se cobra. |
webhook_urlOpcional | string | URL https | Una URL https que recibe los eventos de la tarea. |
end_user_idOpcional | string | 1–128 caracteres | Para cuentas de desarrollador aprobadas (el acceso requiere aprobación previa): tu propio id de usuario para esta solicitud, visible en el uso. |
project_idOpcional | string | prj_… | |
shot_idOpcional | string | sht_… | |
routing_preferenceOpcional | string | stability · speed |
Envía exactamente uno de quote_id o max_credits. Este endpoint rechaza model "auto": para video, llama primero a POST /v1/auto/plan y luego envía el modelo que elija con max_credits.
- Modelos
qwen-image-2512-lightningGenerar · Qwen-Image-2512 · Lightning 4-stepqwen-image-edit-2511-lightningEditar · Qwen-Image-Edit-2511 · Lightningqwen-image-2512Generar · Qwen-Image-2512qwen-image-edit-2511Editar · Qwen-Image-Edit-2511- Frecuencia y concurrencia
- 60 solicitudes por minuto por clave y 3 tareas a la vez por cuenta de forma predeterminada. Más claves no agregan concurrencia.
- Para agentes de IA
- Texto plano para agentes de IA: entradas, valores admitidos, cada precio y un ejemplo de solicitud
qwen-image-2512-lightningqwen-image-2512qwen-image-edit-2511-lightningqwen-image-edit-2511
Límites
Se leen de la misma tabla que usa la API para sus comprobaciones. Cada fila indica quién verifica el límite y qué pasa si se supera.
- 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.
| Límite | Valor | Unidad | Quién lo verifica | Si se supera |
|---|---|---|---|---|
| Solicitud | ||||
Longitud del promptprompt | De 2 a 2000 | caracteres | Aplicado | 400 invalid_request en el campo prompt, antes de cualquier cotización o retención. |
Tamaño de imagensize | 1K, 2KPredeterminado: 1K | – | Aplicado | 400 invalid_request en el campo size. |
Relación de aspectoaspect_ratio | 16:9, 9:16, 1:1Predeterminado: 1:1 | – | Aplicado | 400 invalid_request en el campo aspect_ratio. |
| Imágenes que envías | ||||
Imagen de origenimage_url | Ninguno | – | Aplicado | Los modelos de edición necesitan image_url y los de generación lo rechazan: 400 invalid_request en el campo image_url. |
| Salida | ||||
| Píxeles de salida | 1K16:91344×7689:16768×13441:11024×10242K16:92048×11529:161152×20481:12048×2048 | px | Aplicado | Pedimos exactamente estos tamaños. |
| Lado de salida | De 256 a 2048 | px por lado | Proveedor | Todos los tamaños anteriores caben. El proveedor rechaza los más grandes. |
| Formato de salida | JPEG | – | Aplicado | Cada imagen es un JPEG. |
| Enlaces | ||||
| Enlace de salida | 24 | horas | Aplicado | Después de eso, el enlace responde 403. Vuelve a hacer GET de la tarea para obtener un enlace nuevo. El archivo en sí se conserva 12 meses (outputs_expire_at de la tarea indica la fecha), y más tiempo si el resultado está guardado en Recursos o se usa en un proyecto. |
| Límite | Valor | Unidad | Quién lo verifica | Si se supera |
|---|---|---|---|---|
| Solicitud | ||||
Longitud del promptprompt | De 2 a 2000 | caracteres | Aplicado | 400 invalid_request en el campo prompt, antes de cualquier cotización o retención. |
Tamaño de imagensize | 1K, 2KPredeterminado: 1K | – | Aplicado | 400 invalid_request en el campo size. |
Relación de aspectoaspect_ratio | 16:9, 9:16, 1:1Predeterminado: 1:1 | – | Aplicado | 400 invalid_request en el campo aspect_ratio. |
| Imágenes que envías | ||||
Imagen de origenimage_url | Exactamente 1 | – | Aplicado | Los modelos de edición necesitan image_url y los de generación lo rechazan: 400 invalid_request en el campo image_url. |
Tamaño de archivo subidofile | hasta 10 | MB | Aplicado | POST /v1/uploads responde 400 invalid_request en el campo file. |
Formatos de archivo subidofile | JPEG, PNG, WebP | – | Aplicado | Se lee del propio archivo, no de su nombre. Cualquier otro formato: 400 invalid_request en el campo file. |
| Salida | ||||
| Píxeles de salida | 1K16:91344×7689:16768×13441:11024×10242K16:92048×11529:161152×20481:12048×2048 | px | Aplicado | Pedimos exactamente estos tamaños. |
| Lado de salida | De 512 a 2048 | px por lado | Proveedor | Todos los tamaños anteriores caben. El proveedor rechaza los más grandes. |
| Formato de salida | JPEG | – | Aplicado | Cada imagen es un JPEG. |
| Enlaces | ||||
| Enlace de subida | 7 | días | Aplicado | Después de eso, el enlace responde 403. Sube el archivo de nuevo. |
| Enlace de salida | 24 | horas | Aplicado | Después de eso, el enlace responde 403. Vuelve a hacer GET de la tarea para obtener un enlace nuevo. El archivo en sí se conserva 12 meses (outputs_expire_at de la tarea indica la fecha), y más tiempo si el resultado está guardado en Recursos o se usa en un proyecto. |
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.
Precioscalculados a la vista
Un precio por imagen, según modelo y tamaño, en créditos (1 US$ = 100 créditos). Generar y editar tienen precios distintos.
| Modelo | 1K | 2K |
|---|---|---|
qwen-image-2512-lightningGenerar | 1 cr≈ 0,01 US$≈ 1000 imágenes por 10 US$ | 3 cr≈ 0,03 US$≈ 333 imágenes por 10 US$ |
qwen-image-edit-2511-lightningEditar | 2 cr≈ 0,02 US$≈ 500 imágenes por 10 US$ | 4 cr≈ 0,04 US$≈ 250 imágenes por 10 US$ |
qwen-image-2512Generar | 2 cr≈ 0,02 US$≈ 500 imágenes por 10 US$ | 6 cr≈ 0,06 US$≈ 166 imágenes por 10 US$ |
qwen-image-edit-2511Editar | 3 cr≈ 0,03 US$≈ 333 imágenes por 10 US$ | 10 cr≈ 0,10 US$≈ 100 imágenes por 10 US$ |
Webhookse idempotencia
Los reintentos nunca cobran dos veces y no tienes que consultar el estado periódicamente.
- Idempotencia
- Cada POST de generación necesita un Idempotency-Key (de 1 a 255 caracteres ASCII visibles). La misma clave dentro de 24 horas devuelve la misma tarea con HTTP 200, así que un reintento después de un tiempo de espera agotado nunca crea ni cobra una segunda tarea.
- Prompts rechazados
- Un prompt rechazado por la verificación previa (422 content_rejected) no crea nada, así que la misma clave puede volver a usarse con un prompt nuevo.
- Eventos
job.queuedjob.processingjob.succeededjob.failedjob.cancelled- Firma
- X-Signature lleva t, la hora unix, y v1, un HMAC-SHA256 de t, un punto y el cuerpo sin procesar, con tu secreto whsec_ como clave. Rechaza las marcas de tiempo de más de cinco minutos y elimina duplicados con X-Event-Id.
- Entrega
- Una respuesta 2xx en 10 segundos cuenta como entregada. Las entregas fallidas se reintentan después de 1 min, 5 min, 30 min, 2 h y 6 h. No se siguen las redirecciones.
{
"id": "evt_01J9ZKB1QX",
"type": "job.succeeded",
"created_at": "2026-09-29T14:02:31Z",
"data": {
"job": {
"id": "job_01J9ZK4T8W",
"object": "job",
"kind": "image",
"status": "succeeded",
"money_status": "settled",
"requested_model": "qwen-image-2512",
"credits_charged": 2,
"outputs": [
{
"kind": "image",
"url": "https://…/job_01J9ZK4T8W.png?sig=…"
}
]
}
}
}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.Erroresy qué hacer
Todos los errores tienen la misma forma: un objeto error con code, message, param y request_id. Los que probablemente encuentres:
| Código | HTTP | Qué hacer |
|---|---|---|
invalid_request | 400 | Corrige el campo indicado en param. |
unauthorized | 401 | Revisa la clave y el encabezado Bearer. |
insufficient_credits | 402 | Recarga y vuelve a intentarlo con el mismo Idempotency-Key. |
forbidden_model | 403 | Agrega el modelo o la herramienta a la lista de permitidos de la clave en el estudio, o usa otra clave. Herramientas como Mejorar necesitan su propia entrada. |
quote_expired | 409 | Obtén una cotización nueva; las cotizaciones duran 10 minutos. |
price_changed | 409 | El precio subió por encima de tu límite. Vuelve a cotizar o aumenta max_credits. |
content_rejected | 422 | No se cobró nada. Cambia el prompt o las imágenes. |
rate_limited | 429 | Espera el tiempo indicado en Retry-After y vuelve a intentarlo. |
upstream_unavailable | 503 | El modelo no está disponible. Vuelve a intentarlo más tarde; una tarea que nunca empieza no se cobra. |