Zum Inhalt springen

Dokumentationvom Schlüssel zum ersten Clip

Alles für eine erste Generierung: ein Schlüssel, eine Anfrage, der Auftrag und der Webhook.

Zugang zur Entwickler-API gibt es auf Antrag.

Überblick

REST über HTTPS, JSON rein und raus. Jede Generierung läuft asynchron.

Basis-URL
https://api.pershot.app/v1
Authentifizierung
Authorization: Bearer, gefolgt von deinem Schlüssel. Schlüssel beginnen mit sk_test_ oder sk_live_ und werden nur einmal angezeigt; wir speichern nur einen Hash.
Schlüsselberechtigungen
Jeder Schlüssel hat Scopes und, wenn du im Studio eine festlegst, eine Positivliste mit Modellen und Tools. Ein Schlüssel mit Positivliste kann nur nutzen, was darauf steht, Tools eingeschlossen: Bild-Upscaling, Hintergrundentfernung, Verbessern, Lippensynchronisation, Outpainting und den Bildeditor. Alles andere antwortet mit 403 forbidden_model. 1080p oder 4K mit Verbessern braucht sowohl das Videomodell als auch Verbessern.
Fehlerformat
{ "error": { "code", "message", "param", "request_id" } }
IDs
Mit Präfix nach Typ: job_, qt_, key_, up_.
Für KI-Agenten
Klartext für KI-Agenten: Eingaben, zulässige Werte, jeder Preis und ein Anfragebeispielseedance-2.0-fastseedance-2.0-miniqwen-image-2512-lightningqwen-image-2512qwen-image-edit-2511-lightningqwen-image-edit-2511

Echte Generierungen sind noch nicht verfügbar. Hinterlass deine E-Mail-Adresse auf einer Modellseite, dann schreiben wir dir einmal, sobald sie verfügbar sind.

Schnellstart

