SDK PHP
Le SDK PHP officiel de SpicyAPI (spicyapi/spicyapi) — l’installer avec Composer, obtenir un devis, lancer votre première génération, téléverser un fichier local, vérifier les webhooks sans risque et traiter chaque code d’erreur.
spicyapi/spicyapi est le SDK PHP officiel de SpicyAPI. Il place la génération d’images, de vidéos et de sons derrière un seul SpicyClient : il joint votre clé API, déballe l’enveloppe de réponse en simples tableaux associatifs, attend une tâche avec une temporisation bornée, téléverse les fichiers locaux en un seul appel et vérifie les signatures de webhook sans commettre aucune des trois erreurs que le code PHP fait habituellement à cet endroit.
Il demande PHP 8.1 ou plus récent, plus ext-json et ext-hash, et n’a aucune dépendance de bibliothèque. ext-curl est utilisée lorsqu’elle est présente ; à défaut, le client se rabat sur le gestionnaire de flux https://.
Est-ce le bon outil ?
| Vous voulez… | Utilisez |
|---|---|
| Appeler SpicyAPI depuis votre propre code PHP | Ce SDK |
| L’appeler depuis Node.js ou TypeScript | Le SDK TypeScript |
| L’appeler depuis Python, Go ou Java | Le SDK Python, Go ou Java |
| Générer des contenus depuis un terminal sans écrire de code | La CLI |
| Laisser un assistant IA appeler SpicyAPI pour vous | Le serveur MCP et l’Agent Skill |
| Utiliser un modèle de chat ou de texte | Un client PHP établi pour les formats OpenAI, Anthropic ou Gemini ; voir Texte et streaming |
L’installer
composer require spicyapi/spicyapiPlacez votre clé dans l’environnement, pour qu’elle n’apparaisse jamais dans votre code ni dans votre gestionnaire de versions :
export SPICY_API_KEY="sk-spicy-..."Votre premier résultat
Demandez le prix, décidez, créez la tâche, attendez, lisez les fichiers. Le devis est gratuit et ne réserve rien.
use SpicyApi\SpicyClient;
$client = new SpicyClient(); // lit SPICY_API_KEY dans l’environnement
$quote = $client->quote('MODEL_ID_FROM_CATALOG', ['prompt' => 'a lantern in fog']);
printf("at most %s\n", $quote['maxCharge']); // décidez avant de dépenser
$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";
}Chaque appel renvoie le data décodé de l’enveloppe de réponse sous forme de simple tableau associatif : lisez-le donc avec $quote['maxCharge'], pas avec $quote->maxCharge.
MODEL_ID_FROM_CATALOG est un emplacement réservé. Prenez un vrai identifiant dans le catalogue authentifié — $client->listModels(), c’est-à-dire GET /api/v1/models — et jamais de mémoire ni d’une page ancienne : des modèles sont ajoutés, suspendus et retirés. Construisez l’entrée à partir de l’inputSchema propre à ce modèle, que renvoie listModels() ; chaque modèle accepte des champs différents.
Les montants sont des chaînes décimales en dollars américains, jamais des flottants, parce qu’un flottant binaire ne peut pas contenir exactement la plupart des montants décimaux. Comparez-les avec SpicyClient::compareUsd(). Il n’y a ni crédits ni points nulle part dans cette API.
waitForTerminal rend la main dès qu’un état final est atteint, et un état final n’est pas pour autant un succès. Vérifiez l’état avant de lire la sortie, et commencez par lire SpicyClient::outputText($final) pour les points de terminaison qui répondent par du texte plutôt que par des fichiers.
Sauter la première interrogation
waitSeconds garde la connexion ouverte jusqu’à la fin de la tâche : un travail court n’a donc besoin d’aucune boucle d’interrogation. Ce n’est pas la promesse d’une tâche terminée. Si le budget s’épuise, vous recevez à la place la réponse d’acceptation ordinaire — même forme, state différent : branchez donc sur l’état, jamais sur le fait d’avoir passé le paramètre.
$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']);
}Partir d’un fichier local
L’image vers vidéo, le remplacement de personne et la retouche d’image demandent tous que votre matière première soit d’abord chez nous. uploadFile réalise le téléversement en trois temps — ticket, PUT vers le stockage, validation — en un seul appel et renvoie une URI spicy://. C’est cette URI qui part dans l’entrée.
$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(),
);Une URL HTTPS publiquement joignable peut aller directement dans l’entrée, sans aucun téléversement. Les images sont acceptées jusqu’à 10 Mio, l’audio et la vidéo jusqu’à 90 Mio ; voir Médias.
Webhooks
use SpicyApi\SpicyWebhook;
// Le secret vient en premier. Les autres arguments sont facultatifs : $_SERVER et les
// octets bruts de php://input sont lus pour vous, sauf si un framework les a déjà consommés.
$delivery = SpicyWebhook::verifyRequest(getenv('SPICY_WEBHOOK_SECRET') ?: '');
// Dans un framework qui met le corps en tampon, passez-lui vous-même les octets exacts :
$delivery = SpicyWebhook::verifyRequest(
secret: getenv('SPICY_WEBHOOK_SECRET') ?: '',
server: $_SERVER,
rawBody: $request->getContent(),
);Trois façons pour du code PHP de se tromper ici, que cette classe évite toutes :
- Sérialiser à nouveau le corps avant de le vérifier. Un
json_decodesuivi d’unjson_encodemodifie les octets, et la signature ne correspondra plus jamais. Vérifiez les octets bruts. - Comparer les signatures avec
==ou===. Au-delà du temps d’exécution,==entre deux chaînes qui ressemblent toutes deux à0e…les compare comme des nombres :"0e123" == "0e456"vauttrue. Ici, c’esthash_equalsqui est utilisée. - Sauter la vérification de l’horodatage. Une signature valide le reste pour toujours ; sans fenêtre de fraîcheur, une livraison capturée peut être rejouée.
Les livraisons sont réessayées : dédoublonnez sur l’identifiant de livraison. Le format complet des livraisons et le calendrier des nouvelles tentatives figurent dans Webhooks.
Solde, consommation et historique
$balance = $client->getBalance(); // available / held / total, chaînes USD
$usage = $client->getUsage('2026-09-01', '2026-09-08'); // dépense réglée sur [from, to)
foreach ($client->iterTasks(state: 'succeeded') as $summary) {
echo $summary['taskId'], "\n"; // chaque page, filtres répétés pour vous
}held est l’argent mis de côté par des tâches qui ne sont pas encore réglées : il n’est donc ni dépensé ni disponible. Les dates sont des jours calendaires UTC, au format YYYY-MM-DD, formant un intervalle semi-ouvert [from, to) de 92 jours au maximum. Les trois appels ne voient que la clé API appelante : les clés sœurs et les générations lancées dans la console n’y figurent pas.
getUsage() sert au rapprochement comptable, pas au suivi d’avancement. Il porte un budget d’appels valable pour tout le compte et partagé par toutes les clés, et ce limiteur se ferme en cas de doute : l’interroger en boucle peut priver le compte entier de ses propres rapports. Suivez plutôt une tâche avec getTask().
listTasks() renvoie une page sous la forme {items, hasMore, nextCursor}. Paginer à la main suppose de répéter chaque filtre à l’identique et de ne faire bouger que le curseur, puis de s’arrêter quand hasMore vaut faux ou quand nextCursor est vide : ne tester que hasMore transforme un incident côté serveur en boucle sans fin d’appels payants, qui ressemble à un blocage. iterTasks() fait tout cela pour vous.
Erreurs
Les échecs lèvent une SpicyApiError qui porte $businessCode, $requestId et $retryAfterSeconds. Branchez sur le code métier, jamais sur le message : les messages sont traduits, les codes non.
Le statut HTTP 503 est partagé par trois codes métier différents : le statut seul ne peut donc pas vous dire quoi faire.
| Code | Signification | Que faire |
|---|---|---|
40003 | Les octets téléversés ne correspondent pas à leur ticket | Recommencez avec un ticket neuf |
40004 | Aucun déploiement ne sert cette combinaison de paramètres | Changez le paramètre nommé dans le message |
40201 | Solde insuffisant | Rechargez |
409 | La clé d’idempotence a été réutilisée pour une autre requête | Renvoyez la requête d’origine sous cette clé, ou la requête modifiée sous une clé neuve |
40901 | Le prix a bougé avant la création de la tâche | Redemandez un devis, en gardant la même clé d’idempotence |
503 | Une dépendance est brièvement indisponible | Patientez selon Retry-After |
50301 | Aucun déploiement ni prix utilisable pour l’instant | Actualisez le catalogue |
50302 | Une génération synchrone a échoué en amont et a été remboursée | Réessayez avec une nouvelle clé d’idempotence |
$error->guidance() renvoie les mêmes conseils à l’exécution. Tous les codes sont listés dans Erreurs.
Deux choses qui vous feront économiser
Gardez une clé d’idempotence par soumission, et réutilisez-la pour chaque renvoi : une réponse perdue ne prouve pas que la tâche n’a pas été créée. Voir Idempotence.
Une tâche qui réussit est facturée, même si le résultat déçoit. quote() ne réserve rien et ne crée rien.
Ce que ce paquet ne fait pas
- Les modèles de texte. Ils parlent les formats réseau d’OpenAI, d’Anthropic et de Gemini ; les clients PHP établis pour ces formats fonctionnent déjà avec
https://api.spicyapi.ai/v1. Voir Texte et streaming. - Le code côté navigateur. La clé reste sur votre serveur, jamais dans quoi que ce soit que vous envoyez à un visiteur.
- Choisir un modèle ou remplir ses champs. Il vous fournit le catalogue et le schéma de chaque modèle ; c’est vous qui décidez quoi envoyer.
- Annuler une tâche. Une fois acceptée, une tâche va jusqu’au bout.
Le code source
Le paquet est sous licence MIT et développé au grand jour sur github.com/spicyapi-ai/spicy-php. Son README est la visite guidée la plus courte et la plus complète du client, et examples/ contient des scripts prêts à exécuter ; cette page y ajoute les notions qui l’entourent.
Étapes suivantes
Catalogue des modèles et schémas
Lire l’inputSchema d’un modèle et choisir des entrées valides
Tâches asynchrones
États des tâches, polling et signification de chaque champ
Devis et compatibilité
Voir le prix exact d’une requête avant de la payer
Webhooks
Format des livraisons, calendrier des nouvelles tentatives et vérification des signatures
Erreurs
Chaque code d’erreur, et ceux qui valent la peine d’être retentés
SDK Java
Le même déroulé depuis Java, avec une taxonomie d’erreurs scellée
SDK Go
Le SDK Go officiel de SpicyAPI (github.com/spicyapi-ai/spicy-go) — l’installer, obtenir un devis, lancer votre première génération, téléverser un fichier local, parcourir l’historique de vos tâches et traiter chaque code d’erreur.
SDK Java
Le SDK Java officiel de SpicyAPI (ai.spicyapi spicyapi-java) — l’installer, obtenir un devis, lancer votre première génération, téléverser un fichier local, vérifier les callbacks et parcourir de façon exhaustive une taxonomie d’erreurs scellée.

