ApiframeApiframe Docs
AI Photos (LoRAs)

Create LoRA

Upload subject photos and start a personal LoRA training run.

POST /v2/loras

Multipart endpoint. The body must contain 15–30 image files, a name, and (for the default person subject kind) a gender and ethnicity. Returns 202 Accepted with the new LoRA's id and initial status — training continues in the background and you should poll GET /v2/loras/:id (or use a webhook) to track progress.

Body — multipart/form-data

FieldTypeDescription
imagesfile[]Required. Between 15 and 30 image parts (jpg / png / webp). Each ≤ 10 MB, total ≤ 100 MB. The field name images may be repeated.
namestringRequired. Display name for the LoRA (1–60 chars). Surfaced in your dashboard listings.
subjectKindstringOptional. One of person (default), style, object.
genderstringRequired when subjectKind=person. One of female, male, non-binary. Baked into the captioning prompt and the trigger phrase to materially improve fidelity.
ethnicitystringRequired when subjectKind=person. Free-form (1–40 chars). The studio constrains it to White, Black, East Asian, South Asian, Southeast Asian, Hispanic/Latino, Middle Eastern, Mixed, but the API accepts any string.
agestringOptional, free-form (1–40 chars). E.g. "25", "early 30s", "around 40".
stepsintegerOptional. Number of training steps (500–3000, default 1000). Higher = better quality + slower + same flat 255cr cost.
loraRankintegerOptional. LoRA rank (8–64, default 32).
webhookUrlstringOptional HTTPS URL — receives a JSON POST when training reaches a terminal state.
webhookEventsstringOptional comma-separated subset of completed,failed.

Response — 202

{
  "id": "lora_8a4d2c…",
  "status": "PENDING",
  "imageCount": 18
}

Errors

CodeWhen
400Wrong number of images, oversize file, unsupported mime, missing required fields.
402Insufficient credits (the 2cr lora-create fee couldn't be deducted).
429Team is at its concurrent training cap (maxConcurrentTrainings, default 10).

Example

curl -X POST https://api.apiframe.ai/v2/loras \
  -H "X-API-Key: afk_your_api_key_here" \
  -F "name=Alex – studio portraits" \
  -F "subjectKind=person" \
  -F "gender=male" \
  -F "ethnicity=White" \
  -F "[email protected]" \
  -F "[email protected]" \
  # … repeat for all 15-30 images
  -F "[email protected]"
import requests

files = [("images", open(f"photo{i:02d}.jpg", "rb")) for i in range(1, 19)]
response = requests.post(
    "https://api.apiframe.ai/v2/loras",
    headers={"X-API-Key": "afk_your_api_key_here"},
    data={
        "name": "Alex – studio portraits",
        "subjectKind": "person",
        "gender": "male",
        "ethnicity": "White",
    },
    files=files,
)
print(response.json())
import fs from "node:fs";

const form = new FormData();
form.append("name", "Alex – studio portraits");
form.append("subjectKind", "person");
form.append("gender", "male");
form.append("ethnicity", "White");
for (let i = 1; i <= 18; i++) {
  form.append("images", new Blob([fs.readFileSync(`photo${String(i).padStart(2, "0")}.jpg`)]), `photo${i}.jpg`);
}

const response = await fetch("https://api.apiframe.ai/v2/loras", {
  method: "POST",
  headers: { "X-API-Key": "afk_your_api_key_here" },
  body: form,
});
console.log(await response.json());

On this page