Vai al contenuto

Documentazionedalla chiave alla prima clip

Tutto quello che serve per una prima generazione: una chiave, una richiesta, il lavoro e il webhook.

L'accesso all'API per sviluppatori è su richiesta.

Panoramica

REST su HTTPS, JSON in ingresso e in uscita. Ogni generazione è asincrona.

URL di base
https://api.pershot.app/v1
Autenticazione
Authorization: Bearer seguito dalla tua chiave. Le chiavi iniziano con sk_test_ o sk_live_ e vengono mostrate una sola volta; noi conserviamo solo un hash.
Permessi delle chiavi
Ogni chiave ha degli ambiti e, se ne imposti una nello studio, una lista di modelli e strumenti consentiti. Una chiave con una lista consentita può usare solo ciò che c'è nella lista, strumenti inclusi: aumento di risoluzione delle immagini, rimozione dello sfondo, Migliora, sincronizzazione labiale, espansione dell'immagine e l'editor di immagini. Tutto il resto risponde 403 forbidden_model. 1080p o 4K con Migliora richiedono sia il modello video sia Migliora.
Formato degli errori
{ "error": { "code", "message", "param", "request_id" } }
ID
Con un prefisso per tipo: job_, qt_, key_, up_.
Per gli agenti IA
Testo semplice per gli agenti IA: input, valori ammessi, ogni prezzo e un esempio di richiestaseedance-2.0-fastseedance-2.0-miniqwen-image-2512-lightningqwen-image-2512qwen-image-edit-2511-lightningqwen-image-edit-2511

Le generazioni reali non sono ancora aperte. Lascia la tua email nella pagina di un modello e ti scriveremo una volta sola, quando apriranno.

Avvio rapido

Quattro passaggi, ognuno con la risposta che dovresti vedere.

  1. 01

    Ottieni una chiave

    L'accesso all'API per sviluppatori è su richiesta: chiedilo nello studio, in Chiavi API. Quando è aperto, crea lì una chiave e copiala subito: la chiave completa viene mostrata una sola volta.

    Shell
    export API_KEY="sk_test_…"
  2. 02

    Invia una richiesta

    Invia l'inquadratura in POST con una Idempotency-Key e un tetto max_credits. Ricevi subito 202 e un lavoro in coda, e i crediti vanno in riserva.

    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
      }'
    Dovresti vedere
    {
      "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

    Controlla il lavoro

    Fai GET del lavoro finché lo stato non è succeeded. L'URL del risultato è firmato e scade; scarica ciò che vuoi tenere.

    GET /jobs/{id}
    curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \
      -H "Authorization: Bearer $API_KEY"
    Dovresti vedere
    {
      "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

    Ricevi il webhook

    Passa webhook_url e inviamo in POST ogni cambio di stato, firmato con un timestamp. Rispondi 2xx; le consegne non riuscite vengono ritentate a intervalli crescenti.

    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
            }
          ]
        }
      }
    }

Endpoint

Tutti i percorsi sono relativi all'URL di base.

MetodoPercorsoCosa fa
GET/v1/modelsModelli, funzionalità e stato disponibili per il tuo account
GET/v1/pricingPrezzi pubblici; con l'accesso, i prezzi del tuo account
POST/v1/quotesPiano, voci, addebito massimo e scadenza (10 minuti)
POST/v1/videos/generationsGenera un video; 202 e un lavoro
POST/v1/images/generationsGenera o modifica un'immagine; 202 e un lavoro
GET/v1/jobs/{id}Stato, risultati come link firmati, costo effettivo
GET/v1/jobsI tuoi lavori, dal più recente
POST/v1/jobs/{id}/cancelChiede l'annullamento; i crediti riservati vengono rimborsati, a meno che il modello non abbia già accettato il lavoro
GET/v1/balanceCrediti disponibili e in riserva, per tipo di saldo
GET/v1/usageUtilizzo per giorno o modello, in JSON o CSV
POST/v1/uploadsCarica un JPEG, PNG o WebP fino a 10 MB

Preventivi e tetti

Ogni generazione richiede un quote_id oppure un tetto max_credits, mai entrambi. Con un quote_id il lavoro viene eseguito a quel preventivo; con max_credits viene eseguito solo se il prezzo è pari o inferiore al tetto.

model: "auto" richiede sempre max_credits. Un preventivo è valido per 10 minuti; dopo ricevi quote_expired.

Idempotenza

Invia un header Idempotency-Key (da 1 a 255 caratteri ASCII visibili) in ogni richiesta di generazione.

La stessa chiave entro 24 ore restituisce lo stesso lavoro con HTTP 200 invece di 202, quindi riprovare dopo un timeout non crea né addebita mai un secondo lavoro.

Webhook

Passa webhook_url in una generazione (o in un lotto) per riceverne i cambi di stato. Ogni evento è un POST il cui corpo JSON contiene id, type, created_at e data: data.job per gli eventi dei lavori, data.batch per batch.completed.

Eventi

  • job.queued
  • job.processing
  • job.succeeded
  • job.failed
  • job.cancelled
  • batch.completed
  • Ogni consegna ha un id evento, un timestamp e una firma. Verificala prima di fidarti del corpo.
  • Una consegna è valida quando il tuo endpoint risponde 2xx entro 10 secondi. Altrimenti viene ritentata dopo 1 min, 5 min, 30 min, 2 h e 6 h, poi segnata come non riuscita; l'assistenza può reinviarla. I reindirizzamenti non vengono seguiti.
  • Gli URL dei webhook devono essere https. Gli host su indirizzi privati, di loopback o riservati vengono rifiutati, sia quando li invii sia quando ci colleghiamo.

Header

