Aller au contenu

Documentationde la clé au premier clip

Tout ce qu’il faut pour une première génération : une clé, une requête, la tâche et le webhook.

L’accès à l’API développeur se fait sur demande.

Présentation

REST sur HTTPS, JSON en entrée et en sortie. Chaque génération est asynchrone.

URL de base
https://api.pershot.app/v1
Authentification
Authorization: Bearer suivi de votre clé. Les clés commencent par sk_test_ ou sk_live_ et ne sont affichées qu’une fois ; nous n’en stockons qu’un hachage.
Permissions des clés
Chaque clé a des permissions et, si vous en définissez une dans le studio, une liste autorisée de modèles et d’outils. Une clé avec une liste autorisée ne peut utiliser que ce qui y figure, outils compris : agrandissement d’image, suppression d’arrière-plan, Améliorer, synchronisation labiale, extension d’image et retouche d’image. Tout le reste répond 403 forbidden_model. Le 1080p ou la 4K avec Améliorer nécessite à la fois le modèle vidéo et Améliorer.
Format des erreurs
{ "error": { "code", "message", "param", "request_id" } }
Identifiants
Préfixés par type : job_, qt_, key_, up_.
Pour les agents IA
Texte brut pour les agents IA : entrées, valeurs admises, chaque prix et un exemple de requêteseedance-2.0-fastseedance-2.0-miniqwen-image-2512-lightningqwen-image-2512qwen-image-edit-2511-lightningqwen-image-edit-2511

Les vraies générations ne sont pas encore ouvertes. Laissez votre e-mail sur la page d’un modèle et nous vous écrirons une seule fois, à leur ouverture.

Démarrage rapide

Quatre étapes, chacune avec la réponse que vous devriez voir.

  1. 01

    Obtenez une clé

    L’accès à l’API développeur se fait sur demande : demandez-le dans le studio, sous Clés API. Une fois l’accès ouvert, créez-y une clé et copiez-la tout de suite : la clé complète n’est affichée qu’une fois.

    Shell
    export API_KEY="sk_test_…"
  2. 02

    Envoyez une requête

    Envoyez le plan en POST avec un Idempotency-Key et un plafond max_credits. Vous recevez aussitôt un 202 et une tâche en file, et les crédits sont réservés.

    POST /videos/generations
    curl https://api.pershot.app/v1/videos/generations \
      -H "Authorization: Bearer $API_KEY" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "seedance-2.0-fast",
        "prompt": "Perfume mist in hard side light. Slow push-in, black background, grain.",
        "duration": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "generate_audio": true,
        "max_credits": 34
      }'
    Vous devriez voir
    {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "queued",
      "money_status": "reserved",
      "queue_position": 2,
      "requested_model": "seedance-2.0-fast",
      "credits_quoted": 34,
      "credits_charged": null
    }
  3. 03

    Vérifiez la tâche

    Faites un GET sur la tâche jusqu’à ce que son statut soit succeeded. L’URL de sortie est signée et expire : téléchargez ce que vous gardez.

    GET /jobs/{id}
    curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \
      -H "Authorization: Bearer $API_KEY"
    Vous devriez voir
    {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "succeeded",
      "money_status": "settled",
      "requested_model": "seedance-2.0-fast",
      "resolved_model": "seedance-2.0-fast",
      "credits_quoted": 34,
      "credits_charged": 34,
      "outputs": [
        {
          "kind": "video",
          "url": "https://…/job_01J9ZK4T8W.mp4?sig=…",
          "width": 1280,
          "height": 720,
          "duration_s": 5
        }
      ],
      "error": null
    }
  4. 04

    Recevez le webhook

    Passez webhook_url et nous y envoyons en POST chaque changement d’état, signé avec un horodatage. Répondez 2xx ; les livraisons échouées sont relancées avec un délai croissant.

    POST → webhook_url
    {
      "id": "evt_01J9ZKB1QX",
      "type": "job.succeeded",
      "created_at": "2026-09-29T14:02:31Z",
      "data": {
        "job": {
          "id": "job_01J9ZK4T8W",
          "object": "job",
          "status": "succeeded",
          "money_status": "settled",
          "credits_charged": 34,
          "outputs": [
            {
              "kind": "video",
              "url": "https://…/job_01J9ZK4T8W.mp4?sig=…",
              "width": 1280,
              "height": 720,
              "duration_s": 5
            }
          ]
        }
      }
    }

Points d’accès

Tous les chemins sont relatifs à l’URL de base.

