spicyapiDocumentazione
Guide
Contenuto principale

Sostituzione di persone

Un solo toolkit, due forme. I modelli a una chiamata sostituiscono un volto o una testa in un’unica richiesta; quelli a due chiamate analizzano prima la clip, così sei tu a scegliere chi cambia.

Alcuni modelli non generano una scena nuova: sostituiscono una persona. Un volto portato da una foto a un’altra, una testa intera oppure un’intera figura dentro una clip video. Il catalogo li raccoglie sotto un’unica etichetta, toolkit: "subject-swap", e si presentano in due forme: una chiamata oppure due chiamate con una scelta in mezzo.

Per il resto non hanno nulla di particolare. Sono normali attività asincrone, i loro parametri arrivano dal rispettivo inputSchema e i risultati compaiono in output.assets[].

toolkit ti dice che il modello è uno di questi

toolkit è un’etichetta di classificazione su una voce del catalogo ed è presente sia nella risposta autenticata di GET /api/v1/models sia nel catalogo pubblico. Oggi ha un solo valore, subject-swap, e nei modelli che non appartengono ad alcun toolkit il campo non c’è affatto: controlla quindi il valore, mai la presenza della chiave.

È un campo puramente informativo. Non decide quale chiave può chiamare un modello, come viene calcolato il prezzo della richiesta o che cosa il modello accetta. Un valore che non conosci va trattato come una voce sconosciuta di badges: ignoralo.

curl -s "https://api.spicyapi.ai/api/v1/models?modality=video" \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  | jq -r '.data.items[]
      | select(.toolkit == "subject-swap" and .enabled and .available)
      | [.model, (.tasks | join(",")), .family] | @tsv'

Ogni riga è un endpoint richiamabile. A decidere tutto il resto di questa pagina è la colonna tasks: una family che ha un endpoint video-analyze è una family a due chiamate, e l’endpoint video-edit che la accompagna non si può chiamare da solo.

Quale delle due forme ho davanti?

Una chiamataDue chiamate
EndpointUn solo endpoint di modificavideo-analyze, poi video-edit, nella stessa family
Chi viene sostituitoChi trova il modello. La richiesta non ha alcun campo per indicare una personaI soggetti che hai scelto, indicati dalla loro posizione
Che cosa inviiL’immagine o la clip di partenza più l’immagine di riferimento, in un’unica richiestaNella prima chiamata la clip; nella seconda le posizioni scelte, ciascuna con le proprie immagini di riferimento
Che cosa paghiUn’attivitàDue attività, con prezzi separati
Adatta quandoNell’inquadratura c’è un solo soggetto evidenteNell’inquadratura ci sono più persone, oppure sbagliare persona costa caro

Entrambe le forme sono async: true: crei un’attività e poi recuperi il risultato con il polling o con un webhook.

Una chiamata

Qui non cambia nulla rispetto a qualsiasi altro modello di modifica. Leggi l’inputSchema dell’endpoint, compilalo, crea l’attività:

const created = await fetch('https://api.spicyapi.ai/api/v1/jobs/createTask', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SPICY_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-8814-swap-1',
  },
  body: JSON.stringify({
    model: swap.model, // una delle voci della query al catalogo qui sopra
    input: {
      image_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
      reference_image_url: 'spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m',
    },
  }),
});

I nomi dei campi cambiano da un endpoint all’altro: alcuni accettano un prompt, un seed o un formato di uscita, altri nient’altro che le due immagini. L’elenco valido è lo schema; non portare i nomi dei campi da un endpoint a un altro. Un riferimento può essere un URL HTTPS pubblico, una piccola immagine in Base64 scritta direttamente nella richiesta oppure un URI spicy:// già caricato; vedi File multimediali.

Due chiamate

La prima chiamata guarda la tua clip e riferisce che cosa ci ha trovato, sotto forma di una serie ordinata di fotogrammi di anteprima. Sei tu a decidere quali sostituire. Il lavoro vero e proprio lo fa la seconda chiamata.

la tua clip ──▶ video-analyze ──▶ output.assets[0], [1], [2] …   (un fotogramma per soggetto)
                                          │
                      scegli le posizioni │ 0 e 2
                                          ▼
          video-edit (analysis_task_id + swap_targets) ──▶ la clip finita

Perché non in una sola chiamata? Perché in una scena con quattro persone una chiamata sola dovrebbe indovinare a chi ti riferivi, e un’ipotesi sbagliata è un video finito e addebitato in cui è stata sostituita la persona sbagliata. Dividere il lavoro rende la scelta esplicita e verificabile.

Analizza la clip

Invia la clip all’endpoint video-analyze. Imposta resolution in modo esplicito se l’endpoint ha quel campo: la seconda chiamata dovrà girare sullo stesso valore, quindi scegli qui la risoluzione che vuoi per la clip finita. Su questi endpoint il prezzo dell’analisi non dipende da essa.

const analysis = await createTask({
  model: analyze.model, // la voce i cui tasks contengono "video-analyze"
  input: {
    video_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
    resolution: '720p',
  },
});
const analysisTaskId = analysis.taskId;

Leggi che cosa ha trovato

Attendi l’attività come al solito. Un’analisi riuscita restituisce un fotogramma di anteprima per ogni soggetto tracciato, in output.assets[]:

{
  "state": "succeeded",
  "output": {
    "assets": [
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" },
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" }
    ]
  },
  "settled": true
}

L’ordine di quell’array è il contratto. L’indice di ogni elemento è l’object_index che rimandi indietro nella seconda chiamata: il primo fotogramma è 0, il secondo 1, e così via. Mostra i fotogrammi a chi deve scegliere, oppure abbinali tu; in ogni caso rimanda posizioni prese da questo stesso array.

