curl --request POST \
--url 'https://api.apimart.ai/v1/uploads/images?retention_days=3' \
--header 'Authorization: Bearer <token>' \
--form 'file=@/path/to/reference.png'
import requests
with open("reference.png", "rb") as file:
response = requests.post(
"https://api.apimart.ai/v1/uploads/images?retention_days=3",
headers={"Authorization": "Bearer <token>"},
files={"file": file}
)
response.raise_for_status()
result = response.json()
print(result["url"])
const input = document.querySelector('input[type="file"]');
const file = input?.files?.[0];
if (!file) throw new Error("Select a file first");
const formData = new FormData();
formData.append("file", file, file.name);
const response = await fetch("https://api.apimart.ai/v1/uploads/images?retention_days=3", {
method: "POST",
headers: { Authorization: "Bearer <token>" },
body: formData
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error?.message ?? `HTTP ${response.status}`);
}
console.log(result.url);
{
"url": "https://cdn.example/uploads/reference.png",
"filename": "reference.png",
"content_type": "image/png",
"file_type": "image",
"bytes": 235680,
"created_at": 1793836800,
"retention_days": 3,
"expires_at": 1794182400
}
{
"error": {
"message": "insufficient balance: uploading files requires a positive account balance, please top up",
"type": "insufficient_user_quota"
}
}
{
"error": {
"message": "image file exceeds maximum 31457280 bytes (30MB)",
"type": "invalid_request_error"
}
}
Upload Management
Upload Image
Upload a JPEG, PNG, GIF, or WebP image to get a public URL for generation APIs. Default file limit: 30MB.
POST
/
v1
/
uploads
/
images
curl --request POST \
--url 'https://api.apimart.ai/v1/uploads/images?retention_days=3' \
--header 'Authorization: Bearer <token>' \
--form 'file=@/path/to/reference.png'
import requests
with open("reference.png", "rb") as file:
response = requests.post(
"https://api.apimart.ai/v1/uploads/images?retention_days=3",
headers={"Authorization": "Bearer <token>"},
files={"file": file}
)
response.raise_for_status()
result = response.json()
print(result["url"])
const input = document.querySelector('input[type="file"]');
const file = input?.files?.[0];
if (!file) throw new Error("Select a file first");
const formData = new FormData();
formData.append("file", file, file.name);
const response = await fetch("https://api.apimart.ai/v1/uploads/images?retention_days=3", {
method: "POST",
headers: { Authorization: "Bearer <token>" },
body: formData
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error?.message ?? `HTTP ${response.status}`);
}
console.log(result.url);
{
"url": "https://cdn.example/uploads/reference.png",
"filename": "reference.png",
"content_type": "image/png",
"file_type": "image",
"bytes": 235680,
"created_at": 1793836800,
"retention_days": 3,
"expires_at": 1794182400
}
{
"error": {
"message": "insufficient balance: uploading files requires a positive account balance, please top up",
"type": "insufficient_user_quota"
}
}
{
"error": {
"message": "image file exceeds maximum 31457280 bytes (30MB)",
"type": "invalid_request_error"
}
}
Upload reference media and receive a public URL immediately, without polling. Retention tiers are 3, 7, 15, and 30 days. The 3-day tier is free within your account allowance; overage and longer retention require paid uploads. Check your actual free allowance and available tiers in the console.
The documentation Playground does not support file uploads. Use the cURL, Python, or JavaScript examples below. The JavaScript example requires an
<input type="file"> with a file selected on the page.curl --request POST \
--url 'https://api.apimart.ai/v1/uploads/images?retention_days=3' \
--header 'Authorization: Bearer <token>' \
--form 'file=@/path/to/reference.png'
import requests
with open("reference.png", "rb") as file:
response = requests.post(
"https://api.apimart.ai/v1/uploads/images?retention_days=3",
headers={"Authorization": "Bearer <token>"},
files={"file": file}
)
response.raise_for_status()
result = response.json()
print(result["url"])
const input = document.querySelector('input[type="file"]');
const file = input?.files?.[0];
if (!file) throw new Error("Select a file first");
const formData = new FormData();
formData.append("file", file, file.name);
const response = await fetch("https://api.apimart.ai/v1/uploads/images?retention_days=3", {
method: "POST",
headers: { Authorization: "Bearer <token>" },
body: formData
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error?.message ?? `HTTP ${response.status}`);
}
console.log(result.url);
{
"url": "https://cdn.example/uploads/reference.png",
"filename": "reference.png",
"content_type": "image/png",
"file_type": "image",
"bytes": 235680,
"created_at": 1793836800,
"retention_days": 3,
"expires_at": 1794182400
}
{
"error": {
"message": "insufficient balance: uploading files requires a positive account balance, please top up",
"type": "insufficient_user_quota"
}
}
{
"error": {
"message": "image file exceeds maximum 31457280 bytes (30MB)",
"type": "invalid_request_error"
}
}
Authentication
string
required
Use a valid, enabled, unexpired APIMart API Key as
Bearer <token>, with an account that is not suspended. The account balance must be positive. Paid uploads also require enough account balance and remaining API Key spending allowance to cover the upload. The free file allowance and the Key’s spending limit are separate limits. Get a Key from API Key management.Request parameters
integer
Optional:
3, 7, 15, or 30 days. An explicit value overrides the account default; when omitted, the currently effective console default applies, which is not necessarily 3 days. Only URL query parameters are accepted; a form field with this name has no effect. See the console for available tiers.file
required
Send one file, including its filename, in the
file field of a multipart/form-data request. Do not substitute JSON, a base64 string, or a file URL for the file part. Other form fields are ignored; if multiple file parts are sent, only the first is processed.Supported formats: JPEG (.jpg, .jpeg), PNG (.png), GIF (.gif), WebP (.webp)Default maximum file size: 30MB. File size limits can be configured. The active service configuration and size-limit error message take precedence.--form, Python files, or JavaScript FormData, let the client generate the Content-Type header with its boundary. Do not set this header manually.
File validation
The file type is detected from its content, not its extension or the MIME type supplied by the client. The content must match the endpoint; renaming a file does not convert it. A type mismatch returns HTTP400.
Images must fully decode, with at most 40MP total and no side exceeding 32768 pixels. GIF files may contain at most 500 frames.
| Format | Detected MIME type |
|---|---|
| JPEG (.jpg, .jpeg) | image/jpeg |
| PNG (.png) | image/png |
| GIF (.gif) | image/gif |
| WebP (.webp) | image/webp |
Retention and paid upload settings
| Retention tier | Charging rule | Free allowance |
|---|---|---|
| 3 days | Free within the allowance. Beyond it, enable paid uploads and confirm the tier price to pay by actual file size. | Uses the shared account allowance. |
| 7 / 15 / 30 days | Every file is charged at the selected tier, regardless of remaining free allowance. | Does not consume free slots. |
If your default is 7, 15, or 30 days, uploads without
retention_days are charged individually even when free slots remain. Set retention_days=3 to use the 3-day rules for a specific upload. All request examples above explicitly select 3 days.curl --request POST \
--url 'https://api.apimart.ai/v1/uploads/images?retention_days=7' \
--header 'Authorization: Bearer <token>' \
--form 'file=@/path/to/reference.png'
Successful response
HTTP200 returns file metadata. url is at the JSON root, without a data wrapper or a generation task ID.
The response example shows file metadata and omits optional quota and overage objects. Their return conditions are described below.
string
Public file URL. Pass it to the appropriate reference-media parameter of the target generation API. Each model still has its own format, size, count, and duration limits; a successful upload does not guarantee model compatibility.
string
Original filename.
string
MIME type detected by the server from the file content, not the client’s declared value.
string
File category. This endpoint returns
image.integer
Actual file size in bytes.
integer
Upload time as a Unix timestamp in seconds.
integer
Retention tier actually used for this file, in days:
3, 7, 15, or 30.integer
File expiration time as a Unix timestamp in seconds. Use this value to determine URL validity.
object
Returned only for charged uploads: 3-day overage or any 7 / 15 / 30-day upload.
charged indicates whether a charge was made; bytes is the billed file size; cost_usd is the charge in USD; price_per_gb_usd is the tier price per GB; retention_days is the billed tier. Free uploads omit this object.object
Optional shared free-allowance information for the account. Returned for the 3-day tier; longer tiers do not consume free slots and omit this object. See below for unlimited accounts or unavailable statistics.
| Field | Description |
|---|---|
quota.limit | Free file allowance for this account within the window. Use the returned value or console; this is not the total upload limit after enabling paid uploads. |
quota.used | Counted 3-day uploads within the window, including this successful upload. |
quota.remaining | Remaining free file slots. |
quota.window_hours | Window length in hours; defaults to 72. |
quota.reset_at | Time when the oldest batch leaves the window, in Unix seconds. Some slots become available then; the entire allowance does not reset at once. |
Shared quota and request rate limits
Images, videos, and audio share one account free allowance. Multiple API Keys do not add allowances. The window rolls on the hour and normally counts successful 3-day uploads over approximately the past 71–72 hours. Longer tiers do not consume free slots. Invalid files, oversized files, and storage failures do not consume slots. Use the console or returnedquota.limit for your allowance.
Ordinary quota-limited 3-day responses include X-Upload-Quota-Limit, X-Upload-Quota-Remaining, and X-Upload-Quota-Reset, corresponding to limit, remaining, and reset_at; Reset is in Unix seconds. Unlimited accounts receive quota: {"unlimited": true} without these headers.
Successful responses may omit quota when statistics are temporarily unavailable; longer tiers also omit it. Do not interpret its absence as zero or unlimited, or re-upload solely because it is absent.
After the 3-day free allowance is exhausted, uploads can continue with charges if paid uploads are enabled and the tier price is confirmed. Otherwise, handle 429 as described below. A separate limit of 1000 requests per Key per 60 seconds uses rate_limit_error and is independent of the free file allowance.
Error handling
Errors use a root-levelerror object. Inspect the HTTP status, error.type / error.code, and root-level overage fields instead of relying only on localized error.message text. Error overage differs from the successful billing object: it may contain available, enabled, needs_reconfirm, retention_days, price_per_gb_usd, optional settings_url, and reason. Enable paid uploads or reconfirm prices in the console.
| HTTP | Field / condition | Cause and action |
|---|---|---|
| 400 | invalid_request_error | Missing file, non-multipart request, empty file, wrong media type, unsupported format, or damaged content. Fix the file or endpoint before retrying. |
| 400 | retention_days | Invalid retention value or unavailable tier. Check the query parameter and available console tiers. |
| 401 / 403 | — | Invalid or disabled Key, or suspended account. Check authentication and account status. |
| 402 | insufficient_user_quota | Non-positive account balance or expired Key. Add balance or use a valid Key. Free uploads still require these access conditions. |
| 402 | overage.reason = overage_not_enabled | A long-retention tier was selected without enabling paid uploads. Enable them and confirm prices in the console. |
| 402 | overage.reason = needs_reconfirm | The long-retention price is unconfirmed or has increased. Confirm the current price before retrying. |
| 402 | overage.reason = insufficient_balance | Insufficient account balance for this upload. Top up before retrying. |
| 402 | overage.reason = insufficient_key_quota | The API Key spending cap leaves insufficient allowance. Increase the Key allowance before retrying. |
| 413 | invalid_request_error | The file exceeds the limit for its type. Compress or reduce it; use the current limit stated in the error message. |
| 429 | rate_limit_error | Too many requests from the Key. Reduce request frequency and retry with backoff. |
| 429 | overage.enabled = false | The 3-day free allowance is exhausted and paid uploads are disabled. Enable them and confirm prices in the console, or wait for free slots according to Retry-After. |
| 429 | overage.needs_reconfirm = true | The 3-day price requires confirmation. Reconfirm it in the console before retrying. |
| 429 | error.type = rate_limit_exceeded | Free allowance exhausted. If overage is absent, follow error.message; paid uploads may be unavailable. To wait for free slots, retry after the seconds in Retry-After. |
| 500 | server_error | Failed to write the file to storage. Retry later. |
| 503 | Retry-After | Billing or account allowance lookup is temporarily unavailable. Retry after the seconds specified by the header; this failed attempt is not charged. |
Using URLs and retaining files
The 72-hour free-allowance window is separate from file retention. Files expire according to the selected tier; use the returnedexpires_at for the actual expiration time. Expired URLs return 404. Keep original files and do not assume URLs are permanent.
For large upload volumes, consider your own object storage (OSS). If the model accepts public file URLs, use your own storage URLs directly to reduce repeated uploads and storage costs.