Reintenta con seguridad con Idempotency-Key
Envías una solicitud que cuesta dinero y la conexión se cae. ¿La recibió Hyperouter? No puedes saberlo. Si la envías de nuevo y había llegado, inicias un segundo trabajo y pagas dos veces.
Un encabezado Idempotency-Key elimina la duda. Hyperouter reconoce la repetición, no inicia un segundo trabajo, no hace una segunda retención y responde a partir de la primera solicitud.
Envía una en cada solicitud que cueste dinero
Genera un valor aleatorio nuevo, como un UUID v4, para cada solicitud nueva. Guárdalo junto con la solicitud y envía el mismo valor cada vez que repitas esa solicitud.
curl https://api.hyperouter.app/v1/images/generations \
-H "Authorization: Bearer $HYPEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-d '{"model": "x-ai/grok-imagine-image", "prompt": "A lighthouse at dusk", "n": 1}'
Los SDK de OpenAI no envían este encabezado por su cuenta. Pásalo como encabezado adicional; los SDK lo reenvían sin cambios en sus reintentos automáticos.
import uuid key = str(uuid.uuid4()) # one per request; reuse it when you retry this request img = client.images.generate( model="x-ai/grok-imagine-image", prompt="A lighthouse at dusk", extra_headers={"Idempotency-Key": key}, )
import { randomUUID } from "node:crypto"; const key = randomUUID(); // one per request; reuse it when you retry this request const img = await client.images.generate( { model: "x-ai/grok-imagine-image", prompt: "A lighthouse at dusk" }, { headers: { "Idempotency-Key": key } }, );
Qué reproduce cada endpoint
| Endpoint | Una repetición devuelve |
|---|---|
POST /v1/images/generations, POST /v1/images/edits | La imagen de la primera solicitud, o su 202 con el mismo id bajo Prefer: respond-async. |
POST /v1/videos, /v1/videos/edits, /v1/videos/extensions, /v1/videos/{id}/remix | El objeto Video de la primera solicitud, tal como está ahora. |
POST /v1/chat/completions | 409 idempotency_key_replayed con el id de la primera solicitud. |
El chat es distinto
Las respuestas de chat no se reproducen. Una repetición con la misma clave responde 409 con el código idempotency_key_replayed, y error.generation_id es el id de la primera solicitud. La solicitud no se vuelve a ejecutar ni a facturar.
{
"error": {
"message": "A request with this Idempotency-Key already ran as generation 019a1f3c-5b7e-7d21-9c4a-2e8f6b0d3a17. ...",
"type": "conflict",
"code": "idempotency_key_replayed",
"param": null,
"generation_id": "019a1f3c-5b7e-7d21-9c4a-2e8f6b0d3a17"
}
}
Consulta la primera solicitud con GET /v1/generations/{id} para ver su estado, consumo y coste. Si el primer chat completion falló y no se facturó, su clave queda libre y una repetición se ejecuta como solicitud nueva.
Así que en el chat la clave es una protección contra un cargo doble, no una forma de volver a obtener la respuesta. Trata el 409 como "ya se ejecutó" y lee el registro.
import os
import httpx
import openai
try:
res = client.chat.completions.create(
model="x-ai/grok-4.7",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"Idempotency-Key": key},
)
except openai.ConflictError as e:
body = e.body or {} # the error object
if body.get("code") != "idempotency_key_replayed":
raise
record = httpx.get(
f"https://api.hyperouter.app/v1/generations/{body['generation_id']}",
headers={"Authorization": f"Bearer {os.environ['HYPEROUTER_API_KEY']}"},
).json()["data"]
Compara por code, nunca por message.
Qué obtiene una repetición
| Envías | Hyperouter responde |
|---|---|
| Una clave nueva | Ejecuta la solicitud con normalidad. |
| La misma clave y la misma solicitud, después de que la primera fuera aceptada | El resultado de la primera solicitud, como en la tabla anterior. Ni segundo trabajo, ni segunda retención, ni segundo cargo. |
| La misma clave y la misma solicitud, mientras la primera todavía se está aceptando | Espera a la primera y luego responde como arriba. Si la primera no ha iniciado su trabajo en 60 segundos: 409 idempotency_key_in_use. Reintenta con la misma clave. |
| La misma clave y una solicitud distinta | 409 idempotency_key_reused. No se ejecuta nada. Usa una clave nueva. |
La misma clave, después de que la primera fuera rechazada antes de iniciar ningún trabajo (un 400, un 402 o un 5xx enviado sin x-should-retry: false) | Ejecuta la solicitud como nueva, para que puedas corregirla y volver a enviarla con la misma clave. |
| La misma clave, más de 30 minutos después de la primera solicitud | Ejecuta la solicitud como nueva. |
| Una clave de más de 255 caracteres, o que no sea ASCII imprimible | 400 invalid_idempotency_key. No se ejecuta nada. |
Reglas
- Ámbito. Una clave pertenece a la clave de API que la envió. Dos claves de API pueden usar el mismo valor sin afectarse entre sí.
- Ventana. Una clave se respeta durante 30 minutos después de la primera solicitud. Reintenta dentro de ese plazo.
- Misma solicitud. Dos solicitudes son la misma cuando coinciden el método, la ruta y el cuerpo. El JSON se compara después de analizarlo, así que el orden de los campos y los espacios no importan. En una subida multipart, también se comparan los bytes de los archivos.
- Facturación. Una solicitud hecha con una clave se factura como máximo una vez, sea cual sea el número de repeticiones. Una repetición respondida a partir de la primera solicitud no cuesta nada.
- Garantía. Una clave nunca inicia más de un trabajo, tampoco cuando dos repeticiones llegan al mismo tiempo.
Reintentos automáticos en los SDK
Los SDK de OpenAI reintentan dos veces, por defecto, una respuesta 408, 409, 429 o 5xx, salvo que la respuesta incluya el encabezado x-should-retry: false. Hyperouter envía ese encabezado en toda respuesta en la que un reintento iniciaría un segundo trabajo o un segundo cargo: el 503 timeout de una imagen síncrona, un 500 después de crear el trabajo, y 409 idempotency_key_reused e idempotency_key_replayed.
Nunca lo envía en un 429, en un 409 idempotency_key_in_use ni en un 5xx anterior a que exista cualquier trabajo. Esos son seguros de reintentar. Con una Idempotency-Key, todos los reintentos son seguros.
Las reglas completas están en Reintentos e idempotencia.