Montagea API · v1
Developer API documentation
Call Montagea's self-hosted text, image and video models through an OpenAI-compatible API. Authenticate with a developer key and use paid credits or applicable Unlimited benefits. Free accounts do not include API access.
Get started
Create a key in Settings → Developer API. Every endpoint uses the base URL below. Keep the key on your server; never put it in a public web page or app.
export MONTAGEA_API_BASE="https://api.montagea.com/api/v1"
export MONTAGEA_API_KEY="mtg_..."Send the key as a Bearer token in the Authorization header on every request. Add an Idempotency-Key to generation requests so you can retry safely after a network failure.
Pricing
Standard credit-based API prices use the same billing logic as the website and are generated by the billing system. Personal API calls covered by an active Unlimited plan cost zero credits. USD equivalents use current credit-pack prices; larger packs lower the cost per credit.
| Model | Unit | Credits | Approx. USD |
|---|---|---|---|
| qwen-27b | Per million input tokens | 480 | $1.20–$2.40 |
| qwen-27b | Per million output tokens | 2,880 | $7.19–$14.40 |
| qwen-image | Per 1K image | 20 | $0.050–$0.10 |
| qwen-image | Per 2K image | 60 | $0.15–$0.30 |
| z-image | Per 1K image | 20 | $0.050–$0.10 |
| minimax-h3 | 5-second 720p video | 120 | $0.30–$0.60 |
| minimax-h3 | 10-second 720p video | 240 | $0.60–$1.20 |
| minimax-h3 | 15-second 720p video | 360 | $0.90–$1.80 |
- Charges are in credits; USD is for reference only. Credit-pack prices on the pricing page are authoritative.
- Text is billed on actual tokens, with a minimum of 1 credit per request.
- Video is billed per second, for clips of 4 to 15 seconds. Failed requests are not charged.
- Starter, Pro and Premium API calls use subscription or purchased credits; their website quotas do not apply. Unlimited Images covers Z-Image and Qwen Image (including 2K), Unlimited Videos covers H3 720p, and Studio covers both. Covered calls with personal API keys cost zero credits, need no credit balance and do not spend the credit budget. Other models use paid credits; channel keys retain their contracted billing. Bonus credits cannot fund API calls. Paid requests reserve their maximum cost and refund the unused part on completion. GET /models lists standard credit-based prices; text USD rates are a credit pricing basis. Key budgets are lifetime credit limits, including reservations.
Website work has priority. API requests are admitted only when spare capacity is available; busy capacity returns 429 with Retry-After and no charge. Already running inference is not preempted. API requests never start elastic video VMs. One API request per account may run at a time; submission rate is 60/minute per key.
Models and limits
Text
Model: qwen-27b. Supports system/developer/user/assistant/tool messages, text content arrays and user image_url parts. The 32,768-token context includes messages, tool definitions, schema instructions, images and reserved output; the complete input also has a 64,000-character limit excluding image bodies. Up to 10 PNG/JPEG/WebP images, 20 MiB each and 64 MiB combined; images are normalized to at most 1024 px before review and inference. Non-stream output is capped at 2,048 tokens, streaming at 8,192. Actual model usage determines the charge; unused reservation is refunded.
Images
Models: qwen-image (1K or 2K, up to 8 reference images) and z-image (1K, text-to-image only). Images per request (n): qwen-image up to 6 at 1K (4 with references) and 1 at 2K; z-image up to 8; never more than the account's concurrent image limit. Prompt up to 5,500 characters. Aspect ratios: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3. Optional seed 0–2147483647, incremented per image in a batch and wrapping to 0. A partly successful batch charges only for the images delivered.
Video
Model: minimax-h3. Text-to-video, first/last frames or reference media; 720p, 4–15 seconds. Optional seed: 0–2147483647. Aspect ratios: 16:9, 9:16, 1:1, 4:3, 3:4, 21:9; auto requires visual input. Poll the returned ID every 5 seconds. Video includes generated sound; reference audio does not guarantee lip-sync. No hosted models or automatic resubmissions.
References accept public HTTP(S) URLs without redirects; images also accept PNG/JPEG/WebP base64 data URLs. All downloads of one request must finish within 45 seconds. Each file up to 20 MiB, 64 MiB combined. Images must be non-animated, at most 16 megapixels, and are scaled to 2048 px on the long side after orientation. Video: MP4/MOV up to 4K, re-encoded to 24 fps and at most 1920 px wide, reviewed frame by frame; the model receives exactly that re-encoded file. Audio: MP3/WAV. Each clip, and each media type in total, is at most 15 seconds. The review payload must fit 14 MiB; compress clips if it does not. Invalid or unreviewable media is refused before any charge.
MiniMax accepts start_frame_url / end_frame_url OR reference_image_urls (up to 9), reference_video_urls (up to 3) and reference_audio_urls (up to 3), at most 12 reference files total. Audio references require a reference image or video. Five or more images, or any video/audio, limit output to 10 seconds; lighter inputs allow up to 15 seconds. GPU condition capacity may impose a lower limit; invalid combinations are rejected before billing. Frames follow their source aspect ratio.
OpenAI image controls: response_format=url (default) or b64_json. size accepts auto, 256x256, 512x512, 1024x1024, 1536x1024, 1024x1536, 1792x1024, 1024x1792 and 2048x2048 (Qwen only). Explicit sizes select a matching render bucket, then resize and center-crop to the exact requested dimensions; only 2048x2048 selects 2K billing. Conflicting resolution/aspect_ratio is rejected. Replaying the same Idempotency-Key with another response_format returns the same image without a new charge.
POST /videos accepts JSON or multipart. input_reference accepts a PNG/JPEG/WebP upload, image URL/data URL, or an object containing image_url, and maps to the first frame. File IDs are unsupported. size=1280x720 or 720x1280 selects orientation and delivers that exact MP4 size. Other native aspect ratios remain available without size. Statuses are queued, in_progress, completed and failed; created_at is Unix seconds. GET /videos/{id}/content downloads MP4 with the same key. Thumbnail, spritesheet, remix and extension are unsupported. GET /requests/{id} retains the previous status format.
Endpoints
| GET | /models | Models and current pricing |
| GET | /balance | Paid balance, unlimited models and key budget |
| POST | /chat/completions | Text completion (JSON or SSE) |
| POST | /images/generations | Synchronous image generation (up to 8 outputs) |
| POST | /images/edits | Qwen image editing (JSON or multipart) |
| POST | /videos | Submit an admitted video task (202) |
| GET | /videos/{id} | Read video status and result |
| GET | /videos/{id}/content | Download completed MP4 (authenticated) |
| GET | /requests/{id} | Recover a request by its ID |
Examples
Text
Request
curl "$MONTAGEA_API_BASE/chat/completions" \
-H "Authorization: Bearer $MONTAGEA_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: chat-example-1' \
-d '{"model":"qwen-27b","messages":[{"role":"user","content":"Describe a quiet mountain lake."}],"max_tokens":1024}'Response
{
"id": "req_8c41d2",
"object": "chat.completion",
"created": 1793102400,
"model": "qwen-27b",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "The lake lies perfectly still..." },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 18, "completion_tokens": 96, "total_tokens": 114 },
"montagea_usage": { "credits": 1 }
}Images
Request
curl "$MONTAGEA_API_BASE/images/generations" \
-H "Authorization: Bearer $MONTAGEA_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: image-example-1' \
-d '{"model":"qwen-image","prompt":"A quiet mountain lake at sunrise","resolution":"1K","aspect_ratio":"16:9","n":1}'Response
{
"id": "req_5b07e9",
"created": 1793102400,
"data": [{ "url": "https://.../req_5b07e9.png" }],
"montagea_usage": { "credits": 20 }
}Video
Request
curl "$MONTAGEA_API_BASE/videos" \
-H "Authorization: Bearer $MONTAGEA_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: video-example-1' \
-d '{"model":"minimax-h3","prompt":"A quiet mountain lake at sunrise","seconds":5,"resolution":"720p","aspect_ratio":"16:9"}'
# Poll every 5 seconds with the returned id
curl "$MONTAGEA_API_BASE/videos/req_3f2a9c" \
-H "Authorization: Bearer $MONTAGEA_API_KEY"Response
// POST /videos -> 202
{
"id": "req_3f2a9c",
"model": "minimax-h3",
"object": "video",
"kind": "video",
"status": "in_progress",
"created_at": 1793102400,
"progress": 0,
"seconds": "5",
"size": "1344x768",
"result": null,
"error": null,
"usage": { "credits": 0, "cost_nano_usd": null, "reserved_credits": 120 }
}
// GET /videos/req_3f2a9c once finished
{
"id": "req_3f2a9c",
"status": "completed",
"result": { "url": "https://.../req_3f2a9c.mp4", "seconds": 5 },
"error": null,
"usage": { "credits": 120, "cost_nano_usd": null, "reserved_credits": 0 },
...
}Qwen image editing
POST /images/edits accepts JSON with reference_image_urls or multipart image / image[] files, including OpenAI SDK images.edit. POST /images/generations also accepts reference_image_urls for qwen-image. Use the same resolution, aspect_ratio, seed and n parameters. Z-Image does not accept reference images or edits.
curl "$MONTAGEA_API_BASE/images/edits" \
-H "Authorization: Bearer $MONTAGEA_API_KEY" \
-H 'Idempotency-Key: edit-example-2' \
-F 'model=qwen-image' \
-F 'prompt=Change the background to a sunny garden' \
-F '[email protected]' \
-F 'resolution=1K' \
-F 'seed=42'MiniMax frames and reference media
// POST /videos (JSON): first/last frames
{
"model": "minimax-h3", "prompt": "A person turns toward the camera",
"start_frame_url": "https://your-storage.example/start.png",
"end_frame_url": "https://your-storage.example/end.png",
"seconds": 5, "seed": 42
}
// POST /videos (JSON): reference media, without first/last frames
{
"model": "minimax-h3", "prompt": "The person walks through the garden",
"reference_image_urls": ["https://your-storage.example/person.png"],
"reference_video_urls": ["https://your-storage.example/motion.mp4"],
"reference_audio_urls": ["https://your-storage.example/voice.wav"],
"seconds": 5, "seed": 42
}Text returns choices and usage; images return data[].url or data[].b64_json. Non-streaming generation results include montagea_usage.credits. Video includes object=video, progress, size, seconds, and result.url when completed; failures use error.code and error.message. usage retains charged/reserved credits. X-Request-ID and Inference-Id identify the admitted request. Storage links are shareable; the content download endpoint requires the owning API key.
Use the OpenAI SDK
Text, image generation/editing, and video create/retrieve/poll/download work with the OpenAI SDK. Set base_url to Montagea and use Montagea model IDs. Pass additional native controls in extra_body. Video uploads use input_reference; image uploads use image.
from openai import OpenAI
client = OpenAI(base_url="https://api.montagea.com/api/v1", api_key="mtg_...")
reply = client.chat.completions.create(
model="qwen-27b",
messages=[{"role": "user", "content": "Describe a quiet mountain lake."}],
max_tokens=1024,
extra_headers={"Idempotency-Key": "chat-example-2"},
)
print(reply.choices[0].message.content)
image = client.images.generate(
model="qwen-image",
prompt="A quiet mountain lake at sunrise",
n=1,
extra_body={"resolution": "1K", "aspect_ratio": "16:9"},
extra_headers={"Idempotency-Key": "image-example-2"},
)
print(image.data[0].url)
with open("portrait.png", "rb") as reference:
edited = client.images.edit(
model="qwen-image",
image=reference,
prompt="Change the background to a sunny garden",
extra_body={"resolution": "1K", "seed": 42},
extra_headers={"Idempotency-Key": "edit-example-1"},
)
print(edited.data[0].url)
encoded = client.images.generate(
model="qwen-image", prompt="A quiet lake",
size="1536x1024", response_format="b64_json",
extra_headers={"Idempotency-Key": "image-base64-1"},
)
import base64
with open("lake.png", "wb") as output:
output.write(base64.b64decode(encoded.data[0].b64_json))
video = client.videos.create_and_poll(
model="minimax-h3", prompt="A quiet lake at sunrise",
seconds="4", size="1280x720", poll_interval_ms=5000,
extra_headers={"Idempotency-Key": "video-sdk-1"},
)
if video.status == "completed":
client.videos.download_content(video.id).write_to_file("lake.mp4")
else:
print(video.error)Function tools support auto, none, required or a named tool_choice, plus parallel_tool_calls. Your application executes returned tools and sends role=tool results with matching tool_call_id values. response_format supports text, json_object and json_schema; structured output is validated before delivery, with no fallback to plain text. Schemas are limited to 16,000 characters each; acyclic local references are supported, while remote/dynamic references and regex constraints are rejected. Tool descriptions, arguments, results and schema content are reviewed too.
# Tool calls are returned to your app; Montagea does not execute them.
reply = client.chat.completions.create(
model="qwen-27b",
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
tools=[{"type": "function", "function": {
"name": "get_weather", "strict": True,
"parameters": {
"type": "object", "properties": {"city": {"type": "string"}},
"required": ["city"], "additionalProperties": False,
},
}}],
tool_choice="required",
)
print(reply.choices[0].message.tool_calls)
structured = client.chat.completions.create(
model="qwen-27b",
messages=[{"role": "user", "content": "Return the city Paris."}],
response_format={"type": "json_schema", "json_schema": {
"name": "city", "strict": True,
"schema": {
"type": "object", "properties": {"city": {"type": "string"}},
"required": ["city"], "additionalProperties": False,
},
}},
)
print(structured.choices[0].message.content)
import base64
with open("portrait.png", "rb") as image:
image_url = "data:image/png;base64," + base64.b64encode(image.read()).decode()
vision = client.chat.completions.create(
model="qwen-27b",
messages=[{"role": "user", "content": [
{"type": "text", "text": "Describe this picture."},
{"type": "image_url", "image_url": {"url": image_url}},
]}],
)
print(vision.choices[0].message.content)Streaming text
stream=true consumes the model’s live stream. For plain text, approved prefixes are released while generation continues, with at most three output reviews covering the full conversation and generated prefix. Initial checkpoints are around 80 and 1,024 characters; the last pass reviews the remainder. This is reviewed chunk delivery, not an unmoderated token relay. Tool arguments and JSON output remain buffered until complete validation. Set stream_moderation=buffered for whole-answer review before any content is sent. Keep-alives do not count as the first content token; latency is not guaranteed below five seconds. Default terminal usage can be disabled with stream_options.include_usage=false. Disconnecting does not cancel inference. On a later refusal or interruption, already approved content cannot be retracted and is billed using its recorded usage; the remainder is refunded. Retry the same Idempotency-Key or GET /requests/{id} to recover the recorded result. Partial retries do not generate again and report the original error before [DONE].
stream = client.chat.completions.create(
model="qwen-27b",
messages=[{"role": "user", "content": "Write a short poem about rain."}],
max_tokens=512,
stream=True,
stream_options={"include_usage": True},
extra_headers={"Idempotency-Key": "chat-stream-example-1"},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")Errors and retries
Errors include error.code, error.message and error.type, plus legacy detail fields. Rejected requests and generations that deliver no approved output are not charged. A failed stream may retain a billed approved prefix: inspect result.montagea_usage.partial and usage through /requests/{id}. artifact_unavailable means a completed, billed result cannot currently be read; retry with the same Idempotency-Key or video ID. model_capability_unavailable or tokenizer_unavailable needs an operator configuration check. streaming_price_unavailable means the channel rate does not cover incremental review; use stream_moderation=buffered or contact the operator. Payment cannot bypass review.
| 400 | content_not_allowed | The prompt or reply contains prohibited content. Not charged. |
| 401 | invalid_api_key | The key is missing, invalid or revoked. |
| 402 | insufficient_credits · key_budget_exceeded | Not enough paid credits, or the key budget is spent. |
| 403 | paid_account_required · model_not_allowed · account_suspended · channel_only | The account has no completed payment, the model is not allowed for this key, the account is suspended, or the key serves a Poe bot only (channel_only). |
| 404 | request_not_found | The request ID does not exist or belongs to another key. |
| 409 | idempotency_conflict · request_in_progress · price_changed | The same Idempotency-Key was sent with a different body, the original request is still running, or the price changed after authorization. |
| 410 | request_expired | The request is older than 30 days; its content has been deleted and it cannot be replayed. |
| 413 | reference_too_large | Reference files or request body exceed the size limit. Not charged. |
| 422 | invalid_request · invalid_reference · context_too_large · n_exceeds_limit | Invalid parameters, the conversation exceeds the context limit, a reference cannot be read, or n exceeds the account's concurrent image limit (n_exceeds_limit). |
| 429 | capacity_busy · account_busy · rate_limit | Capacity is busy, your account already has a request running, or the per-minute limit was hit. Retry after Retry-After. |
| 500 | generation_failed · submission_failed | Generation or submission failed; the reserved credits were returned. |
| 503 | api_unavailable · capacity_unavailable · content_check_unavailable · model_unavailable · artifact_unavailable · tokenizer_unavailable · model_capability_unavailable · streaming_price_unavailable | The API, capacity, content review or stored artifact is temporarily unavailable. Retry the same request later. |
Retries and results
Send a unique Idempotency-Key on every generation request. Reuse the same key and body after a network interruption to retrieve the original operation. A different body with the same key returns 409. A failed operation requires a new key for a new attempt. Revoking a credential stops new calls; an already accepted task can still settle.
Data and privacy
- Prompts, replies, reference snapshots and generated files are deleted automatically 30 days after the request; only billing records remain. We do not use API data to train any model.
- Prompts, supplied images/video/audio and text replies are sent to Google Gemini for prohibited-content review. Unreadable or unavailable review rejects the request before generation.
- Reference snapshots and generated images/videos are stored in Montagea storage and deleted with the request after 30 days. Storage URLs open without signing in; treat them as shareable links.