One call, finished media
run submits the generation, polls with the backoff the API recommends, and resolves to the completed request with result.url.
At a glance
The Mage Python SDK is the official, open-source client for the Mage API, published as mage-space. Install it with "pip install mage-space", set MAGE_API_KEY, and call mage.run with a model id and a config to get finished images, video, or audio from any of 28 models. It runs on Python 3.10 or later, and every generation is paid in Gems.
3.10+
Python
httpx
HTTP client
28
Typed models
MIT
Open source
Setup
Add mage-space to your project.
pip install mage-spaceCreate a key in API → API Keys on mage.space and export it. The client reads MAGE_API_KEY.
export MAGE_API_KEY="mage_sk_..."Call run with a model id and its config. It waits for the result and returns it; the output is at result.url.
from mage_space import Mage
mage = Mage() # reads MAGE_API_KEY
request = mage.run("mango", {
"prompt": "Editorial portrait in soft daylight, 35mm film look",
"aspect_ratio": "4:5",
"model_id": "mango-v3",
})
print(request["result"]["url"])Examples
Each example starts from a client that reads MAGE_API_KEY.
from mage_space import Mage
mage = Mage() # reads MAGE_API_KEY
submitted = mage.generate("cherry", {
"prompt": "Waves rolling onto a black sand beach at sunset",
"resolution": "720p",
"duration": "5",
})
final = mage.requests.wait(
submitted["request_id"],
timeout=900,
on_update=lambda request: print(request["status"]),
)from mage_space import Mage
mage = Mage() # reads MAGE_API_KEY
clip = mage.uploads.upload("clip.mp4") # a path, bytes, or a binary file
mage.run("cherry", {
"prompt": "Restyle this clip as a watercolor painting",
"videos": [clip["url"]],
})from mage_space import Mage
mage = Mage() # reads MAGE_API_KEY
mage.characters.create(
name="Ana",
handle="ana",
image="https://example.com/ana.png",
)
mage.run("mango", {"prompt": "@ana walking through a night market"})from mage_space import Mage, MageAPIError, MageGenerationError
mage = Mage() # reads MAGE_API_KEY
try:
mage.run("mango", {"prompt": "A lighthouse at night"})
except MageAPIError as error:
if error.code == "insufficient_gems":
print("Gems needed:", error.body["error"]["gems_required"])
else:
raise
except MageGenerationError as error:
print("No output:", error.code)Why the SDK
run submits the generation, polls with the backoff the API recommends, and resolves to the completed request with result.url.
Every submission carries an idempotency key. Network errors and server errors are retried with backoff, and a retried submission is never charged again.
Files too large for a data URL go through a signed upload, with the ticket, the PUT, and the size check handled for you.
API refusals carry their HTTP status, error code, and request id; failed or cancelled generations and timeouts have their own error types.
Mage and AsyncMage share every method. The async client fits web servers, notebooks, and pipelines that run many generations at once.
Each model has a TypedDict, such as MangoConfig, generated from the API reference. Annotate a config with it and your type checker checks the fields.
Reference
The same methods cover every model; the model is the first argument.
| Method | What it does |
|---|---|
| mage.run(model, config) | Submit a generation, wait for it, and return the completed request. |
| mage.generate(model, config) | Submit a generation and return the request at once. |
| mage.requests.get / cancel / wait | Read, stop, or wait for a request by id. |
| mage.uploads.upload(data) | Upload a path, bytes, or file of up to 100 MB and return its URL. |
| mage.characters / mage.references | List, create, and delete saved characters and references. |
| mage.account.get() | Your Gems balance. |
| mage.architectures.list() | The live model catalog with options and prices. |
Models
Image · from 37 Gems
Photorealism that passes for a photograph
Image · from 135 Gems
Our flagship image family
Image · from 68 Gems
Crisp, readable text, up to 4K
Video · from 618 Gems
Our flagship video family
Video · from 245 Gems
Every video feature, at a friendlier price
Audio · from 19 Gems
Voices, music, and sound effects from one prompt
FAQ
Yes. AsyncMage has the same methods as Mage, awaited, and works as an async context manager.
Python 3.10 and later. Its only dependencies are httpx and typing-extensions.
Yes. Use Mage in scripts and notebooks, or AsyncMage with asyncio to run many generations at once. The account runs up to 20 generations in flight.
Yes. The SDKs and the ComfyUI nodes are MIT-licensed on GitHub, and installing them is free. You pay only for the generations you run, in Gems, at each model’s listed price.
Yes. Pass the new model’s id as a string and it runs; the SDK checks its config against the fields every model shares. A bot updates the types whenever the API reference changes, so new models arrive with their own types in the next release.
Every submission carries an Idempotency-Key. The SDK makes a new key per call and reuses it when it retries that call itself, so a retried submission returns the original request and charges nothing.
They are in beta at 0.x, so a minor version may still change them. The API itself is versioned separately and stays stable within v1.
There is no subscription or monthly fee. Each generation is paid in Gems at its model's listed price, and 1,000 Gems cost $1 on the standard pack. Every model page lists its default-settings price, and each response reports the exact charge. A generation that fails is refunded; one Mage's content policy blocks is not. A request your balance cannot cover is refused before anything is charged.
No. The API runs on Gems alone. Membership plans and their unlimited in-app generation do not apply to API requests, so any Mage account with Gems can call it.
Yes. Content you create with Mage, including through the API and MCP server, can be used commercially. Attribution is appreciated but not required.
One account, one Gems balance, every model. Pay only for what you generate.