Réessayer en toute sécurité avec Idempotency-Key

· Hyperouter · 5 min de lecture

Vous envoyez une requête qui coûte de l’argent, et la connexion se coupe. Hyperouter l’a-t-il reçue ? Vous ne pouvez pas le savoir. Si vous la renvoyez et qu’elle était arrivée, vous démarrez une seconde tâche et vous payez deux fois.

Un en-tête Idempotency-Key supprime le doute. Hyperouter reconnaît la répétition, ne démarre pas de seconde tâche, ne bloque pas de second montant, et répond à partir de la première requête.

Envoyez-en une sur chaque requête qui coûte de l’argent

Générez une nouvelle valeur aléatoire, par exemple un UUID v4, pour chaque nouvelle requête. Conservez-la avec la requête et envoyez la même valeur chaque fois que vous renvoyez cette requête.

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}'

Les OpenAI SDK n’envoient pas cet en-tête d’eux-mêmes. Passez-le comme en-tête supplémentaire ; les SDK le renvoient alors inchangé lors de leurs nouvelles tentatives automatiques.

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

Ce que rejoue chaque endpoint

EndpointUne répétition renvoie
POST /v1/images/generations, POST /v1/images/editsL’image de la première requête, ou son 202 avec le même id sous Prefer: respond-async.
POST /v1/videos, /v1/videos/edits, /v1/videos/extensions, /v1/videos/{id}/remixL’objet Video de la première requête, dans son état actuel.
POST /v1/chat/completions409 idempotency_key_replayed avec l’id de la première requête.

Le chat est différent

Les réponses de chat ne sont pas rejouées. Une répétition avec la même clé répond 409 avec le code idempotency_key_replayed, et error.generation_id est l’id de la première requête. La requête n’est ni réexécutée ni refacturée.

{
  "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"
  }
}

Retrouvez la première requête avec GET /v1/generations/{id} pour voir son statut, sa consommation et son coût. Si la première chat completion a échoué et n’a pas été facturée, sa clé est libérée et une répétition s’exécute comme une nouvelle requête.

Ainsi, pour le chat, la clé protège contre un double débit, elle ne sert pas à récupérer de nouveau la réponse. Lisez le 409 comme « elle a déjà été exécutée », et lisez l’enregistrement.

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"]

Fiez-vous à code, jamais à message.

Ce que reçoit une répétition

Vous envoyezHyperouter répond
Une nouvelle cléExécute la requête normalement.
La même clé et la même requête, après acceptation de la premièreLe résultat de la première requête, comme dans le tableau ci-dessus. Pas de seconde tâche, pas de second blocage, pas de second débit.
La même clé et la même requête, alors que la première est encore en cours d’acceptationAttend la première, puis répond comme ci-dessus. Si la première n’a pas démarré sa tâche au bout de 60 secondes : 409 idempotency_key_in_use. Réessayez avec la même clé.
La même clé et une requête différente409 idempotency_key_reused. Rien ne s’exécute. Utilisez une nouvelle clé.
La même clé, après que la première a été refusée avant le démarrage de toute tâche (un 400, un 402, ou un 5xx envoyé sans x-should-retry: false)Exécute la requête comme une nouvelle, ce qui vous permet de la corriger et de la renvoyer avec la même clé.
La même clé, plus de 30 minutes après la première requêteExécute la requête comme une nouvelle.
Une clé de plus de 255 caractères, ou qui n’est pas en ASCII imprimable400 invalid_idempotency_key. Rien ne s’exécute.

Règles

Nouvelles tentatives automatiques dans les SDK

Les OpenAI SDK réessaient deux fois par défaut une réponse 408, 409, 429 ou 5xx, sauf si la réponse porte l’en-tête de réponse x-should-retry: false. Hyperouter envoie cet en-tête sur toute réponse pour laquelle une nouvelle tentative démarrerait une seconde tâche ou un second débit : le 503 timeout d’une image synchrone, un 500 après la création de la tâche, et 409 idempotency_key_reused et idempotency_key_replayed.

Il ne l’envoie jamais sur un 429, sur un 409 idempotency_key_in_use, ni sur un 5xx survenu avant l’existence de toute tâche. Dans ces cas, réessayer est sans risque. Avec une Idempotency-Key, chaque nouvelle tentative est sûre.

Les règles complètes se trouvent dans Nouvelles tentatives et idempotence.