MéthodeCheminCe qu’il fait
GET/v1/modelsModèles, capacités et état disponibles pour votre compte
GET/v1/pricingPrix publics ; une fois connecté, les prix de votre compte
POST/v1/quotesPlanification, lignes, facturation maximale et expiration (10 minutes)
POST/v1/videos/generationsGénérer une vidéo ; 202 et une tâche
POST/v1/images/generationsGénérer ou retoucher une image ; 202 et une tâche
GET/v1/jobs/{id}Statut, sorties sous forme de liens signés, coût réel
GET/v1/jobsVos tâches, les plus récentes d’abord
POST/v1/jobs/{id}/cancelDemander l’annulation ; les crédits réservés sont remboursés sauf si le modèle a déjà accepté la tâche
GET/v1/balanceCrédits disponibles et réservés, par catégorie
GET/v1/usageUtilisation par jour ou par modèle, en JSON ou en CSV
POST/v1/uploadsImporter un JPEG, PNG ou WebP jusqu’à 10 Mo

Devis et plafonds

Chaque génération exige soit un quote_id, soit un plafond max_credits, jamais les deux. Avec un quote_id, la tâche s’exécute au prix du devis ; avec max_credits, elle ne s’exécute que si le prix est inférieur ou égal au plafond.

model: "auto" exige toujours max_credits. Un devis est valable 10 minutes ; ensuite, vous recevez quote_expired.

Idempotence

Envoyez un en-tête Idempotency-Key (1 à 255 caractères ASCII visibles) sur chaque requête de génération.

La même clé dans les 24 heures renvoie la même tâche avec HTTP 200 au lieu de 202 : une nouvelle tentative après un délai dépassé ne crée ni ne facture jamais une seconde tâche.

Webhooks

Passez webhook_url sur une génération (ou sur un lot) pour recevoir ses changements d’état. Chaque événement est un POST dont le corps JSON contient id, type, created_at et data : data.job pour les événements de tâche, data.batch pour batch.completed.

Événements

  • job.queued
  • job.processing
  • job.succeeded
  • job.failed
  • job.cancelled
  • batch.completed
  • Chaque livraison a un identifiant d’événement, un horodatage et une signature. Vérifiez-la avant de faire confiance au corps.
  • Une livraison compte quand votre point d’accès répond 2xx en moins de 10 secondes. Sinon, elle est relancée après 1 min, 5 min, 30 min, 2 h et 6 h, puis marquée échouée ; le support peut la relancer. Les redirections ne sont pas suivies.
  • Les URL de webhook doivent être en https. Les hôtes à adresse privée, de bouclage ou réservée sont refusés, à l’envoi comme à la connexion.

En-têtes

X-Signature
t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>
X-Event-Id
L’identifiant de l’événement, aussi présent dans le corps. Les nouvelles tentatives et les relances le conservent : stockez-le et ignorez un identifiant déjà traité.
Secret de signature
Un secret whsec_… par compte, dans le studio sous Clés API. La clé HMAC est le secret entier, préfixe compris.
Renouvellement
Après un renouvellement, l’ancien secret continue de signer pendant 24 heures, sous forme d’une seconde entrée v1. Acceptez une livraison si l’une des entrées v1 correspond.

Calculez le HMAC sur le corps brut, pas sur une copie resérialisée, comparez en temps constant, et refusez les horodatages de plus de cinq minutes.

Vérifier X-Signature
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received, before any JSON parsing.
export function verifyWebhook(header, rawBody, secret, toleranceSec = 300) {
  let t = null;
  const signatures = [];
  for (const item of (header ?? "").split(",")) {
    const [key, value] = item.split("=", 2);
    if (key === "t") t = Number(value);
    if (key === "v1") signatures.push(Buffer.from(value, "hex"));
  }
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return signatures.some((s) => s.length === expected.length && timingSafeEqual(s, expected));
}

// verifyWebhook(req.headers["x-signature"], rawBody, process.env.WEBHOOK_SECRET)
// Answer 2xx quickly, and skip an X-Event-Id you have already handled.

Erreurs

Les codes et les statuts HTTP sont fixes ; le message s’adresse aux humains.