X-Signature
t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>
X-Event-Id
L'id dell'evento, presente anche nel corpo. Nuovi tentativi e reinvii lo mantengono, quindi salvalo e salta un id che hai già gestito.
Segreto di firma
Un segreto whsec_… per account, nello studio sotto Chiavi API. La chiave HMAC è l'intero segreto, prefisso incluso.
Rotazione
Dopo la rotazione, il vecchio segreto continua a firmare per 24 ore come seconda voce v1. Accetta una consegna se una qualsiasi v1 corrisponde.

Calcola l'HMAC sul corpo grezzo, non su una copia riserializzata, confronta in tempo costante e rifiuta i timestamp più vecchi di cinque minuti.

Verifica 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.

Errori

Codici e stati HTTP sono fissi; il messaggio è per le persone.

CodiceHTTPQuando
invalid_request400Un campo manca o è fuori intervallo
unauthorized401Nessuna chiave, una chiave revocata o una firma non valida
insufficient_credits402Il saldo non copre la riserva
forbidden_model403La lista consentita della chiave non include questo modello o strumento
region_not_supported403Il modello non è offerto nella tua area
developer_api_closed403Una chiave sviluppatore mentre l'API per sviluppatori è chiusa; l'accesso è su richiesta
member_connections_paused403Un accesso da membro (MCP, codice di connessione o riga di comando) mentre il collegamento del tuo agente IA è in pausa
not_found404Nessun lavoro, preventivo o caricamento del genere nel tuo account
quote_expired409Il preventivo ha più di 10 minuti
price_changed409Il prezzo è salito sopra il tuo tetto
content_rejected422Il contenuto è stato rifiutato; non viene addebitato nulla
rate_limited429Troppe richieste per questa chiave
concurrency_limited429Troppi lavori in corso per questo account
internal_error500Qualcosa non ha funzionato da parte nostra
upstream_unavailable503Il modello non è disponibile; riprova più tardi

Un esito incerto del modello non è un errore: il lavoro riporta lo stato unknown e viene controllato prima di reinviare qualsiasi cosa.

Limiti

Richieste
60 al minuto per chiave, per impostazione predefinita.
Concorrenza
3 lavori alla volta per account, per impostazione predefinita; puoi chiedere di aumentarlo.
Tetto massimo
La capacità del modello è un tetto rigido. Più chiavi non aggiungono concorrenza.

Immagini

POST /v1/images/generations accetta gli stessi header. Scegli un id di modello per livello e operazione; i modelli di modifica richiedono anche image_url, e i modelli di generazione lo rifiutano.

Livellogenerazionemodifica
Image MiniEconomico · bozzaqwen-image-2512-lightningqwen-image-edit-2511-lightning
Image FastConsigliatoqwen-image-2512qwen-image-edit-2511

Limiti per modello

La pagina API di ogni modello ha la tabella completa: il valore, l'unità, chi lo controlla e cosa succede se lo superi. GET /v1/models restituisce gli stessi limiti in JSON.

  • ApplicatoLo controlliamo noi e rispondiamo 400 prima di qualsiasi preventivo o riserva.
  • FornitoreLo controlla il fornitore. Un lavoro che rifiuta non riesce, e non viene addebitato nulla.
  • ConsigliatoNessuno lo controlla. Restarci dentro dà risultati migliori.
ModelloLimiti principaliTabella completa
seedance-2.0-fastSeedance 2.0 FastPrompt da 2 a 2000 caratteri · da 4 a 15 s · fino a 9 immagini di riferimento · immagini da 300 a 6000 px per latoApri la tabella
seedance-2.0-miniSeedance 2.0 MiniPrompt da 2 a 2000 caratteri · da 4 a 15 s · fino a 9 immagini di riferimento · immagini da 300 a 6000 px per latoApri la tabella
qwen-image-2512Qwen-Image-2512Prompt da 2 a 2000 caratteri · 1K o 2K · nessuna immagine di partenzaApri la tabella
qwen-image-2512-lightningQwen-Image-2512 · Lightning 4-stepPrompt da 2 a 2000 caratteri · 1K o 2K · nessuna immagine di partenzaApri la tabella
qwen-image-edit-2511Qwen-Image-Edit-2511Prompt da 2 a 2000 caratteri · 1K o 2K · un'immagine di partenza, JPEG, PNG o WebP fino a 10 MBApri la tabella
qwen-image-edit-2511-lightningQwen-Image-Edit-2511 · LightningPrompt da 2 a 2000 caratteri · 1K o 2K · un'immagine di partenza, JPEG, PNG o WebP fino a 10 MBApri la tabella

Caricamenti

POST /v1/uploads legge il tipo dal file stesso. I link di caricamento durano 7 giorni.

FileFormatiDimensione massimaDurata massima
Immagine: primo fotogramma, riferimento o immagine da modificareJPEG, PNG, WebP10 MB–
Video: sincronizzazione labialeMP450 MB60 s
Audio: sincronizzazione labialeMP3, WAV, M4A10 MB60 s

Non scarichiamo mai immagini da altri host, quindi per quei link la dimensione la controlla il fornitore, e un lavoro che rifiuta non viene addebitato. Gli stessi limiti sono disponibili in JSON: GET /v1/models restituisce un oggetto limits per ogni modello.

Se arrivi da fal, Replicate o WaveSpeed

Cambiare l'URL di base e la chiave non basta: ogni richiesta ha bisogno anche dell'id del modello nel corpo, di un header Idempotency-Key e di un tetto di prezzo (max_credits o un quote_id). La guida alla migrazione fa corrispondere ogni campo per Seedance 2.0 e Qwen-Image ed elenca cosa non si trasferisce.