SDK PHP
L’SDK PHP ufficiale di SpicyAPI (spicyapi/spicyapi) — installalo con Composer, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, verifica i webhook in sicurezza e gestisci ogni codice di errore.
spicyapi/spicyapi è l’SDK PHP ufficiale di SpicyAPI. Mette la generazione di immagini, video e audio dietro un unico SpicyClient: allega la tua chiave API, apre la struttura della risposta in semplici array associativi, attende un’attività con un backoff limitato, carica i file locali con una sola chiamata e verifica le firme dei webhook senza incorrere in nessuno dei tre errori che il codice PHP commette di solito in questo punto.
Richiede PHP 8.1 o successivo più ext-json ed ext-hash, e non ha dipendenze da altre librerie. Usa ext-curl quando è presente; in sua assenza il client ripiega sullo stream wrapper https://.
È lo strumento giusto?
| Vuoi… | Usa |
|---|---|
| Chiamare SpicyAPI dal tuo codice PHP | Questo SDK |
| Chiamarlo da Node.js o TypeScript | L’SDK TypeScript |
| Chiamarlo da Python, Go o Java | L’SDK Python, Go o Java |
| Generare contenuti da un terminale, senza scrivere codice | La CLI |
| Far chiamare SpicyAPI al posto tuo da un assistente IA | Il server MCP e l’Agent Skill |
| Usare un modello di chat o di testo | Un client PHP affermato per i formati OpenAI, Anthropic o Gemini: vedi Testo e streaming |
Installalo
composer require spicyapi/spicyapiMetti la chiave nell’ambiente, così non comparirà mai nel tuo codice né nel controllo di versione:
export SPICY_API_KEY="sk-spicy-..."Il tuo primo risultato
Chiedi il prezzo, decidi, crea l’attività, aspetta, leggi i file. Il preventivo è gratuito e non accantona nulla.
use SpicyApi\SpicyClient;
$client = new SpicyClient(); // legge SPICY_API_KEY dall'ambiente
$quote = $client->quote('MODEL_ID_FROM_CATALOG', ['prompt' => 'a lantern in fog']);
printf("at most %s\n", $quote['maxCharge']); // decidi prima di spendere
$task = $client->createTask(
model: $quote['model'],
input: ['prompt' => 'a lantern in fog'],
idempotencyKey: SpicyClient::idempotencyKey(),
quoteId: $quote['quoteId'],
expectedCost: $quote['estimatedCost'],
);
$final = $client->waitForTerminal($task['taskId']);
foreach (SpicyClient::readyAssets($final) as $asset) {
echo $asset['url'], "\n";
}Ogni chiamata restituisce il campo data della struttura della risposta già decodificato, come semplice array associativo: quindi leggilo con $quote['maxCharge'], non con $quote->maxCharge.
MODEL_ID_FROM_CATALOG è un segnaposto. Prendi un ID reale dal catalogo autenticato — $client->listModels(), cioè GET /api/v1/models — e mai a memoria o da una pagina vecchia: i modelli vengono aggiunti, sospesi e ritirati. Costruisci l’input a partire dall’inputSchema di quel modello, che listModels() restituisce; ogni modello accetta campi diversi.
Gli importi sono stringhe decimali in dollari USA, mai numeri a virgola mobile, perché i float binari non riescono a contenere esattamente la maggior parte degli importi decimali. Confrontali con SpicyClient::compareUsd(). In questa API non esistono crediti né punti.
waitForTerminal ritorna a qualsiasi stato terminale, e uno stato terminale non è uno stato riuscito. Controlla lo stato prima di leggere l’output, e leggi prima SpicyClient::outputText($final) per gli endpoint che rispondono con del testo invece che con dei file.
Salta il primo polling
waitSeconds tiene aperta la connessione finché l’attività non è finita, così un lavoro breve non richiede alcun ciclo di polling. Non promette però un’attività conclusa. Se il budget si esaurisce, ricevi la normale risposta di accettazione — stessa forma, state diverso — quindi ramifica sullo stato, mai sul fatto di aver passato il parametro:
$submitted = $client->createTask(
model: 'MODEL_ID_FROM_CATALOG',
input: ['prompt' => 'a lantern in fog'],
idempotencyKey: SpicyClient::idempotencyKey(),
waitSeconds: 30,
);
if (!SpicyClient::isTerminal($submitted)) {
$submitted = $client->waitForTerminal($submitted['taskId']);
}Parti da un file locale
Da immagine a video, sostituzione del soggetto e modifica delle immagini hanno tutte bisogno che il tuo materiale arrivi prima dalla nostra parte. uploadFile esegue il caricamento in tre passaggi — ticket, PUT allo storage, commit — con una sola chiamata e restituisce un URI spicy://. È quell’URI che va nell’input.
$uploaded = $client->uploadFile('/path/to/reference.png');
$task = $client->createTask(
model: 'MODEL_ID_FROM_CATALOG',
input: ['image' => $uploaded['uri'], 'prompt' => 'slow dolly in'],
idempotencyKey: SpicyClient::idempotencyKey(),
);Un URL HTTPS raggiungibile pubblicamente può entrare direttamente nell’input e non richiede alcun caricamento. Le immagini sono accettate fino a 10 MiB, audio e video fino a 90 MiB; vedi Media.
Webhook
use SpicyApi\SpicyWebhook;
// Il segreto viene per primo. Gli altri argomenti sono facoltativi: $_SERVER e i byte
// grezzi di php://input vengono letti per te, a meno che un framework non li abbia gia' consumati.
$delivery = SpicyWebhook::verifyRequest(getenv('SPICY_WEBHOOK_SECRET') ?: '');
// Dentro un framework che bufferizza il corpo, passagli tu stesso i byte esatti:
$delivery = SpicyWebhook::verifyRequest(
secret: getenv('SPICY_WEBHOOK_SECRET') ?: '',
server: $_SERVER,
rawBody: $request->getContent(),
);Ci sono tre modi in cui il codice PHP sbaglia questo passaggio, e questa classe li evita tutti:
- Riserializzare il corpo prima di verificarlo. Un
json_decodeseguito dajson_encodemodifica i byte, e la firma non corrisponderà mai. Verifica i byte grezzi. - Confrontare le firme con
==o===. Al di là dei tempi di esecuzione,==su due stringhe che assomigliano entrambe a0e…le confronta come numeri:"0e123" == "0e456"ètrue. Qui si usahash_equals. - Saltare il controllo del timestamp. Una firma valida resta valida per sempre; senza una finestra di validità, una consegna intercettata può essere riprodotta.
Le consegne vengono ritentate, quindi deduplica sull’ID della consegna. Il formato completo delle consegne e il calendario dei nuovi tentativi sono in Webhook.
Saldo, consumi e storico
$balance = $client->getBalance(); // disponibile / accantonato / totale, stringhe in USD
$usage = $client->getUsage('2026-09-01', '2026-09-08'); // spesa liquidata nell'intervallo [from, to)
foreach ($client->iterTasks(state: 'succeeded') as $summary) {
echo $summary['taskId'], "\n"; // ogni pagina, con i filtri ripetuti per te
}held è il denaro accantonato da attività non ancora liquidate, quindi non è né speso né disponibile. Le date sono giorni di calendario UTC, YYYY-MM-DD, e formano un intervallo semiaperto [from, to) di al massimo 92 giorni. Tutte e tre le chiamate vedono soltanto la chiave API che chiama: le altre chiavi e le generazioni della console non sono incluse.
getUsage() serve alla riconciliazione, non a seguire l’avanzamento. Porta con sé un budget di frequenza valido per tutto l’account e condiviso da ogni chiave, e quel limitatore si chiude in caso di dubbio: interrogarlo di continuo può escludere l’intero account dai propri dati. Segui invece un’attività con getTask().
listTasks() restituisce una pagina come {items, hasMore, nextCursor}. Impaginare a mano significa ripetere ogni filtro senza cambiarlo, spostare soltanto il cursore e fermarsi quando hasMore è falso oppure nextCursor è vuoto: controllare solo hasMore trasforma un intoppo lato server in un ciclo infinito di chiamate a pagamento che sembra un blocco. iterTasks() fa tutto questo per te.
Errori
I fallimenti lanciano SpicyApiError, che porta con sé $businessCode, $requestId e $retryAfterSeconds. Ramifica sul codice applicativo, mai sul messaggio: i messaggi vengono tradotti, i codici no.
L’HTTP 503 è condiviso da tre codici applicativi diversi, quindi il solo stato non può dirti che cosa fare:
| Codice | Che cosa significa | Che cosa fare |
|---|---|---|
40003 | i byte caricati non corrispondono al loro ticket | ricomincia da un ticket nuovo |
40004 | nessuna istanza disponibile serve quella combinazione di parametri | cambia il parametro indicato nel messaggio |
40201 | saldo insufficiente | aggiungi fondi |
409 | la chiave di idempotenza è stata riusata per una richiesta diversa | reinvia la richiesta originale con quella chiave, oppure quella modificata con una chiave nuova |
40901 | il prezzo è cambiato prima che l’attività venisse creata | chiedi di nuovo il preventivo, mantenendo la stessa chiave di idempotenza |
503 | una dipendenza è momentaneamente non disponibile | aspetta il tempo indicato da Retry-After |
50301 | al momento non c’è né un’istanza utilizzabile né un prezzo | aggiorna il catalogo |
50302 | una generazione sincrona è fallita a monte ed è stata rimborsata | riprova con una nuova chiave di idempotenza |
$error->guidance() restituisce lo stesso consiglio a runtime. Tutti i codici sono elencati in Errori.
Due cose che ti faranno risparmiare
Tieni una sola chiave di idempotenza per ogni invio e riusala a ogni reinvio: una risposta persa non dimostra che l’attività non sia stata creata. Vedi Idempotenza.
Un’attività che riesce viene addebitata, anche se il risultato delude. quote() non accantona nulla e non crea nulla.
Che cosa questo pacchetto non fa
- I modelli di testo. Parlano i formati OpenAI, Anthropic e Gemini; i client PHP affermati per quei formati funzionano già su
https://api.spicyapi.ai/v1. Vedi Testo e streaming. - Il codice lato browser. La chiave sta sul tuo server, mai in qualcosa che spedisci a chi visita il sito.
- Scegliere un modello o compilarne i campi. Ti consegna il catalogo e lo schema di ogni modello; che cosa inviare lo decidi tu.
- Annullare un’attività. Una volta accettata, un’attività arriva fino in fondo.
Codice sorgente
Il pacchetto ha licenza MIT ed è sviluppato in modo aperto su github.com/spicyapi-ai/spicy-php. Il suo README è il giro più breve e completo del client, e examples/ contiene script pronti da eseguire; questa pagina aggiunge i concetti che ci stanno intorno.
Prossimi passi
Catalogo dei modelli e schemi
Come leggere l’inputSchema di un modello e scegliere input validi
Attività asincrone
Gli stati delle attività, il polling e il significato di ogni campo
Preventivi e compatibilità
Scopri il prezzo esatto di una richiesta prima di pagarlo
Webhook
Formato delle consegne, calendario dei nuovi tentativi e verifica della firma
Errori
Tutti i codici di errore e quali conviene riprovare
SDK Java
Lo stesso flusso di lavoro da Java, con una gerarchia sigillata di errori
SDK Go
L’SDK Go ufficiale di SpicyAPI (github.com/spicyapi-ai/spicy-go) — installalo, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, scorri lo storico delle attività e gestisci ogni codice di errore.
SDK Java
L’SDK Java ufficiale di SpicyAPI (ai.spicyapi spicyapi-java) — installalo, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, verifica le callback e gestisci ogni errore con uno switch esaustivo su una gerarchia sigillata.