CodeHTTPQuand
invalid_request400Un champ est manquant ou hors limites
unauthorized401Pas de clé, une clé révoquée ou une signature invalide
insufficient_credits402Le solde ne couvre pas la réservation
forbidden_model403La liste autorisée de la clé n’inclut pas ce modèle ou cet outil
region_not_supported403Le modèle n’est pas proposé dans votre région
developer_api_closed403Une clé développeur alors que l’API développeur est fermée ; l’accès se fait sur demande
member_connections_paused403Une connexion de membre (MCP, code de connexion ou ligne de commande) alors que la connexion de votre propre agent IA est en pause
not_found404Aucune tâche, aucun devis ni aucun import de ce nom dans votre compte
quote_expired409Le devis a plus de 10 minutes
price_changed409Le prix a dépassé votre plafond
content_rejected422Le contenu a été refusé ; rien n’est facturé
rate_limited429Trop de requêtes pour cette clé
concurrency_limited429Trop de tâches en cours pour ce compte
internal_error500Un problème est survenu de notre côté
upstream_unavailable503Le modèle est indisponible ; réessayez plus tard

Un résultat incertain du modèle n’est pas une erreur : la tâche indique le statut unknown et est vérifiée avant toute nouvelle soumission.

Limites

Requêtes
60 par minute et par clé par défaut.
Simultanéité
3 tâches à la fois par compte par défaut ; demandez à l’augmenter.
Plafond
La capacité du modèle est un plafond strict. Plus de clés n’ajoutent pas de simultanéité.

Images

POST /v1/images/generations prend les mêmes en-têtes. Choisissez un identifiant de modèle selon le niveau et l’opération ; les modèles de retouche exigent aussi image_url, que les modèles de génération refusent.

Niveaugénérationretouche
Image MiniÉconomique · brouillonqwen-image-2512-lightningqwen-image-edit-2511-lightning
Image FastRecommandéqwen-image-2512qwen-image-edit-2511

Limites par modèle

La page API de chaque modèle contient la table complète : la valeur, l’unité, qui la vérifie et ce qui se passe au-delà. GET /v1/models renvoie les mêmes limites en JSON.

  • AppliquéeNous la vérifions et répondons 400 avant tout devis ou toute réservation.
  • FournisseurLe fournisseur la vérifie. Une tâche qu’il refuse échoue, et rien n’est facturé.
  • RecommandéePersonne ne la vérifie. La respecter donne de meilleurs résultats.
ModèleLimites principalesTable complète
seedance-2.0-fastSeedance 2.0 FastPrompt de 2 à 2 000 caractères · 4 à 15 s · jusqu’à 9 images de référence · images de 300 à 6 000 px par côtéOuvrir la table
seedance-2.0-miniSeedance 2.0 MiniPrompt de 2 à 2 000 caractères · 4 à 15 s · jusqu’à 9 images de référence · images de 300 à 6 000 px par côtéOuvrir la table
qwen-image-2512Qwen-Image-2512Prompt de 2 à 2 000 caractères · 1K ou 2K · sans image sourceOuvrir la table
qwen-image-2512-lightningQwen-Image-2512 · Lightning 4-stepPrompt de 2 à 2 000 caractères · 1K ou 2K · sans image sourceOuvrir la table
qwen-image-edit-2511Qwen-Image-Edit-2511Prompt de 2 à 2 000 caractères · 1K ou 2K · une image source, JPEG, PNG ou WebP jusqu’à 10 MoOuvrir la table
qwen-image-edit-2511-lightningQwen-Image-Edit-2511 · LightningPrompt de 2 à 2 000 caractères · 1K ou 2K · une image source, JPEG, PNG ou WebP jusqu’à 10 MoOuvrir la table

Imports

POST /v1/uploads lit le type dans le fichier lui-même. Les liens d’import durent 7 jours.

FichierFormatsTaille maximaleDurée maximale
Image : première image, référence ou source de retoucheJPEG, PNG, WebP10 Mo–
Vidéo : synchronisation labialeMP450 Mo60 s
Audio : synchronisation labialeMP3, WAV, M4A10 Mo60 s

Nous ne récupérons jamais d’images sur d’autres hôtes : pour ces liens, c’est le fournisseur qui vérifie la taille, et une tâche qu’il refuse n’est pas facturée. Les mêmes limites existent en JSON : GET /v1/models renvoie un objet limits pour chaque modèle.

Vous venez de fal, Replicate ou WaveSpeed

Changer l’URL de base et la clé ne suffit pas : chaque requête a aussi besoin de l’identifiant du modèle dans le corps, d’un en-tête Idempotency-Key et d’un plafond de prix (max_credits ou un quote_id). Le guide de migration fait correspondre chaque champ pour Seedance 2.0 et Qwen-Image et liste ce qui ne se transpose pas.