PopNowDokumentacja API
Dokumentacja techniczna
Treść główna

Błędy

Każdy kod biznesowy, co oznacza i które błędy warto ponawiać.

Gdy wywołanie się nie uda, odpowiedź zawiera liczbę (code), która mówi, jaki to problem, oraz zdanie (msg) dla człowieka. Rozgałęziaj logikę według liczby; zdanie pokazuj albo zapisuj w logach.

Koperta

{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }
  • code to kod biznesowy. 200 oznacza sukces; pozostałe są wypisane niżej.
  • Przy błędzie nie ma pola data — wyjątkiem są niektóre błędy biblioteki zasobów, które dodają data.reason.
  • Status HTTP też jest ustawiany poprawnie (40201 przychodzi z HTTP 402). Opieraj się na jednym z nich, bez mieszania — polecamy dokładniejszy code.
  • request_id jest też w nagłówku X-Request-Id. Podaj go, pisząc do pomocy.

Jeśli odpowiedź w ogóle nie jest JSON-em — np. stroną HTML lub tekstową z sieci pośredniczącej — obsłuż ją według statusu HTTP i zapisz w logach początek treści.

Język komunikatów o błędach

msg i errorMessage nieudanego zadania są domyślnie po angielsku. Wyślij Accept-Language z wartością en, ja, ko, de, fr, es, pt-BR, ru, it lub pl albo ustaw język błędów API w konsoli, a dostaniesz je w tym języku; pozostałe języki wracają do angielskiego. Odpowiedzi z błędem mają nagłówek Content-Language. Kody nigdy nie zmieniają się z językiem — kolejny powód, by nigdy nie parsować msg.

Gdy model poda konkretny powód niepowodzenia (np. nieczytelny obraz referencyjny), errorMessage przekazuje go po angielsku, bez szczegółów wewnętrznych.

Kody ogólne

codeHTTPZnaczeniePonawiać?
200200 / 202Sukces—
400400Zapytanie lub jego input są niepoprawne; msg wskazuje problemNie — popraw
40003400Przesłany plik nie zgadza się z biletem (rozmiar, typ lub zawartość)Nie — nowy bilet, nowe przesłanie
40004400Zapytanie jest poprawne, ale model nie obsłuży teraz tej kombinacji ustawieńNie — zmień ustawienie wskazane w msg
401401Brak klucza, nieznany lub cofnięty (szczegóły)Nie
403403Brak uprawnień (zobacz Uprawnienia)Nie
404404Nie istnieje albo należy do innego kluczaNie
409409Konflikt z bieżącym stanem (kiedy ponawiać)Czasem
40901409Wycena wygasła albo cena się zmieniłaWyceń ponownie
413413Treść zapytania jest za dużaNie — prześlij media zamiast je osadzać
429429Za dużo zapytańTak — po Retry-After
500500Coś zepsuło się po naszej stronieTak — z rosnącą przerwą
503503Usługa pomocnicza jest chwilowo niedostępnaTak — po Retry-After

Saldo i limity

codeHTTPZnaczenieCo zrobić
40201402Za mało kredytów na blokadę tego zadaniaKup kredyty i wyślij ponownie
40202402Osiągnięto limit wydatków kluczaPodnieś go w konsoli API albo poczekaj na wyzerowanie

Uprawnienia

codeHTTPZnaczenieCo zrobić
40301403Ten klucz nie może wywoływać tego modeluDodaj model do listy dozwolonych dla klucza
40302403Zapytanie przyszło z adresu spoza listy dozwolonych IPDodaj adres albo usuń ograniczenie
40303403Usługa nie jest dostępna w tym regionieNie da się tego obejść
40310403Adres e-mail konta nie jest jeszcze potwierdzonyOtwórz link z maila rejestracyjnego; klucz zadziała od razu po potwierdzeniu

Zwykłe 403 przy ważnym kluczu oznacza, że konto nie może teraz korzystać z API; napisz do pomocy i podaj request_id.

Dostępność modeli

codeHTTPZnaczeniePonawiać?
50301503Model nie przyjmuje teraz zadańTak, później — albo wybierz inny model
40004400Model działa, ale nie z tymi ustawieniamiNie — zmień ustawienie

Żaden z nich nie tworzy zadania ani nie blokuje kredytów.

Kody niepowodzeń zadań

