Pro API: saldo, limity i wytrenowane postacie
Dwie trasy odczytu, zawsze dozwolone niezależnie od uprawnień klucza, oraz dziennik aktywności przechowywany w aplikacji.
Saldo i limity
GET /credits
{ "balance": 1830, "licenseId": "lic_...", "key": "k_1a2b3c4d",
"limits": { "concurrent": 2, "perMinute": 60, "perDay": 0 }, "running": 1,
"buy_url": "https://tendre.ai/credits?license=lic_..." }| Pole | Znaczenie |
|---|---|
balance | Pozostałe kredyty. null, gdy saldo jest nieznane (środowisko testowe). |
licenseId | Licencja, do której należy ten klucz. |
key | Publiczny identyfikator klucza, bezpieczny do logowania. Nigdy nie jest sekretem. |
limits.concurrent | Renderowania uruchomione jednocześnie, domyślnie 2. |
limits.perMinute | Zgłoszenia na minutę, domyślnie 60. |
limits.perDay | Dzienny limit kredytów ustawiony na kluczu. 0 oznacza brak limitu. |
running | Renderowania z tego klucza wciąż oczekujące na odbiór. |
buy_url | Strona zakupu kredytów dla twojej licencji, kartą lub kryptowalutą. |
To najtańsze wywołanie w API i właśnie to należy użyć, żeby sprawdzić, czy klucz działa.
Kupowanie kredytów zapisaną kartą
POST /credits/purchase
{ "pack": "m" }pack to identyfikator pakietu (s, m, l, xl, xxl lub xxxl) albo "c25000" dla dowolnej liczby kredytów powyżej największego pakietu. Płatność idzie z karty zapisanej w automatycznym doładowaniu na stronie twojego konta, tylko jeśli pozwoliłeś tam agentowi kupować, w ramach ustalonego tam limitu miesięcznego (automatyczne doładowania wliczone). Każdy zakup potwierdza e-mail. Jeden zakup naraz.
Odpowiedź to 200 { credits, eur, balance }. Każda odmowa zawiera buy_url, zwykły link do zakupu, którego używasz zamiast tego: 402 confirmation_required (twój bank prosi o potwierdzenie) lub payment_declined; 409 agent_not_allowed, no_saved_card, monthly_cap_reached, topup_in_progress lub purchase_in_progress; 400 unknown_pack. Przy 503 payment_unavailable nie wiemy, czy obciążenie nastąpiło: nie ponawiaj od razu, obciążenie, które przeszło, zostanie zaksięgowane.
Wytrenowane postacie
Są to postacie wytrenowane jako LoRA, przeznaczone do obrazów i zwykłych filmów. Postacie z Multi-Ref Wideo, siedem widoków wykonanych z jednego zdjęcia, to osobna sprawa: zobacz Postacie.
GET /loras
{ "image": [ { "name": "megan_krea2", "trigger": "megan", "base": "krea2", "size": 0 } ],
"video": [ { "name": "megan_ltx", "trigger": "megan", "size": 0 } ] }Podaj name jako lora, biorąc je z listy image dla obrazów i z listy video dla filmów,
i wstaw jej trigger do promptu, w przeciwnym razie postać się nie pojawi. Postać, która
nie należy do konta, zwraca 403 unknown_lora.
Prześlij własne LoRA
POST /loras/upload
Plik .safetensors, który już masz, wytrenowany gdzie indziej lub leżący na własnym serwerze,
trafia do przestrzeni konta i staje się dostępny przez lora, dokładnie jak postać wytrenowana
w aplikacji. Są dwa sposoby przesłania: jednorazowo do 90 MB albo w częściach powyżej
tej granicy. LoRA waży zazwyczaj od 200 do 500 MB, więc ten drugi sposób jest typowy. Limit
wynosi 800 MB w obu przypadkach.
Jednorazowo: surowe ciało żądania (application/octet-stream, wymagany Content-Length),
parametry w URL.
Powyżej 100 MB żądanie nigdy nie dociera do API. Infrastruktura przed nim odrzuca je, więc
w odpowiedzi pojawia się samo 413 bez ciała JSON: klient, który parsuje każdy błąd jako
JSON, zawiesi się zamiast go zgłosić. Ten rozmiar należy przesyłać w częściach.
| Parametr | Rola |
|---|---|
name | Nazwa pliku, .safetensors, litery, cyfry, . _ -, maksymalnie 80 znaków. Istniejąca nazwa zostaje zastąpiona. |
kind | image (domyślnie) lub video. Wymaga odpowiednio uprawnienia images albo videos. |
trigger | Słowo wyzwalające do umieszczenia w prompcie, maksymalnie 60 znaków. |
curl -s -X POST "$B/loras/upload?name=my_character.safetensors&kind=image&trigger=ohwx" -H "Authorization: Bearer $KEY" -H "Content-Type: application/octet-stream" --data-binary @my_character.safetensorsOdpowiedź to { "ok": true, "name": "my_character.safetensors", "kind": "image", "size": 314572800, "trigger": "ohwx", "architecture": "krea2", "storage": { ... } }.
Kompatybilność jest sprawdzana podczas przesyłania. Obrazy są renderowane przez Krea 2, który ma własną architekturę: stosuje się wyłącznie LoRA w formacie Krea 2, wytrenowane w tendre.AI lub dla Krea 2 gdzie indziej. Warto wiedzieć, że LoRA FLUX.1 zostaje odrzucone, co nie jest pierwszym skojarzeniem każdego. Filmy są renderowane przez LTX 2.5, więc obowiązuje LoRA LTX 2.5 lub 2.3.
API odczytuje nagłówek pliku przed zapisaniem czegokolwiek. LoRA rozpoznane jako SDXL, SD 1.5,
SD3, FLUX.1, Wan lub HunyuanVideo jest odrzucane z błędem 415 incompatible_lora, który
podaje znalezioną architecture, a plik niebędący safetensors otrzymuje 415 invalid_safetensors.
Nic nie jest zapisywane i żaden limit nie zostaje zużyty w żadnym z tych przypadków.
Jeden przypadek jednak przechodzi: architektura, której nagłówek nie identyfikuje, jest
akceptowana z architecture: "inconnue" i oceniana dopiero w czasie renderowania. To jedyna
pomyłka, która kosztuje cię renderowanie, zanim się o niej dowiesz.
Wyślij duże LoRA w częściach
Trzy wywołania, plus jedno do rezygnacji.
1. Otwórz sesję. POST /loras/upload/start z { "name", "kind", "size", "trigger" },
gdzie size to cały plik w bajtach. Sprawdza nazwę, rozmiar i limit, a następnie odpowiada:
{ "uploadId": "...", "part_size": 20971520, "max_part_size": 104857600, "parts_expected": 15 }2. Wyślij każdą część. POST /loras/upload/part?uploadId=...&part=N, surowe ciało, części
numerowane od 1, każda po part_size bajtów oprócz ostatniej, nigdy więcej niż max_part_size.
Każda odpowiedź zawiera etag, a wszystkie będą potrzebne w kolejnym kroku, więc zachowuj je
na bieżąco.
Część 1 jest wyjątkowa: zawiera nagłówek pliku, więc właśnie tam jest oceniana architektura.
Niekompatybilne LoRA zostaje wykryte tam z błędem 415 i sesja jest anulowana. Dowiadujesz
się po 20 MB zamiast po 400.
3. Zamknij ją. POST /loras/upload/finish z { "uploadId", "parts": [ { "part": 1, "etag": "..." }, ... ] }.
Składa plik i rejestruje go, odpowiadając dokładnie tak jak przesłanie jednorazowe.
Żeby zrezygnować, wywołaj POST /loras/upload/abort z { "uploadId" }. Sesja pozostawiona
bez finish jest usuwana po sześciu godzinach. Nic nie pojawia się w GET /loras i nic nie
jest wliczane do limitu, dopóki finish nie powiedzie się, więc przerwane przesyłanie nie
kosztuje cię miejsca.
import os, requests
B, H = "https://api.tendre.ai/pro/v1", {"Authorization": "Bearer " + KEY}
path = name = "my_character.safetensors"
s = requests.post(f"{B}/loras/upload/start", headers=H,
json={"name": name, "kind": "image", "size": os.path.getsize(path), "trigger": "ohwx"}).json()
parts = []
with open(path, "rb") as f:
n = 1
while chunk := f.read(s["part_size"]):
r = requests.post(f"{B}/loras/upload/part",
headers={**H, "Content-Type": "application/octet-stream"},
params={"uploadId": s["uploadId"], "part": n}, data=chunk)
r.raise_for_status()
parts.append({"part": n, "etag": r.json()["etag"]})
n += 1
done = requests.post(f"{B}/loras/upload/finish", headers=H,
json={"uploadId": s["uploadId"], "parts": parts}).json()
print(done["name"], done["architecture"], done["storage"])Gotowy skrypt PowerShell jest dołączony do aplikacji, pod ścieżką scripts/api-pro-lora.ps1.
Usuń LoRA
DELETE /loras/{name}?kind=image|video usuwa plik i zwalnia limit. Odpowiada
{ "ok": true, "name": ..., "storage": { ... } } albo 404 unknown_lora, gdy nazwa nie
istnieje na koncie.
Limit przestrzeni
Każda licencja otrzymuje maksymalnie 20 plików i 10 GB, obrazy i filmy razem, a postacie
wytrenowane w aplikacji są wliczane do tego limitu. GET /loras raportuje stan w storage:
{ "count": 3, "max_count": 20, "bytes": 943718400, "max_bytes": 10737418240 }Po przekroczeniu któregokolwiek limitu przesyłanie zwraca 413 lora_quota wraz z informacją
o tym, co pozostało. Usuń plik, żeby zwolnić miejsce.
Trenowanie postaci nie jest jeszcze dostępne przez API. Odbywa się w aplikacji, a wynik pojawia się na tej liście.
Aktywność
Aplikacja przechowuje dziennik każdego renderowania wykonanego przez twoje klucze (data, klucz, typ, koszt, status) oraz zużycie na klucz, w sekcji Ustawienia > API > Activity.
To też miejsce, w którym możesz unieważnić klucz, który wyciekł, bez dotykania pozostałych.