Un’analisi che non trova nulla da tracciare non riesce restituendo un array vuoto: finisce come failed con errorCode: "generation_failed", e il blocco viene liberato per intero. Perciò un’analisi in stato succeeded ha sempre almeno un fotogramma, e al tuo codice resta solo da scegliere tra quelli.

Sostituisci i soggetti che hai scelto

Invia l’ID dell’attività di analisi e una voce di swap_targets per ogni soggetto da sostituire. I soggetti che ometti restano intatti.

const swapped = await createTask({
  model: edit.model, // la voce "video-edit" della stessa family
  input: {
    analysis_task_id: analysisTaskId,
    resolution: '720p', // lo stesso valore con cui è girata l’analisi
    swap_targets: [
      { object_index: 0, image_urls: ['spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m'] },
    ],
  },
});

Il risultato è una clip in output.assets[], come in qualsiasi altra attività video.

Che cosa verifica la seconda chiamata prima di accettare

Questi controlli avvengono alla creazione, quindi una richiesta rifiutata non crea nulla e non blocca nulla.

RegolaPerché
L’analisi deve appartenere al tuo account ed essere riuscitaUn’analisi in coda o fallita non ha nulla a cui puntare
Usa la stessa chiave API per entrambe le chiamateUn’analisi si rilegge solo con la chiave che l’ha creata, quindi con un’altra non vedi nemmeno i fotogrammi tra cui stai scegliendo
Ogni object_index deve rientrare nell’intervallo e comparire una volta solaFuori intervallo verrebbe sostituita in silenzio la persona sbagliata; ripetuto, la stessa persona verrebbe sostituita due volte. In entrambi i casi l’attività finirebbe e verrebbe addebitata normalmente
I campi che incidono sul prezzo, come resolution, devono coincidere con l’analisiEreditarli nasconderebbe la fascia di prezzo su cui vieni addebitato
La clip di partenza deve essere la stessaOmetti video_url e viene usata la clip analizzata; se la invii, deve essere identica
L’analisi resta utilizzabile per 24 ore dalla fineDopo di che i dati di tracciamento alle sue spalle non ci sono più e spesso nemmeno la clip di partenza

Imposta `resolution` in entrambe le chiamate, oppure leggi con attenzione il valore predefinito

I campi omessi assumono il valore default del loro schema prima che le due richieste vengano confrontate. Un’analisi lasciata al valore predefinito e una sostituzione lasciata al valore predefinito coincidono davvero, ma un’analisi in cui hai impostato un valore e una sostituzione lasciata vuota no, e la discrepanza viene rifiutata anziché corretta in silenzio. Inviare il valore in modo esplicito in entrambe le chiamate elimina il dubbio.

Quando la seconda chiamata viene rifiutata

Sono tutti HTTP 400 con codice di business code: 400. Il campo msg vale uno di questi:

msgChe cosa è successoChe cosa fare
The referenced analysis task was not found.Su questo account non esiste quell’attività, oppure analysis_task_id non è un ID di attivitàUsa il taskId restituito dalla tua analisi. Un’attività di un altro account viene segnalata allo stesso modo, di proposito
The referenced task belongs to a different model.L’ID punta a un’altra attività: spesso l’attività di sostituzione stessa, oppure l’analisi di un’altra familyFallo puntare all’attività video-analyze della stessa family
The referenced analysis did not complete successfully.L’analisi è ancora in corso oppure è finita come failed / expiredAspettala oppure rilanciala
That analysis has no reusable result. Run the analysis step again.L’analisi è riuscita ma non ha lasciato nulla di utilizzabile per la sostituzioneRilancia l’analisi sulla stessa clip
That analysis result has expired. Run the analysis step again.Sono passate più di 24 oreRilancia l’analisi
"resolution" must match the analysis step.Le due chiamate non concordano su un campo che incide sul prezzoInvia il valore dell’analisi, oppure lancia una nuova analisi con il valore che vuoi
"video_url" must match the analysis step.Hai inviato una clip diversaInvia lo stesso URI, oppure ometti il campo
That analysis found N target(s); "object_index" is out of range.Una posizione va oltre quelle restituite dall’analisiLe posizioni partono da zero e si fermano a assets.length - 1
"object_index" is listed more than once.Due voci indicano lo stesso soggettoAssegna una sola voce per soggetto, con tutte le sue immagini di riferimento nell’image_urls di quella voce

Quanto costano le due chiamate

Analisi e sostituzione sono due attività distinte con due prezzi distinti, ciascuna bloccata e addebitata per conto proprio. Una seconda chiamata rifiutata non costa nulla; un’analisi riuscita viene addebitata anche se poi decidi di non sostituire niente.

  • L’analisi di solito ha prezzo per_request: un addebito per chiamata, qualunque sia la durata della clip e quanti soggetti trovi.
  • La sostituzione di solito ha prezzo per_second, a blocchi interi, e la durata arriva dalla clip e non da un campo che imposti tu. L’importo indicato prima dell’invio vale quindi per la clip più lunga che l’endpoint accetta: l’importo indicato è quello bloccato, l’addebito finale si calcola sulla durata arrotondata per eccesso e non supera mai il blocco, e la differenza torna sul tuo saldo quando l’attività viene chiusa. Vedere un blocco più alto dell’addebito finale, qui, è normale. Leggi Fatturazione prima di farci un budget.

Leggi entrambi i prezzi dal catalogo invece di darli per scontati e, se l’importo esatto conta, chiedi un preventivo per ciascuna chiamata.

Passi successivi

In questa pagina