Reintenta con seguridad con Idempotency-Key

· Hyperouter · 5 min de lectura

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},
)

Qué reproduce cada endpoint

EndpointUna repetición devuelve
POST /v1/images/generations, POST /v1/images/editsLa 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}/remixEl objeto Video de la primera solicitud, tal como está ahora.
POST /v1/chat/completions409 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íasHyperouter responde
Una clave nuevaEjecuta la solicitud con normalidad.
La misma clave y la misma solicitud, después de que la primera fuera aceptadaEl 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á aceptandoEspera 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 distinta409 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 solicitudEjecuta la solicitud como nueva.
Una clave de más de 255 caracteres, o que no sea ASCII imprimible400 invalid_idempotency_key. No se ejecuta nada.

Reglas

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.