Katalog modeli i schematy
Korzystaj z uwierzytelnionego katalogu na żywo, aby wybierać dostępne modele, odczytywać ich ceny i budować żądania według aktualnego JSON Schema.
createTask.model przyjmuje dokładną wartość model zwróconą w uwierzytelnionej odpowiedzi GET /api/v1/models. Jego input definiuje aktualne inputSchema tej samej pozycji. Ta odpowiedź otrzymana podczas działania aplikacji jest jedynym źródłem prawdy o identyfikatorze dostępnym do wywołania.
Nie kopiuj identyfikatora ze statycznej strony
Migawki z momentu premiery, strony rodzin modeli i katalogi twórców modeli pomagają odkrywać ofertę. Mogą zawierać pozycje planowane, wyłączone lub przemianowane i nigdy nie mogą być źródłem zastępczym, gdy katalog na żywo jest pusty. Przed płatną operacją pobierz uwierzytelniony katalog i wymagaj zarówno enabled: true, jak i available: true.
W przykładach na tej stronie MODEL_ID_FROM_CATALOG jest symbolem zastępczym, a nie prawdziwym modelem: zastąp go wartością model z własnego katalogu na żywo. Odczyt katalogu jest bezpłatny.
Wybierz model i jego zwalidowane dane wejściowe
Schematy żądań różnią się między modelami, więc bezpieczny sposób uruchomienia wybiera razem dostępny model i jeden z jego przykładów zwalidowanych przez serwer:
const response = await fetch(
'https://api.spicyapi.ai/api/v1/models?includeSchema=1&includeExamples=1',
{ headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` } },
);
const envelope = await response.json();
if (!response.ok || envelope.code !== 200) throw new Error(envelope.msg);
const selected = envelope.data.items.find(
(item) => item.enabled && item.available && item.examples?.length,
);
if (!selected) throw new Error('No callable model with a validated example');
const createTaskBody = {
model: selected.model,
input: selected.examples[0].input,
};Każdy przykład to kompletne input, które serwer już sprawdził względem aktualnego schematu, więc można wysłać je bez zmian.
Odczytuj katalog programowo
GET /api/v1/models
GET /api/v1/models/{model}curl "https://api.spicyapi.ai/api/v1/models?modality=video" \
-H "Authorization: Bearer $SPICY_API_KEY"Lista nie ma paginacji: jedna odpowiedź zawiera wszystkie modele widoczne dla twojego klucza i pasujące do filtrów. Wszystkie parametry są opcjonalne.
| Parametr | Znaczenie |
|---|---|
modality | image, video, audio lub text |
provider | Twórca modelu, dopasowywany dokładnie z uwzględnieniem wielkości liter, na przykład ByteDance. Skopiuj go z provider danej pozycji |
task | Jedno zadanie, na przykład text-to-image, edit (edycja obrazów), image-to-video, text-to-video, reference-to-video, speech-to-text, video-analyze, video-edit, video-extend, character-animation lub chat. Aktualny zestaw odczytaj z tasks w katalogu. Edycja obrazów ma dwa akceptowane zapisy: modele są publikowane jako .../edit, ale tasks klasyfikuje je jako image-to-image, a filtr przyjmuje oba |
search | Tekst dopasowywany bez uwzględniania wielkości liter w dowolnym miejscu identyfikatora lub nazwy wyświetlanej modelu, do 128 znaków |
includeSchema | true lub 1 dodaje inputSchema każdej pozycji |
includeExamples | true lub 1 dodaje examples każdej pozycji, zwalidowane względem aktualnego schematu |
Endpoint pojedynczego modelu, GET /api/v1/models/{model}, zawsze zawiera inputSchema i examples; includeSchema i includeExamples mają znaczenie tylko dla listy. Ukośniki w identyfikatorze mogą pozostać bez zmian; koduj je procentowo tylko wtedy, gdy klient HTTP traktuje parametr ścieżki jako pojedynczy segment. Model, który nie istnieje lub nie jest obecnie oferowany, zwraca 404.
Skrócona odpowiedź listy:
{
"code": 200,
"msg": "success",
"data": {
"total": 1,
"items": [
{
"model": "MODEL_ID_FROM_CATALOG",
"family": "…",
"displayName": "…",
"provider": "…",
"modality": "video",
"tasks": ["image-to-video"],
"async": true,
"enabled": true,
"available": true,
"availability": "available",
"taskTimeoutSeconds": 1200,
"maxOutputDurationSeconds": 10,
"quantityField": "duration_seconds",
"pricing": [
{ "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
{ "variant": "generate_audio=true;resolution=720p", "unit": "per_second", "price": "0.08", "currency": "USD" }
],
"startingPrice": { "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
"badges": ["audio_output"],
"relatedModels": [],
"version": "v1",
"updatedAt": "2026-09-16T16:47:57Z"
}
]
},
"request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}Powyższe ceny są przykładowe. inputSchema i examples pojawiają się w pozycjach listy tylko wtedy, gdy o nie poprosisz.
Pola, z których powinien korzystać klient
| Pole | Przeznaczenie |
|---|---|
model | Dokładna wartość przesyłana jako createTask.model, w postaci publisher/model/task |
family | Stabilny identyfikator rodziny produktu do grupowania powiązanych endpointów |
displayName | Czytelna dla człowieka nazwa tego endpointu |
provider | Twórca modelu, na przykład laboratorium, które go zbudowało |
modality / tasks | Rodzaj mediów, pod którym endpoint jest grupowany w katalogu, oraz wykonywane zadanie. modality steruje przeglądaniem i filtrem modality; nie gwarantuje rodzaju pliku wynikowego — endpoint speech-to-text jest grupowany pod audio, a odpowiada tekstem. Zobacz Struktura wyniku |
toolkit | Rodzina narzędzi, do której należy endpoint, obecnie tylko subject-swap. Pole jest nieobecne, gdy model nie należy do zestawu narzędzi, więc sprawdzaj wartość, a nie sam klucz. Podobnie jak badges jest klasyfikacją i nigdy nie wpływa na przyjęcie żądania, cenę ani walidację. Zobacz Podmiana obiektu |
async | true: utwórz zadanie i odbierz wynik przez odpytywanie lub webhook. false: odpowiedź wraca bezpośrednio w odpowiedzi na żądanie |
inputSchema | JSON Schema 2020-12 używany do generowania formularzy i walidacji żądań |
examples | Dane wejściowe zwalidowane względem aktualnego schematu; na liście zwracane tylko na żądanie |
enabled | Czy pozycję można teraz wywołać. Katalog zawiera też pozycje opublikowane, ale jeszcze nieudostępnione, oraz pozycje wstrzymane przez platformę; dla nich zwracane jest false. Sprawdzaj je razem z available przed wysłaniem zadania |
available | Czy model można teraz wywołać. false oznacza, że wywołanie się nie powiedzie, na przykład z powodu braku aktualnej ceny |
availability | Deklarowany etap: planned, available, preview (pola i wyniki mogą się jeszcze zmieniać) lub maintenance (przejściowe problemy, przewidywany powrót) |
pricing / startingPrice | Twoje ceny; zobacz Odczytywanie cen |
quantityField | Pole wejściowe, którego wartość stanowi rozliczaną ilość, zwykle duration_seconds dla wideo. Pusta wartość oznacza, że każde wywołanie liczy się jako jedna jednostka. Zawsze odczytuj je z katalogu |
taskTimeoutSeconds | Termin wykonania jednego zadania ustalony przez platformę. To nie długość mediów |
maxOutputDurationSeconds | Najdłuższy wynik możliwy do wygenerowania przez model, pobierany z maximum jego właściwości duration_seconds; pole jest pomijane, gdy nie zadeklarowano maksimum |
badges | Oznaczenia możliwości i licencji, takie jak commercial_use lub no_watermark. Ignoruj nierozpoznane wartości |
relatedModels | Ręcznie wybrane alternatywy lub kolejne kroki, jako identyfikatory modeli dostępnych do wywołania |
version / updatedAt | Śledzenie zmian: porównaj je, aby ustalić, czy kolejny odczyt przyniósł zmianę |
mature i policyTier to oznaczenia informacyjne. Nie uczestniczą w decyzji o przyjęciu żądania.
Odczytywanie cen
Każda pozycja w pricing oznacza jeden poziom ceny:
| Pole | Znaczenie |
|---|---|
unit | per_image, per_second (wynikowych mediów), per_request lub per_1k_tokens |
price | Twoja cena jednostkowa w dolarach amerykańskich jako ciąg dziesiętny. Uwzględnia już stawki konta, które mogą różnić się między kontami |
variant | Ustawienia wybierające ten poziom ceny. Puste, gdy model ma jedną cenę; sama wartość (na przykład 720p), gdy poziom wybiera jedno pole; pary field=value połączone ; i posortowane według nazwy pola (na przykład generate_audio=false;resolution=480p), gdy wybiera go kilka pól |
currency | Zawsze USD |
regularPrice, offerLabel, offerPercent, offerEndsAt | Obecne tylko podczas oferty dla klienta: cena przed ofertą, nazwa oferty, jej procent i termin zakończenia |
startingPrice to najtańsza pozycja, a nie cena twoich ustawień. Pola wybierające poziom ceny są wymienione w inputSchema["x-pricing"].variantFields, jeśli to pole istnieje; w przeciwnym razie są to pola oznaczone x-ui.affects_price: true (z wyjątkiem quantityField). Pominięte pole jest liczone według jego schematowego default.
- Modele multimedialne: koszt to
price × quantity, gdzie ilość jest wartościąquantityFieldw twoich danych wejściowych (lub jedną jednostką, jeśliquantityFieldjest puste). Niektóre endpointy następnie doliczają osobną opłatę za każde dodatkowe wejście, najczęściej za każdy obraz referencyjny. Traktuj ją jako rzeczywistą część rachunku, a nie szczegół zaokrąglenia: w endpoincie edycji przyjmującym tablicę obrazów referencyjnych wypełnienie tablicy może zwielokrotnić kwotę, choćunit,quantity,varianti cenapricepoziomu pozostają dokładnie takie jak dla jednego obrazu — powyższy rachunek zaniża więc kwotę bez widocznego sygnału. Maszynowo rozpoznasz to po polu oznaczonymx-ui.affects_price: true, które nie jest aniquantityField, ani polem wymienionym wx-pricing.variantFields: zmienia kwotę bez wybierania poziomu ceny. Dla takiego endpointu traktujpricingjako dolną granicę i pobieraj kwotę z wyceny. - Modele tekstowe (
per_1k_tokens): jedna liczba nie opisuje całej opłaty.pricejest wyłącznie stawką za tokeny wyjściowe, na 1 000 tokenów. Tokeny wejściowe i wejściowe z pamięci podręcznej mają własne, zwykle niższe stawki i są rozliczane według zmierzonego użycia każdego wywołania. Nie mnożpriceprzez łączną liczbę tokenów. Ceny za milion tokenów wejściowych, wejściowych z pamięci podręcznej i wyjściowych są podane na stronie każdego modelu oraz wprice.perMillionTokenspublicznego katalogu.
Aby poznać kwotę jednego dokładnego żądania natywnego API przed płatnością, użyj POST /api/v1/jobs/quote. Ostateczne opłaty są w rekordzie zadania (cost przy settled: true) i w Rozliczeniach.
Buforowanie katalogu
- Odpowiedź katalogu można przechowywać w pamięci podręcznej przez najwyżej 60 sekund (
Cache-Control: private, max-age=60). Przechowuj ją wyłącznie w prywatnej pamięci dla danego konta: ceny są przypisane do konta, więc nigdy nie umieszczaj jej za współdzieloną lub publiczną pamięcią CDN. - Ponadto nie odczytuj katalogu ponownie przed każdym zadaniem. Odczyty katalogu korzystają z osobnego limitu odczytów konta i nie zużywają limitu wysyłania zadań. Pobieranie katalogu przed każdym wywołaniem nadal marnuje dostępną liczbę odczytów; zobacz Limity żądań.
- Odświeżaj go okresowo (na przykład co kilka minut), po błędzie walidacji
400oraz gdy wysłanie zadania do danego modelu zostanie odrzucone. - Zachowaj
versioniupdatedAtużytego rekordu, aby móc stwierdzić, czy późniejszy odczyt coś zmienił. - Nigdy nie dostarczaj schematu wpisanego na stałe do kodu ani nie korzystaj ze statycznej strony jako zamiennika, gdy katalog na żywo nie ma pozycji dostępnej do wywołania.
Wspólne zasady schematu i Playground
inputSchema używa JSON Schema 2020-12. Standardowe słowa kluczowe sterują walidacją serwera; obiekt x-ui na poziomie właściwości opisuje jedynie kolejność w Playground, prezentację i wskazówki odświeżania wyceny. Klient musi ignorować nieznane rozszerzenia prezentacji, które nigdy nie zastępują required, wartości enum, granic ani gałęzi warunkowych. Zobacz pełny Kontrakt Spicy Schema.
Wyrenderuj własną tabelę parametrów każdego modelu
Ta strona nie zawiera tabel parametrów poszczególnych modeli. Nie wynika to z zaniedbania: modele i pola zmieniają się co tydzień, każda statyczna kopia staje się nieaktualna, a skutkiem odczytania starego parametru może być 400 lub błędna cena. Parametry mają dokładnie jedno źródło — inputSchema zwracane przez katalog. Playground renderuje ten sam dokument, a SDK waliduje dane względem niego.
Jak przełożyć części schematu na tabelę
| W schemacie | W twojej tabeli parametrów |
|---|---|
klucz properties | nazwa parametru. Nazwy pól są ujednolicone między modelami: jedno znaczenie, jedna nazwa wszędzie |
obecność w required | wymagany lub opcjonalny |
anyOf / allOf | wymagany warunkowo. anyOf = podaj co najmniej jedno z tych pól; w allOf reguła if → then = jeśli podasz A, musisz podać B |
type | typ |
description | tekst objaśnienia. Jest po angielsku i można go wyświetlić bez zmian; nie musisz pisać własnego |
enum | akceptowane wartości. Jeśli istnieje x-ui.enum_labels, użyj go do zamiany wartości na czytelne etykiety |
minimum / maximum | akceptowany zakres; x-ui.step to krok, a x-ui.unit przyrostek jednostki (s, px) |
minItems / maxItems | dla tablicy: liczba akceptowanych wpisów. Wiersz bez tego nie mówi użytkownikowi nic o limicie, który napotka |
items | dla tablicy: struktura jednego wpisu. Gdy items.type to object, ten podschemat jest samodzielnym, pełnym schematem obiektu: odczytaj jego required, additionalProperties oraz type, description, zakres i default każdego podpola i przedstaw je jako zagnieżdżoną tabelę zamiast pola swobodnego tekstu |
default | wartość domyślna stosowana po pominięciu pola |
x-ui.order | klucz sortowania, od największej wartości. Nie sortuj według kolejności kluczy w properties — kolejność kluczy obiektu JSON nie jest częścią kontraktu |
x-ui.primary | przypnij na górze jako główne pole wejściowe |
x-ui.advanced | można zwinąć do sekcji zaawansowanej |
x-ui.affects_price | oznacz jako wpływające na cenę; zmiana wartości uzasadnia nową wycenę |
x-ui.widget | kontrolka do wyrenderowania w edytowalnym formularzu (trzynaście wartości, zobacz Spicy Schema) |
Gałęzie warunkowe występują tylko w korzeniu inputSchema, w dwóch postaciach: anyOf, gdzie każda gałąź wymienia tylko pola required („podaj co najmniej jedno z tych pól”), oraz allOf, gdzie każda gałąź ma postać if → then („jeśli podasz A, musisz podać B”). Nie wyprowadzaj ich z rodziny modelu i nie oczekuj oneOf, not ani dependentSchemas. Nowe endpointy używają standardowych pól Spicy Schema; starsze — własnego schematu na żywo.
Pobieranie schematu
const modelId = 'MODEL_ID_FROM_CATALOG'; // the exact `model` value from GET /api/v1/models
const res = await fetch(`https://api.spicyapi.ai/api/v1/models/${modelId}`, {
headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` },
});
const { data } = await res.json();
const schema = data.inputSchema;
const rows = Object.entries(schema.properties)
.sort(([, a], [, b]) => (b['x-ui']?.order ?? 0) - (a['x-ui']?.order ?? 0))
.map(([name, prop]) => ({
name,
required: (schema.required ?? []).includes(name),
type: prop.type,
description: prop.description,
values: prop.enum
? prop.enum.map((v) => prop['x-ui']?.enum_labels?.[v] ?? v)
: prop.minimum !== undefined || prop.maximum !== undefined
? [`${prop.minimum ?? ''}–${prop.maximum ?? ''}${prop['x-ui']?.unit ?? ''}`]
: [],
default: prop.default,
affectsPrice: prop['x-ui']?.affects_price === true,
advanced: prop['x-ui']?.advanced === true,
}));Ten szkic celowo kończy się na wartościach skalarnych: dla tablicy pozostawia values puste, ponieważ tablica opisuje akceptowane dane w items, minItems i maxItems, a nie w enum lub zakresie. Rozbuduj go przed wydaniem — wiersz, który przedstawia parametr tablicowy jedynie jako array, bez struktury wpisu i limitu długości, nie pozwala użytkownikowi ustalić, co ma wpisać.
Rekord pojedynczego modelu zwraca też examples: każdy jest kompletnym wejściem już zwalidowanym po stronie serwera względem aktualnego schematu, więc sprawdza się zarówno jako minimalny działający przykład w dokumentacji, jak i jako treść żądania gotowa do wysłania bez zmian. To bezpieczniejsze niż ręczne tworzenie przykładu, ponieważ ręczny przykład staje się nieaktualny po zmianie schematu, a examples nie.
Ten sam kod obsługuje dowolny model po zmianie modelId i nie traci ważności po dodaniu nowych modeli. Aby wygenerować tabelę całego katalogu, przejdź po items z GET /api/v1/models?includeSchema=1 i przechowuj ją w pamięci podręcznej zgodnie z Buforowaniem katalogu.
Objaśnienia dłuższe niż description
description to jedno zdanie o długości odpowiedniej do etykiety formularza. Gdy parametr wymaga szerszego wyjaśnienia, publiczny katalog udostępnia dodatkową notatkę dla każdego parametru:
curl "https://api.spicyapi.ai/console/v1/catalog/models/MODEL_ID_FROM_CATALOG?locale=en"Rekord pojedynczego modelu zwraca paramNotes, obiekt z kluczami będącymi nazwami parametrów, dostępny w dowolnym z dziesięciu języków dokumentacji przez locale. To miejsce na informacje, które nie mieszczą się w etykiecie — względem czego rzeczywiście mierzona jest wartość, jakie ustawienie zauważalnie spowalnia wywołanie i jakie dane wejściowe są przyjmowane przez schemat, ale nadal dają rozczarowujący wynik. Dla parametru wskazującego samodzielnie hostowany plik zwykle tylko tam opisano, czy go pobieramy, kiedy to robimy i co się stanie, gdy okaże się bezużyteczny.
Są dwie granice: paramNotes występuje w publicznym katalogu, który nie wymaga klucza, a nie w uwierzytelnionym GET /api/v1/models, więc odczytuj je osobno i buforuj obok schematu. Ponadto jest tekstowym objaśnieniem, nigdy regułą — inputSchema pozostaje jedynym źródłem decydującym o przyjęciu żądania.

