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_.
- OpenAPI
- /api/v1/openapi.json
- Per gli agenti IA
- Testo semplice per gli agenti IA: input, valori ammessi, ogni prezzo e un esempio di richiesta
seedance-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.
- 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.
export API_KEY="sk_test_…" - 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.
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 }'{ "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 } - 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.
curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \ -H "Authorization: Bearer $API_KEY"{ "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 } - 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.
{ "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.
| Metodo | Percorso | Cosa fa |
|---|---|---|
| GET | /v1/models | Modelli, funzionalità e stato disponibili per il tuo account |
| GET | /v1/pricing | Prezzi pubblici; con l'accesso, i prezzi del tuo account |
| POST | /v1/quotes | Piano, voci, addebito massimo e scadenza (10 minuti) |
| POST | /v1/videos/generations | Genera un video; 202 e un lavoro |
| POST | /v1/images/generations | Genera o modifica un'immagine; 202 e un lavoro |
| GET | /v1/jobs/{id} | Stato, risultati come link firmati, costo effettivo |
| GET | /v1/jobs | I tuoi lavori, dal più recente |
| POST | /v1/jobs/{id}/cancel | Chiede l'annullamento; i crediti riservati vengono rimborsati, a meno che il modello non abbia già accettato il lavoro |
| GET | /v1/balance | Crediti disponibili e in riserva, per tipo di saldo |
| GET | /v1/usage | Utilizzo per giorno o modello, in JSON o CSV |
| POST | /v1/uploads | Carica 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.queuedjob.processingjob.succeededjob.failedjob.cancelledbatch.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-Signaturet=<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.
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.
| Codice | HTTP | Quando |
|---|---|---|
invalid_request | 400 | Un campo manca o è fuori intervallo |
unauthorized | 401 | Nessuna chiave, una chiave revocata o una firma non valida |
insufficient_credits | 402 | Il saldo non copre la riserva |
forbidden_model | 403 | La lista consentita della chiave non include questo modello o strumento |
region_not_supported | 403 | Il modello non è offerto nella tua area |
developer_api_closed | 403 | Una chiave sviluppatore mentre l'API per sviluppatori è chiusa; l'accesso è su richiesta |
member_connections_paused | 403 | Un accesso da membro (MCP, codice di connessione o riga di comando) mentre il collegamento del tuo agente IA è in pausa |
not_found | 404 | Nessun lavoro, preventivo o caricamento del genere nel tuo account |
quote_expired | 409 | Il preventivo ha più di 10 minuti |
price_changed | 409 | Il prezzo è salito sopra il tuo tetto |
content_rejected | 422 | Il contenuto è stato rifiutato; non viene addebitato nulla |
rate_limited | 429 | Troppe richieste per questa chiave |
concurrency_limited | 429 | Troppi lavori in corso per questo account |
internal_error | 500 | Qualcosa non ha funzionato da parte nostra |
upstream_unavailable | 503 | Il 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.
| Livello | generazione | modifica |
|---|---|---|
| Image MiniEconomico · bozza | qwen-image-2512-lightning | qwen-image-edit-2511-lightning |
| Image FastConsigliato | qwen-image-2512 | qwen-image-edit-2511 |
Un riepilogo in testo semplice per gli agenti si trova in /llms.txt.
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.
| Modello | Limiti principali | Tabella completa |
|---|---|---|
seedance-2.0-fastSeedance 2.0 Fast | Prompt da 2 a 2000 caratteri · da 4 a 15 s · fino a 9 immagini di riferimento · immagini da 300 a 6000 px per lato | Apri la tabella |
seedance-2.0-miniSeedance 2.0 Mini | Prompt da 2 a 2000 caratteri · da 4 a 15 s · fino a 9 immagini di riferimento · immagini da 300 a 6000 px per lato | Apri la tabella |
qwen-image-2512Qwen-Image-2512 | Prompt da 2 a 2000 caratteri · 1K o 2K · nessuna immagine di partenza | Apri la tabella |
qwen-image-2512-lightningQwen-Image-2512 · Lightning 4-step | Prompt da 2 a 2000 caratteri · 1K o 2K · nessuna immagine di partenza | Apri la tabella |
qwen-image-edit-2511Qwen-Image-Edit-2511 | Prompt da 2 a 2000 caratteri · 1K o 2K · un'immagine di partenza, JPEG, PNG o WebP fino a 10 MB | Apri la tabella |
qwen-image-edit-2511-lightningQwen-Image-Edit-2511 · Lightning | Prompt da 2 a 2000 caratteri · 1K o 2K · un'immagine di partenza, JPEG, PNG o WebP fino a 10 MB | Apri la tabella |
Caricamenti
POST /v1/uploads legge il tipo dal file stesso. I link di caricamento durano 7 giorni.
| File | Formati | Dimensione massima | Durata massima |
|---|---|---|---|
| Immagine: primo fotogramma, riferimento o immagine da modificare | JPEG, PNG, WebP | 10 MB | – |
| Video: sincronizzazione labiale | MP4 | 50 MB | 60 s |
| Audio: sincronizzazione labiale | MP3, WAV, M4A | 10 MB | 60 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.