Zadanie przyjęte, które potem się nie powiodło, ma state: "failed" i jedną z tych wartości w errorCode. Lista jest zamknięta: każdą inną wartość traktuj jak upstream_failed.

errorCodeZnaczeniePonawiać?
invalid_requestModel nie przyjął parametrówNie — popraw je
unsupported_combinationTa kombinacja ustawień nie jest teraz obsługiwanaNie — zmień ustawienie
content_rejectedModel odrzucił tę treśćNie — zmień prompt albo materiał referencyjny
invalid_assetPliku referencyjnego nie da się odczytać albo nie jest już dostępnyPrześlij go ponownie i utwórz nowe zadanie
rate_limitedUsługa generowania była przeciążonaTak, z rosnącą przerwą
upstream_unavailableGenerowanie jest chwilowo niedostępneTak, za chwilę
generation_failedGenerowanie ruszyło, ale nic nie zwróciłoMoże — te same dane wejściowe często zawodzą tak samo
timeoutZadaniu skończył się czasMoże — spróbuj krótszego wyniku lub niższej rozdzielczości
upstream_failedWszystko inneTak, z rosnącą przerwą

Nieudane zadanie zwalnia blokadę, więc nic nie kosztuje (nieliczne modele pobierają opłatę za odmowę z powodu treści i piszą o tym na swojej stronie). errorMessage opisuje powód słowami; rozgałęziaj logikę tylko według errorCode.

Kiedy warto ponowić 409

SytuacjaPonawiać?
Dwa zapytania z tym samym Idempotency-Key przyszły jednocześnieTak, tym samym kluczem, po krótkiej przerwie
Idempotency-Key został użyty do innego zapytania albo przez inny kluczNie
Przesłany plik lub wynik jest jeszcze przetwarzanyTak, po kilku sekundach
jobs/retry dla zadania, które jeszcze się nie zakończyłoTak, gdy będzie w stanie końcowym
jobs/retry dla zadania zakończonego sukcesemNie — utwórz nowe zadanie
Bilet przesyłania albo przesłany plik wygasłNie — prześlij ponownie

Jak napisać ponowienie

Ponawiaj tylko to, co może się udać za drugim razem, czekaj na Retry-After, jeśli przyszedł, a w przeciwnym razie zwiększaj przerwę wykładniczo:

JavaScript
const RETRYABLE = new Set([429, 500, 503, 50301]);

async function call(path, init, attempts = 4) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(`https://api.popnow.ai/api/v1${path}`, init);
    if (!res.headers.get('content-type')?.includes('json')) {
      throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
    }
    const body = await res.json();
    if (body.code === 200) return body.data;
    if (!RETRYABLE.has(body.code)) throw new Error(`${body.code} ${body.msg} (${body.request_id})`);
    const wait = Number(res.headers.get('retry-after')) * 1000 || 2 ** i * 1000 + Math.random() * 500;
    await new Promise((r) => setTimeout(r, wait));
  }
  throw new Error('out of retries');
}
Python
import random, time, requests

RETRYABLE = {429, 500, 503, 50301}

def call(method, path, attempts=4, **kwargs):
    for i in range(attempts):
        res = requests.request(method, f"https://api.popnow.ai/api/v1{path}", timeout=60, **kwargs)
        try:
            body = res.json()
        except ValueError:
            raise RuntimeError(f"HTTP {res.status_code}: {res.text[:200]}")
        if body["code"] == 200:
            return body["data"]
        if body["code"] not in RETRYABLE:
            raise RuntimeError(f'{body["code"]} {body["msg"]} ({body.get("request_id")})')
        retry_after = res.headers.get("retry-after")
        time.sleep(float(retry_after) if retry_after else 2**i + random.random() / 2)
    raise RuntimeError("out of retries")

Ponawiając createTask, zawsze wysyłaj ten sam Idempotency-Key, żeby zapytanie, które w rzeczywistości się udało, nie zostało wykonane i rozliczone dwa razy.

Diagnozowanie

  1. Zapisz request_id i code.
  2. Znajdź zadanie w logach konsoli API po ID zadania lub request_id.
  3. Nadal utknąłeś? Napisz do pomocy, podając request_id, godzinę (UTC) i code. Nigdy nie wysyłaj swojego klucza API.

Na tej stronie