API referenceGeneration
Personal LoRAs
Import a LoRA file you have permission to use, then apply it to renders on compatible models. Your library is the same one shown in My LoRAs on Sogni Web. Importing checks and stores the file; it does not train a new model.
Only your account can list or use its library. To render, the file is downloaded to GPU workers on the Sogni Supernet, so do not import weights that must stay confidential.
Authenticate with Authorization: Bearer $SOGNI_API_KEY (the api-key header
also works). Responses use { "status": "success", "data": ... } with
Cache-Control: private, no-store.
#Endpoints
| Method and path | Result |
|---|---|
GET /v1/loras/personal |
data.loras (your import records), data.models (model IDs you can import for), and data.limits |
GET /v1/loras/personal/:id |
One import record and its current status |
GET /v1/loras/personal/catalog |
Ready entries in data.loras, shaped like public catalog rows |
POST /v1/loras/personal |
202 with an import record |
DELETE /v1/loras/personal/:id |
Removes the entry from your library; data is {} |
#Access
Importing, reading /catalog, and rendering with a personal LoRA require an
active Unlimited or Unlimited Pro plan. Free trials count, and so does a
cancelled plan until its paid period ends. A renewal payment that is still
being retried does not. Without access, these calls return 403 with
errorCode: 179.
Listing, reading one record, and removing entries keep working after access ends. Entries still show their status, but cannot be used until access returns.
An ID that is not in personal-<uuid> form returns 400. An unknown ID, or one
owned by another account, returns 404. If a DELETE races another change to
the same entry, it returns 409; retry it.
#Import a LoRA
curl --fail-with-body https://api.sogni.ai/v1/loras/personal \
-H "Authorization: Bearer $SOGNI_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"url":"https://huggingface.co/author/repository/resolve/main/style.safetensors","name":"My style","modelId":"krea2_turbo_fp8_scaled","rightsConfirmed":true}'
| Field | Rules |
|---|---|
url |
Required. A supported Hugging Face or Civitai link (see below). |
name |
Required. 1–80 characters, shown only in your library. |
modelId |
Required. One of data.models from GET /v1/loras/personal. |
rightsConfirmed |
Required. Must be the boolean true. Send it only after confirming you have permission to use the file on Sogni. |
The body must be JSON (415 otherwise).
Accepted links. Links must be public HTTPS URLs without a port, credentials, fragment, or access token, up to 2,048 characters.
- Hugging Face: a single file,
https://huggingface.co/<owner>/<repo>/resolve/<revision>/<path>.safetensors.bloblinks are accepted too. Repository pages are not. - Civitai: a model page (
https://civitai.com/models/<id>, which imports its newest published version), a specific version (/models/<id>?modelVersionId=<version>), or a version download link (/api/download/models/<version>). The model must be a LoRA.
The import is pinned to an exact revision or version, so the stored source
can differ from the link you sent.
File requirements. Standard low-rank LoRA weights in .safetensors
format, stored as FP16, BF16, or FP32, with rank 128 or lower. LoKr, LoHa,
DoRA, OFT, GLoRA, LoCon/Tucker, and full-weight files are refused. The
maximum file size is data.limits.fileBytes, which is null when that value
is briefly unavailable.
#What a 202 returns
A new import returns a queued record. If you already imported the same link
for a model in the same family, the API returns that existing entry, whatever
its status, instead of creating another one. If that existing entry was
rejected, it is queued again.
#Import errors
| Status | Cause |
|---|---|
400 |
rightsConfirmed is not true, the link is not supported (the message says why), modelId is not importable, name is invalid, or the source cannot be downloaded. |
403 |
No active Unlimited access (errorCode: 179). |
409 |
The file is already a Sogni catalog LoRA for that model (the message names it), or your library is full. |
429 |
Too many requests (wait for Retry-After), or today's import allowance is used. |
503 |
The source could not be checked right now, or the import queue is full. Retry later. |
#Limits
| Limit | Value |
|---|---|
| Library entries | 20 (limits.entries). Entries in every status count; remove one to free a slot. |
| New imports | 5 per UTC day (limits.importsPerDay), resetting at 00:00 UTC (5 PM PDT or 4 PM PST). |
| LoRAs per render | 8 (limits.perGeneration), personal and catalog LoRAs combined. |
An accepted import uses one of the day's imports even if it is later rejected or removed. Refused links, catalog duplicates, and returning an existing entry do not. Re-queuing a rejected link does. Reads and import attempts are also rate-limited per account, and refused links count toward that limit.
#Track an import
Poll GET /v1/loras/personal/:id about every 10 seconds until the status is
ready, rejected, or revoked. Most imports finish within a few minutes.
{
"status": "success",
"data": {
"id": "personal-3f0c…",
"name": "My style",
"modelId": "krea2_turbo_fp8_scaled",
"modelIds": ["krea2_turbo_fp8_scaled", "krea2_identity_edit_v1_2", "…"],
"source": "https://huggingface.co/author/repository/resolve/main/style.safetensors",
"status": "ready",
"createdAt": 1789600000000,
"updatedAt": 1789600090000,
"bytes": 228480000,
"requirements": []
}
}
createdAt and updatedAt are Unix milliseconds. bytes, reason, and
failureCode appear when known. requirements lists plain-language usage
rules, and is non-empty only for some ready entries.
| Status | Meaning |
|---|---|
queued |
Waiting to be imported. |
validating |
Downloading and checking the file. |
review |
Not usable right now, usually because the model it was imported for changed or was retired. If it does not return to ready, remove it and import for a current model. |
ready |
Usable on every model in modelIds. |
rejected |
Not imported. Show reason to the user. |
revoked |
Withdrawn by Sogni. Remove the entry. |
A status can change after an import finishes, so check it before rendering. Some rejections are retried automatically after Sogni improves import support.
failureCode |
What to do |
|---|---|
source_access |
The file could not be downloaded. It may have been removed, restricted to its owner, or require an agreement. |
subscription_required |
Unlimited access ended while the import was queued. Import again after renewing. |
catalog_duplicate |
The file is already in the Sogni catalog; use that LoRA instead. |
invalid_artifact |
The format, precision, rank, or size is not supported, or the download kept failing. |
A rejection found while rendering can have a reason but no failureCode.
#Render with a ready LoRA
GET /v1/loras/personal/catalog returns one row per ready entry:
{
"loraId": "personal-3f0c…",
"slug": "personal-3f0c…",
"name": "My style",
"description": "Your imported LoRA.",
"relatedLoraIds": [],
"modelIds": ["krea2_turbo_fp8_scaled", "…"],
"ui": {
"category": "personal", "label": "My style",
"min": 0, "max": 1, "default": 1, "step": 0.05,
"recommendedMin": 0.5, "recommendedMax": 1,
"nsfw": true, "sexual": false,
"creator": "Imported by you",
"sourceUrl": "https://huggingface.co/…"
}
}
Other fields can be present; ignore them. loraId is the import id.
- Model. Render only on a model listed in the entry's
modelIds. Never infer compatibility from the LoRA's name. - Strength. Pass
lorasand a matchingloraStrengthsarray in the same order. Personal LoRA strength must be greater than 0 and at most 1. Useui.defaultwhen the user has not chosen one. If strengths are omitted, each LoRA is applied at 1. - Trigger words. Sogni does not add the author's trigger words; include
them in your prompt. If the entry lists
requirements(such as a required phrase or a narrower strength range), follow them, or the render is refused. - Content filter. Every personal entry has
ui.nsfw: true, because imports are not reviewed for content. Sogni Web shows imported LoRAs only while the Sensitive Content Filter is off; apply the same rule in your interface.
#Compatible models
An import works on every model in its family, not only the model you chose
when importing it. Use data.models and each entry's modelIds as the
authoritative lists; they change as models are added. Families include:
| Family | Models |
|---|---|
| Krea 2 | Krea 2 Turbo, Krea 2 Identity Edit, Dark Beast Krea 2, and Dark Beast Krea 2 Identity Edit |
| Qwen Image Edit 2511 | Qwen Image Edit 2511 (not Lightning) |
| MiniMax H3 | Text-to-video, image-to-video, and first/last-frame modes across Standard, Balanced, Turbo, FastH3 Turbo, and FastH3 Two-Stage |
| MiniMax H3 Reference-to-Video | Its own family, separate from the other H3 modes |
Audio-guided H3 modes (image + audio, first/last frame + audio, and audio only) do not accept LoRAs.
#Hosted tools and workflows
Creative workflow steps and hosted chat tools
accept loras and loraStrengths (lora_strengths also works) on
generate_image, edit_image, generate_video, and animate_photo, and use
at most 8. Choose a model argument in the entry's family:
| Tool | Model argument |
|---|---|
generate_image |
model: "krea-2-turbo" or "dark-beast-krea2" |
edit_image |
model: "krea-identity-edit", "dark-beast-krea2-identity-edit", or "qwen" |
generate_video |
videoModel: "minimax-h3-t2v", "minimax-h3-t2v-turbo", "minimax-h3-t2v-balanced", "minimax-h3-fasth3-t2v-turbo", "minimax-h3-fasth3-t2v-turbo-2stage", "minimax-h3-r2v", "minimax-h3-r2v-turbo", "minimax-h3-r2v-balanced", "minimax-h3-r2v-2stage", or "minimax-h3-r2v-balanced-2stage" |
animate_photo |
videoModel: "minimax-h3-i2v", "minimax-h3-i2v-turbo", "minimax-h3-i2v-balanced", "minimax-h3-fasth3-i2v-turbo", "minimax-h3-fasth3-i2v-turbo-2stage", or the matching minimax-h3-flf2v selectors |
Sending a personal-… ID with a model that cannot use personal LoRAs returns
400.
#SDK
const library = await sogni.projects.personalLoras.list(); // choose modelId from library.models
const imported = await sogni.projects.personalLoras.import({
url: 'https://huggingface.co/author/repository/resolve/main/style.safetensors',
name: 'My style',
modelId: 'krea2_turbo_fp8_scaled',
rightsConfirmed: true,
});
const current = await sogni.projects.personalLoras.get(imported.id); // poll until ready, rejected, or revoked
const { loras } = await sogni.projects.personalLoras.catalog({ modelId: 'krea2_turbo_fp8_scaled' });
const entry = loras.find((row) => row.loraId === imported.id);
if (entry) {
await sogni.projects.create({
type: 'image',
modelId: 'krea2_turbo_fp8_scaled',
numberOfMedia: 1,
positivePrompt: 'portrait in my style',
loras: [entry.loraId],
loraStrengths: [entry.ui.default],
});
}
// Only on an explicit removal request:
// await sogni.projects.personalLoras.remove(imported.id);
sogni.projects.availableLoras({ modelId, includePersonal: true }) merges your
ready entries into the public catalog. It requires authentication and active
Unlimited access, and it throws without them rather than returning only public
LoRAs. Personal rows are never stored in the shared public catalog cache. The
Python SDK exposes the same library as sogni.projects.personal_loras
(list, get, import_lora, remove, catalog) and
available_loras(include_personal=True).
The public LoRA catalog remains GET /v1/loras/comfy?modelId=... and needs no
authentication. Each row's modelIds lists the models that offer the LoRA. A
model that can load a LoRA but where tests showed it does not work is listed
under that row's restrictedModelIds instead, never in modelIds, and a render
of that pairing is not offered: for example vh5tape Worn VHS on the FastH3 and
Standard MiniMax H3 text- and image-to-video modes. ?modelId= returns only the
LoRAs a model offers. The field is omitted when a LoRA has no restrictions.
#Render errors and charges
- A render that names an unavailable personal LoRA (not ready, not yours, the wrong model, or no active access) is refused at submission.
- If you remove an entry or lose access, queued jobs that have not started are cancelled.
- If a worker finds the file does not match the model, the job fails without a
charge, and an entry used on its own is marked
rejected. - The first render with a LoRA a worker has not cached starts more slowly.
Completed project records list each personal LoRA used with its public source link and version, never your library name. That record remains after you remove the entry.