Pro API: saldo, begränsningar och tränade karaktärer
Två läsrutter, alltid tillåtna oavsett nyckelns behörigheter, plus aktivitetsloggen som sparas i appen.
Saldo och begränsningar
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_..." }| Fält | Betydelse |
|---|---|
balance | Återstående krediter. null när saldot är okänt (testmiljö). |
licenseId | Den licens som nyckeln tillhör. |
key | Nyckelns publika identifierare, säker att logga. Aldrig hemligheten. |
limits.concurrent | Renderingar som körs samtidigt, 2 som standard. |
limits.perMinute | Inlämningar per minut, 60 som standard. |
limits.perDay | Daglig kreditgräns du ställt in på nyckeln. 0 betyder ingen gräns. |
running | Renderingar från den här nyckeln som fortfarande väntar på att hämtas. |
buy_url | Sidan där du köper krediter till din licens, med kort eller krypto. |
Det här är det billigaste anropet i API:et och det rätta för att kontrollera att en nyckel fungerar.
Köpa krediter med ditt sparade kort
POST /credits/purchase
{ "pack": "m" }pack är ett paket-id (s, m, l, xl, xxl eller xxxl), eller "c25000" för ett valfritt antal över det största paketet. Betalningen dras från kortet som är sparat i den automatiska påfyllningen på din kontosida, bara om du där har tillåtit din agent att köpa, inom den månadsgräns du har satt där (automatiska påfyllningar inräknade). Ett e-postmeddelande bekräftar varje köp. Ett köp i taget.
Svaret är 200 { credits, eur, balance }. Varje avslag innehåller buy_url, den vanliga köplänken, att använda i stället: 402 confirmation_required (din bank begär din bekräftelse) eller payment_declined; 409 agent_not_allowed, no_saved_card, monthly_cap_reached, topup_in_progress eller purchase_in_progress; 400 unknown_pack. Vid 503 payment_unavailable vet vi inte om debiteringen gick igenom: försök inte igen direkt, en debitering som gick igenom krediteras.
Tränade karaktärer
Det här är karaktärer tränade som en LoRA, för bilder och vanliga videor. Karaktärerna för Multi-Ref Video, sju vyer skapade från ett foto, är något annat: se Characters.
GET /loras
{ "image": [ { "name": "megan_krea2", "trigger": "megan", "base": "krea2", "size": 0 } ],
"video": [ { "name": "megan_ltx", "trigger": "megan", "size": 0 } ] }Skicka name som lora, hämta det från image-listan för bilder och video-listan för videor, och lägg in dess trigger i prompten, annars visas inte karaktären. En karaktär som inte tillhör kontot returnerar 403 unknown_lora.
Ladda upp din egen LoRA
POST /loras/upload
En .safetensors-fil du redan har, tränad någon annanstans eller liggande på din egen server, läggs in i kontots utrymme och blir användbar via lora, precis som en karaktär tränad i appen. Det finns två sätt att göra det: ett skott upp till 90 MB, eller i delar utöver det. En LoRA väger vanligtvis 200 till 500 MB, så det andra alternativet är det vanliga. Taket är 800 MB i båda fallen.
Ett skott: en rå body (application/octet-stream, Content-Length krävs), parametrar i URL:en.
Över 100 MB når förfrågan aldrig API:et. Infrastrukturen framför avvisar den, så det som returneras är ett bart 413 utan JSON-body: en klient som tolkar varje fel som JSON kraschar på det istället för att rapportera det. Den storleken hör hemma i delar.
| Parameter | Roll |
|---|---|
name | Filnamn, .safetensors, bokstäver, siffror, . _ -, högst 80 tecken. Ett namn som redan finns ersätts. |
kind | image (standard) eller video. Kräver images- respektive videos-behörighet. |
trigger | Det triggord som ska läggas tillbaka i prompten, högst 60 tecken. |
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.safetensorsSvaret är { "ok": true, "name": "my_character.safetensors", "kind": "image", "size": 314572800, "trigger": "ohwx", "architecture": "krea2", "storage": { ... } }.
Kompatibilitet kontrolleras vid uppladdning. Bilder renderas av Krea 2, som har en egen arkitektur: endast en Krea 2 LoRA fungerar, oavsett om den är tränad i tendre.AI eller för Krea 2 någon annanstans. Observera att en FLUX.1 LoRA avvisas, vilket inte alla räknar med direkt. Videor renderas av LTX 2.5, så det krävs en LTX 2.5 eller 2.3 LoRA.
API:et läser filhuvudet innan något lagras. En LoRA som identifieras som SDXL, SD 1.5, SD3, FLUX.1, Wan eller HunyuanVideo avvisas med 415 incompatible_lora, som anger vilken architecture den hittade, och en fil som inte är en safetensors med 415 invalid_safetensors. Inget lagras och ingen kvot förbrukas i något av dessa fall.
Ett fall tar sig ändå igenom: en arkitektur som filhuvudet inte identifierar accepteras, med architecture: "inconnue", och bedöms först vid renderingstillfället. Det är det enda misstaget som kostar dig en rendering att upptäcka.
Skicka en stor LoRA i delar
Tre anrop, plus ett för att avbryta.
1. Öppna sessionen. POST /loras/upload/start med { "name", "kind", "size", "trigger" }, där size är hela filen i bytes. Det kontrollerar namn, storlek och kvot, och svarar sedan:
{ "uploadId": "...", "part_size": 20971520, "max_part_size": 104857600, "parts_expected": 15 }2. Skicka varje del. POST /loras/upload/part?uploadId=...&part=N, rå body, delar numrerade från 1, part_size bytes var utom den sista, aldrig mer än max_part_size. Varje svar innehåller ett etag, och du behöver alla för nästa steg, så spara dem allteftersom.
Del 1 är speciell: den innehåller filhuvudet, så det är där arkitekturen bedöms. En inkompatibel LoRA stoppas där med 415 och sessionen avbryts. Du får reda på det efter 20 MB snarare än efter 400.
3. Stäng den. POST /loras/upload/finish med { "uploadId", "parts": [ { "part": 1, "etag": "..." }, ... ] }. Det sätter ihop filen och registrerar den, och svarar precis som ett-skott-uppladdningen.
För att avbryta: POST /loras/upload/abort med { "uploadId" }. En session som lämnas utan finish tas bort efter sex timmar. Inget visas i GET /loras och inget räknas mot kvoten förrän finish lyckas, så en avbruten uppladdning kostar dig inget lagringsutrymme.
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"])Ett färdigt PowerShell-skript medföljer appen, på scripts/api-pro-lora.ps1.
Ta bort en LoRA
DELETE /loras/{name}?kind=image|video tar bort filen och frigör kvoten. Det svarar med { "ok": true, "name": ..., "storage": { ... } }, eller 404 unknown_lora om namnet inte finns på kontot.
Lagringskvot
Varje licens får högst 20 filer och 10 GB, bilder och videor tillsammans, och karaktärer tränade i appen räknas mot detta. GET /loras rapporterar tillståndet i storage:
{ "count": 3, "max_count": 20, "bytes": 943718400, "max_bytes": 10737418240 }Om någon av gränserna överskrids svarar en uppladdning med 413 lora_quota tillsammans med vad som återstår. Ta bort en fil för att frigöra utrymme.
Träning av en karaktär är fortfarande inte exponerat via API:et. Det sker i appen, och resultatet visas i den här listan.
Aktivitet
Appen sparar loggen över varje rendering dina nycklar har gjort (datum, nyckel, typ, kostnad, status) och förbrukningen per nyckel, under Settings > API > Activity.
Det är också där du återkallar en nyckel som har läckt, utan att påverka de andra.