aisplice

Riferimento API

Evita addebiti doppi

Idempotenza significa che una richiesta ripetuta viene eseguita una sola volta: invia la stessa Idempotency-Key con il nuovo tentativo e l'addebito avverrà una sola volta, anche se il primo tentativo sembrava solo andato perso.

Una richiesta andata in timeout non è una richiesta fallita. La generazione potrebbe essere già in corso, e un nuovo tentativo alla cieca ne avvia — e ne addebita — una seconda. Invia una Idempotency-Key e il gateway esegue la richiesta una sola volta, poi riproduce la stessa risposta per 24 ore.

#Quando inviare una chiave

Inviala

  • Ogni invio asincrono — video, task immagine, musica, 3D. Sono quelli costosi.
  • Qualsiasi chiamata non in streaming che il tuo worker riprova automaticamente.
  • Chiamate dietro una coda che può riconsegnare un job dopo un crash.

Non serve

  • Richieste in streaming — l'header viene rifiutato con 400, uno stream non può essere riprodotto.
  • Polling GET e letture di metadati — ripeterli è già gratuito e sicuro.
  • Chat interattive in cui una risposta duplicata costa meno della contabilità.
submit.sh
curl https://www.aisplice.store/v1/videos \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "doubao-seedance-2.5",
    "prompt": "A drone shot of a lighthouse at dawn"
  }'

Ricava la chiave dal tuo record

Un UUID generato dentro il ciclo di retry vanifica lo scopo — ogni tentativo ottiene una nuova identità. Ricavalo dalla riga di business che stai evadendo (id ordine, id job), così ogni tentativo di quel job usa la stessa chiave.

#Come vengono confrontate le chiavi

Su attivazione, mai automatico

Senza l'header non cambia nulla. Un nuovo tentativo senza chiave è una seconda richiesta fatturabile.

Solo metodi di scrittura

GET, HEAD e OPTIONS ignorano l'header — interrogare un task è già sicuro da ripetere.

Ambito: utente + chiave + metodo + route

Lo stesso valore con un'altra chiave API, o su un altro endpoint, è un'operazione diversa.

Il corpo fa parte dell'identità

Percorso, query e corpo vengono sottoposti insieme all'hash. Riutilizzare una chiave con un payload modificato restituisce 409, non sovrascrive.

Non compatibile con lo streaming

Una risposta in streaming non può essere salvata per il replay, quindi stream:true insieme a una chiave viene rifiutato con 400.

Blocca in caso di guasto

Se il registro non è disponibile, la richiesta viene rifiutata con 503 invece di essere eseguita senza protezione.

Le chiavi hanno al massimo 255 caratteri. Quelle più lunghe vengono rifiutate con 400 prima che la richiesta venga eseguita.

#Esiti e codici di stato

Una risposta riprodotta include X-Idempotent-Replayed: true e lo stato originale — trattala come la risposta, non come un duplicato da riconciliare. Ogni rifiuto usa la struttura piatta dei controlli preliminari.

HTTPCodiceSignificatoCosa fare
400IDEMPOTENCY_STREAM_UNSUPPORTEDIdempotency-Key inviata su una richiesta in streaming.Rimuovi l'header per le chiamate in streaming, oppure imposta stream:false quando ti serve la protezione da replay.
400IDEMPOTENCY_BODY_UNREADABLEImpossibile calcolare l'hash del corpo per il registro di idempotenza.Invia un normale corpo JSON o multipart — i corpi chunked che non possono essere bufferizzati vengono rifiutati.
409IDEMPOTENCY_PAYLOAD_CONFLICTQuesta chiave è già stata usata con un corpo, un percorso o una query diversi.Genera una nuova chiave. Una chiave identifica una singola richiesta esatta, non un ciclo di tentativi.
409IDEMPOTENCY_IN_PROGRESSLa prima richiesta con questa chiave è ancora in esecuzione.Attendi Retry-After (2s) e riprova con la stessa chiave — riceverai la risposta originale.
409IDEMPOTENCY_RESULT_UNREPLAYABLELa richiesta originale è terminata, ma la sua risposta è stata sigillata e non può essere riprodotta.Non reinviare alla cieca. Riconcilia prima tramite l'endpoint del task o i tuoi registri.
503IDEMPOTENCY_UNAVAILABLEIl registro di idempotenza non è disponibile, quindi il controllo blocca la richiesta invece di rischiare un doppio addebito.Riprova con backoff — la richiesta non è stata eseguita.

#Ciclo di retry

Una chiave per job, riutilizzata in ogni tentativo, con il 409 in elaborazione gestito come "attendi", non come "errore".

submit_once.py
import time, uuid, requests

def submit_once(job_id: str, body: dict, api_key: str, tries: int = 5):
    # Same key for every attempt of this job — that is what makes it safe.
    key = str(uuid.uuid5(uuid.NAMESPACE_URL, f"video-job:{job_id}"))

    for attempt in range(tries):
        response = requests.post(
            f"https://www.aisplice.store/v1/videos",
            headers={
                "Authorization": f"Bearer {api_key}",
                "Idempotency-Key": key,
            },
            json=body,
            timeout=60,
        )

        if response.status_code == 409:
            code = response.json().get("code", "")
            if code == "IDEMPOTENCY_IN_PROGRESS":
                time.sleep(int(response.headers.get("Retry-After", 2)))
                continue
            # PAYLOAD_CONFLICT or RESULT_UNREPLAYABLE: a human decision,
            # never a silent resubmit.
            raise RuntimeError(response.text)

        if response.status_code >= 500:
            time.sleep(2 ** attempt)
            continue

        response.raise_for_status()
        replayed = response.headers.get("X-Idempotent-Replayed") == "true"
        return response.json(), replayed

    raise TimeoutError(f"submit for {job_id} did not settle")

Salva l'id del task prima di riprovare qualsiasi cosa

Scrivi task_id nel database nella stessa transazione che segna il job come inviato. La chiave protegge l'invio; solo il tuo record ti protegge dopo che la risposta è andata persa.

Ciclo di vita dei task asincroni →

Non hai ancora risolto?

Inviaci l'ID richiesta o il messaggio di errore e verificheremo.