HTTP API reference
Base URL: https://api.moondream.ai/v1/tuning/
Authentication
All requests require the X-Moondream-Auth header with your API key:
X-Moondream-Auth: YOUR_API_KEY
Get your API key from the Moondream Cloud Console.
POST /finetunes
Create a new finetune.
Request
{
"name": "my-finetune",
"rank": 32
}
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Unique name for this finetune (alphanumeric, hyphens, underscores) |
| rank | integer | yes | LoRA rank: 8, 16, 24, or 32 (must be multiple of 8; higher = more capacity, but slower) |
Response
{
"finetune_id": "01HXYZ..."
}
| Field | Type | Description |
|---|---|---|
| finetune_id | string | Unique identifier (ULID) for the finetune. Use this ID for all subsequent operations. |
If a finetune with the same name already exists with the same rank, the existing finetune_id is returned (idempotent). If the name exists with a different rank, returns 409 Conflict.
GET /finetunes
List all finetunes for your organization.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Maximum results per page (1–100) |
| cursor | string | — | Pagination cursor from previous response |
Response
{
"finetunes": [
{
"finetune_id": "01HXYZ...",
"name": "my-finetune",
"rank": 32,
"created_at_ms": 1736937000000,
"updated_at_ms": 1736937000000
}
],
"next_cursor": "eyJjIjoiMjAyNS0wMS0xNVQxMDozMDowMFoifQ==",
"has_more": true
}
Results are ordered by creation time (oldest first).
GET /finetunes/:finetuneId
Get details for a specific finetune.
Response
{
"finetune_id": "01HXYZ...",
"name": "my-finetune",
"rank": 32,
"created_at_ms": 1736937000000,
"updated_at_ms": 1736937000000
}
Returns 404 if the finetune does not exist.
DELETE /finetunes/:finetuneId
Delete a finetune. This is a soft delete—the finetune is marked as deleted and its checkpoints are cleaned up asynchronously.
Response
Returns { "ok": true } on success.
Constraints:
- Returns 404 if the finetune does not exist.
POST /rollouts
Generate rollouts for a training or evaluation request.
Request
{
"finetune_id": "01HXYZ...",
"num_rollouts": 4,
"request": { ... },
"ground_truth": { ... } // optional
}
| Field | Type | Required | Description |
|---|---|---|---|
| finetune_id | string | yes | The finetune ID (from POST /finetunes response) |
| num_rollouts | integer | yes | Number of attempts to generate (1–16) |
| request | object | yes | Skill-specific request (see below) |
| ground_truth | object | no | Ground truth for automatic reward computation (point/detect only) |
Skill requests
Query
{
"skill": "query",
"question": "What color is the car?",
"image_url": "data:image/jpeg;base64,...", // optional
"reasoning": false, // optional
"settings": {
"temperature": 1.0,
"top_p": 1.0,
"max_tokens": 128
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| skill | string | yes | Must be "query" |
| question | string | yes | The question to answer |
| image_url | string | no | Base64 data URL of the image |
| reasoning | boolean | no | Enable extended reasoning (default: false) |
| settings | object | no | Generation settings |
Point
{
"skill": "point",
"object": "the red button",
"image_url": "data:image/jpeg;base64,...",
"settings": {
"temperature": 1.0,
"top_p": 1.0,
"max_tokens": 128
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| skill | string | yes | Must be "point" |
| object | string | yes | Description of the object to point to |
| image_url | string | yes | Base64 data URL of the image |
| settings | object | no | Generation settings |
Detect
{
"skill": "detect",
"object": "vehicles",
"image_url": "data:image/jpeg;base64,...",
"settings": {
"temperature": 1.0,
"top_p": 1.0,
"max_tokens": 256,
"max_objects": 8
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| skill | string | yes | Must be "detect" |
| object | string | yes | Description of objects to detect |
| image_url | string | yes | Base64 data URL of the image |
| settings | object | no | Generation settings (includes max_objects) |
Settings
| Field | Type | Default | Description |
|---|---|---|---|
| temperature | number | 1.0 | Controls randomness (0 = deterministic) |
| top_p | number | 1.0 | Nucleus sampling threshold |
| max_tokens | integer | 128 | Maximum output length |
| max_objects | integer | — | Maximum objects to detect (detect only) |
Ground truth
Point — coordinates:
{
"points": [{ "x": 0.52, "y": 0.31 }]
}
Point — bounding boxes (rewards based on containment):
{
"boxes": [{ "x_min": 0.1, "y_min": 0.2, "x_max": 0.4, "y_max": 0.6 }]
}
Detect:
{
"boxes": [
{ "x_min": 0.1, "y_min": 0.2, "x_max": 0.4, "y_max": 0.6 }
]
}
All coordinates are normalized to 0–1.
Response
{
"request": { ... },
"rollouts": [
{
"skill": "detect",
"output": {
"objects": [
{ "x_min": 0.12, "y_min": 0.22, "x_max": 0.39, "y_max": 0.58 }
]
},
// ... opaque training metadata
}
],
"rewards": [0.8, 0.3, 0.6, 0.5]
}
| Field | Type | Description |
|---|---|---|
| request | object | Echoed request |
| rollouts | array | Generated rollouts |
| rewards | array | null | Computed rewards if ground truth was provided |
Rollout output by skill
Query:
{
"answer": "The car is red.",
"reasoning": null // present if reasoning was enabled
}
Point:
{
"points": [{ "x": 0.52, "y": 0.31 }]
}
Detect:
{
"objects": [
{ "x_min": 0.12, "y_min": 0.22, "x_max": 0.39, "y_max": 0.58 }
]
}
POST /train_step
Apply one training step using scored rollouts.
Request
{
"finetune_id": "01HXYZ...",
"groups": [
{
"request": { ... },
"rollouts": [ ... ],
"rewards": [0.8, 0.3, 0.6, 0.5]
}
],
"lr": 0.002
}
| Field | Type | Required | Description |
|---|---|---|---|
| finetune_id | string | yes | The finetune ID to train |
| groups | array | yes | List of training groups |
| lr | number | no | Learning rate (default: 0.002) |
Each group:
| Field | Type | Required | Description |
|---|---|---|---|
| request | object | yes | The echoed request from /rollouts response |
| rollouts | array | yes | The rollout objects from /rollouts response (pass unchanged) |
| rewards | array | yes | Reward for each rollout (same length and order) |
You can mix skills across groups in the same train step.
Response
{}
GET /finetunes/:finetuneId/checkpoints
List saved checkpoints for a finetune.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Maximum results per page (1–100) |
| cursor | string | — | Pagination cursor from previous response |
Response
{
"checkpoints": [
{
"checkpoint_id": "01JXYZ...",
"finetune_id": "01HXYZ...",
"step": 100,
"created_at_ms": 1736937000000,
"updated_at_ms": 1736937600000
}
],
"next_cursor": "eyJzIjo1MDB9",
"has_more": true
}
| Field | Type | Description |
|---|---|---|
| checkpoint_id | string | Unique checkpoint identifier |
| finetune_id | string | Finetune identifier |
| step | integer | Training step number |
| created_at_ms | integer | Unix timestamp in milliseconds |
| updated_at_ms | integer | Unix timestamp in milliseconds |
Results are ordered by step (ascending). Only saved checkpoints are returned.
POST /finetunes/:finetuneId/checkpoints/save
Save the current checkpoint and make it visible in the dashboard. The server resolves the current step for the finetune.
Response
{
"ok": true,
"checkpoint": {
"checkpoint_id": "01JXYZ...",
"finetune_id": "01HXYZ...",
"step": 100,
"created_at_ms": 1736937000000,
"updated_at_ms": 1736937600000
}
}
Constraints:
- Returns 404 if the finetune does not exist or no checkpoint is available.
- Idempotent: repeated calls return success.
DELETE /finetunes/:finetuneId/checkpoints/:step
Delete a saved checkpoint. The checkpoint will be cleaned up shortly.
Response
{ "ok": true }
Constraints:
- Returns 404 if the checkpoint does not exist.
- Returns 410 if the checkpoint was already deleted.
Deleting the latest checkpoint will prevent resuming training for that finetune.
Using finetuned models for inference
Once you have saved a checkpoint, you can use it for inference by specifying the model parameter in your API requests.
Model format
moondream3-preview/{finetune_id}@{step}
For example, if your finetune ID is 01HXYZ... and you saved a checkpoint at step 500:
moondream3-preview/01HXYZ...@500
Example: Query with a finetuned model
curl -X POST https://api.moondream.ai/v1/query \
-H "X-Moondream-Auth: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "moondream3-preview/01HXYZ...@500",
"image_url": "data:image/jpeg;base64,...",
"question": "What do you see?"
}'
Supported endpoints
The model parameter is supported on the following inference endpoints:
| Endpoint | Description |
|---|---|
/v1/query | Question answering |
/v1/caption | Image captioning |
/v1/detect | Object detection |
/v1/point | Point localization |
/v1/batch | Batch processing (model specified at upload completion) |
Notes
- The
modelparameter is optional. If omitted, requests use the basemoondream3-previewmodel. - Only saved checkpoints can be used for inference. Use
POST /finetunes/:finetuneId/checkpoints/saveto save a checkpoint first. - Both the
finetune_idandstepare required in the model string. - Finetune inference is only available for authenticated API requests (not available in playground mode).