Vier Schritte, jeweils mit der Antwort, die du sehen solltest.

  1. 01

    Schlüssel bekommen

    Zugang zur Entwickler-API gibt es auf Antrag: Beantrage ihn im Studio unter API-Schlüssel. Sobald er freigeschaltet ist, leg dort einen Schlüssel an und kopiere ihn sofort: Der vollständige Schlüssel wird nur einmal angezeigt.

    Shell
    export API_KEY="sk_test_…"
  2. 02

    Anfrage senden

    Sende die Einstellung per POST mit einem Idempotency-Key und einer max_credits-Obergrenze. Du bekommst sofort 202 und einen Auftrag in der Warteschlange, und die Credits werden reserviert.

    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
      }'
    Das solltest du sehen
    {
      "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

    Auftrag prüfen

    Frag den Auftrag per GET ab, bis sein Status succeeded ist. Die Ausgabe-URL ist signiert und läuft ab; lade herunter, was du behalten willst.

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

    Webhook empfangen

    Übergib webhook_url, dann senden wir jede Statusänderung per POST dorthin, signiert mit einem Zeitstempel. Antworte mit 2xx; fehlgeschlagene Zustellungen werden in wachsenden Abständen wiederholt.

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

Endpunkte

Alle Pfade sind relativ zur Basis-URL.

MethodePfadFunktion
GET/v1/modelsModelle, Fähigkeiten und Status, die deinem Konto zur Verfügung stehen
GET/v1/pricingÖffentliche Preise; angemeldet die Preise deines Kontos
POST/v1/quotesPlan, Positionen, maximale Abbuchung und Ablauf (10 Minuten)
POST/v1/videos/generationsVideo generieren; 202 und ein Auftrag
POST/v1/images/generationsBild generieren oder bearbeiten; 202 und ein Auftrag
GET/v1/jobs/{id}Status, Ausgaben als signierte Links, tatsächliche Kosten
GET/v1/jobsDeine Aufträge, neueste zuerst
POST/v1/jobs/{id}/cancelAbbruch anfordern; die reservierten Credits werden zurückgebucht, außer das Modell hat den Auftrag schon angenommen
GET/v1/balanceVerfügbare und reservierte Credits, nach Guthabenart
GET/v1/usageNutzung nach Tag oder Modell, als JSON oder CSV
POST/v1/uploadsJPEG, PNG oder WebP bis 10 MB hochladen

Preisangebote und Obergrenzen

Jede Generierung braucht entweder eine quote_id oder eine max_credits-Obergrenze, nie beides. Mit quote_id läuft der Auftrag zu diesem Preisangebot; mit max_credits läuft er nur, wenn der Preis die Obergrenze nicht überschreitet.

model: "auto" braucht immer max_credits. Ein Preisangebot gilt 10 Minuten; danach bekommst du quote_expired.

Idempotenz

Sende bei jeder Generierungsanfrage einen Idempotency-Key-Header (1 bis 255 sichtbare ASCII-Zeichen).

Derselbe Schlüssel innerhalb von 24 Stunden liefert denselben Auftrag mit HTTP 200 statt 202, sodass eine Wiederholung nach einem Timeout nie einen zweiten Auftrag anlegt oder berechnet.

Webhooks

Übergib webhook_url bei einer Generierung (oder bei einem Stapel), um die Statusänderungen zu erhalten. Jedes Ereignis ist ein POST, dessen JSON-Body id, type, created_at und data enthält: data.job für Auftragsereignisse, data.batch für batch.completed.

Ereignisse

  • job.queued
  • job.processing
  • job.succeeded
  • job.failed
  • job.cancelled
  • batch.completed
  • Jede Zustellung hat eine Ereignis-ID, einen Zeitstempel und eine Signatur. Prüfe sie, bevor du dem Body vertraust.
  • Eine Zustellung gilt als erfolgreich, wenn dein Endpunkt innerhalb von 10 Sekunden mit 2xx antwortet. Sonst wird sie nach 1 min, 5 min, 30 min, 2 h und 6 h wiederholt und dann als fehlgeschlagen markiert; der Support kann sie erneut senden. Weiterleitungen werden nicht verfolgt.
  • Webhook-URLs müssen https verwenden. Hosts mit privaten, Loopback- oder reservierten Adressen werden abgelehnt, sowohl beim Einreichen als auch beim Verbinden.

Header

X-Signature
t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>
X-Event-Id
Die Ereignis-ID, auch im Body. Wiederholungen und erneute Zustellungen behalten sie bei, also speichere sie und überspringe IDs, die du schon verarbeitet hast.
Signaturgeheimnis
Ein whsec_…-Geheimnis pro Konto, im Studio unter API-Schlüssel. Der HMAC-Schlüssel ist das ganze Geheimnis, inklusive Präfix.
Rotation
Nach dem Rotieren signiert das alte Geheimnis noch 24 Stunden lang als zweiter v1-Eintrag. Akzeptiere eine Zustellung, wenn irgendein v1 passt.

Berechne den HMAC über den Roh-Body, nicht über eine neu serialisierte Kopie, vergleiche in konstanter Zeit und lehne Zeitstempel ab, die älter als fünf Minuten sind.

X-Signature prüfen
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.

Fehler

Codes und HTTP-Status sind fest; die Meldung ist für Menschen gedacht.

CodeHTTPWann
invalid_request400Ein Feld fehlt oder liegt außerhalb des zulässigen Bereichs
unauthorized401Kein Schlüssel, ein widerrufener Schlüssel oder eine ungültige Signatur
insufficient_credits402Das Guthaben deckt die Reservierung nicht
forbidden_model403Die Positivliste des Schlüssels enthält dieses Modell oder Tool nicht
region_not_supported403Das Modell wird in deiner Region nicht angeboten
developer_api_closed403Ein Entwicklerschlüssel, solange die Entwickler-API geschlossen ist; Zugang auf Antrag
member_connections_paused403Eine Mitglieder-Anmeldung (MCP, Verbindungscode oder Kommandozeile), solange die Verbindung eigener KI-Agenten pausiert ist
not_found404Dieser Auftrag, dieses Preisangebot oder dieser Upload existiert in deinem Konto nicht
quote_expired409Das Preisangebot ist älter als 10 Minuten
price_changed409Der Preis ist über deine Obergrenze gestiegen
content_rejected422Der Inhalt wurde abgelehnt; es wird nichts berechnet
rate_limited429Zu viele Anfragen für diesen Schlüssel
concurrency_limited429Zu viele laufende Aufträge für dieses Konto
internal_error500Bei uns ist etwas schiefgelaufen
upstream_unavailable503Das Modell ist nicht verfügbar; versuch es später erneut

Ein unklares Ergebnis des Modells ist kein Fehler: Der Auftrag meldet den Status unknown und wird geprüft, bevor etwas erneut eingereicht wird.

Limits

Anfragen
Standardmäßig 60 pro Minute und Schlüssel.
Parallelität
Standardmäßig 3 Aufträge gleichzeitig pro Konto; frag nach, wenn du mehr brauchst.
Harte Grenze
Die Kapazität des Modells ist eine harte Grenze. Mehr Schlüssel erhöhen die Parallelität nicht.

Bilder

POST /v1/images/generations nimmt dieselben Header an. Wähle eine Modell-ID nach Stufe und Vorgang; Bearbeitungsmodelle brauchen zusätzlich image_url, Generierungsmodelle lehnen es ab.

StufeGenerierenBearbeiten
Image MiniGünstig · Entwurfqwen-image-2512-lightningqwen-image-edit-2511-lightning
Image FastEmpfohlenqwen-image-2512qwen-image-edit-2511

Limits nach Modell

Die API-Seite jedes Modells enthält die vollständige Tabelle: Wert, Einheit, wer prüft und was bei Überschreitung passiert. GET /v1/models liefert dieselben Limits als JSON.

  • ErzwungenWir prüfen es und antworten mit 400, bevor ein Preisangebot erstellt oder etwas reserviert wird.
  • Beim LieferantenDer Anbieter prüft es. Ein Auftrag, den er ablehnt, schlägt fehl, und es wird nichts berechnet.
  • EmpfohlenNiemand prüft es. Wenn du darunter bleibst, bekommst du bessere Ergebnisse.
ModellWichtigste LimitsVollständige Tabelle
seedance-2.0-fastSeedance 2.0 FastPrompt 2 bis 2.000 Zeichen · 4 bis 15 s · bis zu 9 Referenzbilder · Bilder 300 bis 6.000 px pro SeiteTabelle öffnen
seedance-2.0-miniSeedance 2.0 MiniPrompt 2 bis 2.000 Zeichen · 4 bis 15 s · bis zu 9 Referenzbilder · Bilder 300 bis 6.000 px pro SeiteTabelle öffnen
qwen-image-2512Qwen-Image-2512Prompt 2 bis 2.000 Zeichen · 1K oder 2K · kein AusgangsbildTabelle öffnen
qwen-image-2512-lightningQwen-Image-2512 · Lightning 4-stepPrompt 2 bis 2.000 Zeichen · 1K oder 2K · kein AusgangsbildTabelle öffnen
qwen-image-edit-2511Qwen-Image-Edit-2511Prompt 2 bis 2.000 Zeichen · 1K oder 2K · ein Ausgangsbild, JPEG, PNG oder WebP bis 10 MBTabelle öffnen
qwen-image-edit-2511-lightningQwen-Image-Edit-2511 · LightningPrompt 2 bis 2.000 Zeichen · 1K oder 2K · ein Ausgangsbild, JPEG, PNG oder WebP bis 10 MBTabelle öffnen

Uploads

POST /v1/uploads liest den Typ aus der Datei selbst. Upload-Links sind 7 Tage gültig.

DateiFormateGrößte DateiHöchstdauer
Bild: Startbild, Referenz oder Ausgangsbild zum BearbeitenJPEG, PNG, WebP10 MB–
Video: LippensynchronisationMP450 MB60 s
Audio: LippensynchronisationMP3, WAV, M4A10 MB60 s

Wir laden nie Bilder von anderen Hosts, daher prüft bei solchen Links der Anbieter die Größe, und ein Auftrag, den er ablehnt, wird nicht berechnet. Dieselben Limits gibt es auch als JSON: GET /v1/models liefert für jedes Modell ein limits-Objekt.

Umstieg von fal, Replicate oder WaveSpeed

Basis-URL und Schlüssel zu ändern reicht nicht: Jede Anfrage braucht außerdem die Modell-ID im Body, einen Idempotency-Key-Header und eine Preisobergrenze (max_credits oder eine quote_id). Der Leitfaden zum Umstieg ordnet jedes Feld für Seedance 2.0 und Qwen-Image zu und listet auf, was sich nicht übernehmen lässt.