Last verified: August 13, 2026. The MiniMax File Management API, also described here as the MiniMax File API, is a supporting layer for other MiniMax services. It can accept source files for specific workflows, list uploaded files by purpose, return metadata for generated files, download generated content, and delete a file with its matching purpose. It does not analyze, transcribe, synthesize, or generate media by itself.
Independence and test notice: MiniMax-AI.chat is an independent website and is not affiliated with or endorsed by MiniMax. We reviewed this guide against MiniMax’s official File Management references and checked the JavaScript and Python syntax. We did not upload or delete a file in a live MiniMax account because no testing credential or non-sensitive test asset was available. Test the workflow with non-sensitive data in your own MiniMax account before using it in production.
What the five File API endpoints actually do
The focused MiniMax API guide covers credentials and the broader product surface. This page stays within file lifecycle operations. The same file_id can appear in another product’s request or task response, but each endpoint has a narrower role:
| Operation | Method and path | Required selector | Result |
|---|---|---|---|
| Upload | POST /v1/files/upload | purpose plus multipart file | File record with an ID and metadata |
| List | GET /v1/files/list | purpose query parameter | Array of matching file records |
| Retrieve | GET /v1/files/retrieve | file_id query parameter | JSON metadata for a generated file, including a documented download_url field |
| Retrieve content | GET /v1/files/retrieve_content | file_id query parameter | Generated file content rather than the metadata envelope |
| Delete | POST /v1/files/delete | file_id and matching purpose | Deleted file ID plus base_resp |
That Retrieve distinction is easy to miss. The official Retrieve File reference limits its documented file_id use to IDs returned by video generation and asynchronous speech synthesis tasks. It returns a JSON file object. The separate Retrieve Content reference describes downloading the generated file body. Do not promise that an arbitrary source upload can be read back through either route.
Purpose values are endpoint-specific
purpose is not a free-form tag. It tells MiniMax which downstream workflow owns the file. The accepted set differs across Upload, List, and Delete, so copying one global enum into every request creates avoidable parameter errors.
| Endpoint | Purpose values documented for that endpoint |
|---|---|
| Upload | voice_clone, prompt_audio, t2a_async_input, video_understanding, video_generation_input |
| List | voice_clone, prompt_audio, t2a_async_input, video_generation_input |
| Delete | voice_clone, prompt_audio, t2a_async, t2a_async_input, video_generation |
Those sets come from the separate Upload File schema, List Files schema, and Delete File schema. In particular, t2a_async and video_generation identify generated outputs for deletion; they are not upload purposes. video_understanding is an upload purpose but is not included in the documented List or Delete enums. video_generation_input is documented for Upload and List, but not in the current Delete enum. Keep a per-operation allowlist and recheck the appropriate reference before expanding it.
For MiniMax H3 reference inputs, upload with purpose=video_generation_input and pass the returned file as mm_file://{file_id}. These uploads remain valid for seven days. The current limits are JPG/JPEG/PNG/WebP/HEIC/HEIF images up to 30MB, MP4/MOV reference video up to 50MB, and WAV/MP3 reference audio up to 15MB. MiniMax says non-compliant H3 inputs return HTTP 400 and are not retained.
There is no single safe file-format or file-size rule for every purpose. Requirements belong to the downstream feature and can be narrower than a generic platform summary. Validate the asset against the purpose-specific product reference before sending bytes. For the separate M3 video-understanding flow, use the MiniMax M3 multimodal API guide; for H3 generation inputs, use the MiniMax H3 guide.
Authentication and response validation
All five endpoints use https://api.minimax.io and a Bearer credential. MiniMax’s API overview distinguishes a pay-as-you-go API key from a Token Plan key. Select the credential for the billing path you intend to use. Keep it in a server-side secret manager or environment variable; never embed it in browser JavaScript, a mobile bundle, a repository, or a downloadable WordPress asset.
# Set this only in your server or local development shell
export MINIMAX_API_KEY="replace-with-your-secret"
A successful HTTP status is not enough for the JSON endpoints. MiniMax responses include base_resp.status_code and base_resp.status_msg; this guide accepts the operation only when the status code equals 0. Preserve a request correlation value from your own application logs, but redact the key, uploaded content, download URL, and sensitive filenames.
Node.js: upload a file safely
This example uses a Node.js runtime that provides fetch, FormData, Blob, and AbortSignal.timeout. It buffers the example file in memory, so use it for a small test asset. A production service handling larger accepted inputs should use a reviewed streaming multipart implementation. Do not set the multipart Content-Type header yourself; the runtime must add the boundary.
import { readFile } from "node:fs/promises";
import { basename } from "node:path";
const API_BASE = "https://api.minimax.io";
const apiKey = process.env.MINIMAX_API_KEY;
if (!apiKey) throw new Error("MINIMAX_API_KEY is not set");
const PURPOSES = Object.freeze({
upload: new Set([
"voice_clone",
"prompt_audio",
"t2a_async_input",
"video_understanding",
"video_generation_input",
]),
list: new Set(["voice_clone", "prompt_audio", "t2a_async_input", "video_generation_input"]),
delete: new Set([
"voice_clone",
"prompt_audio",
"t2a_async",
"t2a_async_input",
"video_generation",
]),
});
function requirePurpose(operation, purpose) {
if (!PURPOSES[operation]?.has(purpose)) {
throw new Error(`Unsupported ${operation} purpose: ${purpose}`);
}
}
async function parseMiniMaxJson(response, operation) {
const raw = await response.text();
let body;
try {
body = JSON.parse(raw);
} catch {
throw new Error(`${operation} returned non-JSON data (HTTP ${response.status})`);
}
if (!response.ok) {
throw new Error(`${operation} failed with HTTP ${response.status}`);
}
if (body?.base_resp?.status_code !== 0) {
const code = body?.base_resp?.status_code ?? "missing";
const message = body?.base_resp?.status_msg ?? "No status message";
throw new Error(`${operation} failed: MiniMax ${code} — ${message}`);
}
return body;
}
function requireSafeJsonInteger(fileId) {
const value = typeof fileId === "number" ? fileId : Number(fileId);
if (!Number.isSafeInteger(value) || value <= 0) {
throw new Error(
"fileId must be a positive safe integer; use a lossless int64 JSON encoder for larger IDs"
);
}
return value;
}
async function uploadFile(filePath, purpose) {
requirePurpose("upload", purpose);
const bytes = await readFile(filePath);
const form = new FormData();
form.set("purpose", purpose);
form.set("file", new Blob([bytes]), basename(filePath));
const response = await fetch(`${API_BASE}/v1/files/upload`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: form,
signal: AbortSignal.timeout(120_000),
});
const body = await parseMiniMaxJson(response, "upload");
if (!body.file?.file_id) throw new Error("Upload response has no file_id");
if (
typeof body.file.file_id === "number" &&
!Number.isSafeInteger(body.file.file_id)
) {
throw new Error("Returned file_id needs a lossless int64 JSON parser");
}
return body.file;
}
const uploaded = await uploadFile("./speech-input.txt", "t2a_async_input");
console.log({
fileId: uploaded.file_id,
filename: uploaded.filename,
purpose: uploaded.purpose,
bytes: uploaded.bytes,
});
The documented upload response includes a file record with file_id, byte count, creation timestamp, filename, and purpose. Treat the ID as an opaque identifier: store it without arithmetic and bind it to the user, job, and purpose in your database. MiniMax types the field as an int64; JavaScript Number cannot represent every 64-bit integer exactly. Use a lossless JSON parser and integer encoder if an ID falls outside Number.isSafeInteger instead of allowing it to round.
Node.js: list and retrieve metadata
Use URL query parameters rather than putting a body on these GET requests. Listing requires a purpose; metadata retrieval requires a generated file_id.
async function listFiles(purpose) {
requirePurpose("list", purpose);
const url = new URL(`${API_BASE}/v1/files/list`);
url.searchParams.set("purpose", purpose);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
signal: AbortSignal.timeout(30_000),
});
const body = await parseMiniMaxJson(response, "list");
return Array.isArray(body.files) ? body.files : [];
}
async function retrieveFile(fileId) {
if (!fileId) throw new Error("fileId is required");
const url = new URL(`${API_BASE}/v1/files/retrieve`);
url.searchParams.set("file_id", String(fileId));
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
signal: AbortSignal.timeout(30_000),
});
const body = await parseMiniMaxJson(response, "retrieve metadata");
if (!body.file?.file_id) throw new Error("Retrieve response has no file record");
return body.file;
}
const inputs = await listFiles("t2a_async_input");
console.log(inputs.map(({ file_id, filename, purpose }) => ({
file_id,
filename,
purpose,
})));
// Use an ID returned by a completed video or async speech task here.
const generated = await retrieveFile(process.env.MINIMAX_GENERATED_FILE_ID);
console.log({
fileId: generated.file_id,
filename: generated.filename,
purpose: generated.purpose,
hasDownloadUrl: Boolean(generated.download_url),
});
Node.js: download generated content
The direct content route returns the generated file rather than the normal JSON metadata envelope. Validate the metadata first, generate the destination path inside a controlled directory, refuse to overwrite an existing file, and stream the response instead of loading it all into memory.
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
async function downloadGeneratedFile(fileId, outputPath) {
await retrieveFile(fileId); // Validates JSON metadata and base_resp first.
const url = new URL(`${API_BASE}/v1/files/retrieve_content`);
url.searchParams.set("file_id", String(fileId));
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
signal: AbortSignal.timeout(120_000),
});
if (!response.ok || !response.body) {
const detail = await response.text();
throw new Error(
`content download failed (HTTP ${response.status}): ${detail.slice(0, 200)}`
);
}
await pipeline(
Readable.fromWeb(response.body),
createWriteStream(outputPath, { flags: "wx" })
);
}
await downloadGeneratedFile(
process.env.MINIMAX_GENERATED_FILE_ID,
"./private-output/generated-media.bin"
);
Do not derive outputPath from an untrusted remote filename. A fixed private directory and an application-generated filename avoid path traversal and accidental public exposure. Check that the media signature and content type match the downstream task before serving or processing the file.
Node.js: delete with an explicit confirmation gate
Deletion is a state-changing operation. Require both the file ID and the matching delete-purpose value, verify account ownership in your application, and put a human or job-state confirmation in front of the call. Do not infer the purpose from a filename.
async function deleteFile(fileId, purpose, { confirmed = false } = {}) {
if (!confirmed) throw new Error("Deletion requires explicit confirmation");
if (!fileId) throw new Error("fileId is required");
requirePurpose("delete", purpose);
const numericFileId = requireSafeJsonInteger(fileId);
const response = await fetch(`${API_BASE}/v1/files/delete`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ file_id: numericFileId, purpose }),
signal: AbortSignal.timeout(30_000),
});
const body = await parseMiniMaxJson(response, "delete");
if (!body.file_id) throw new Error("Delete response has no file_id");
return body.file_id;
}
const deletedId = await deleteFile(
process.env.MINIMAX_FILE_ID_TO_DELETE,
"t2a_async_input",
{ confirmed: process.env.CONFIRM_DELETE === "yes" }
);
console.log({ deletedId });
Python: upload, list, retrieve, download, and delete
The Python version uses requests, keeps the same per-operation allowlists, and validates base_resp for every JSON operation.
python -m pip install requests
import os
from pathlib import Path
import requests
API_BASE = "https://api.minimax.io"
API_KEY = os.environ.get("MINIMAX_API_KEY")
if not API_KEY:
raise RuntimeError("MINIMAX_API_KEY is not set")
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
PURPOSES = {
"upload": {"voice_clone", "prompt_audio", "t2a_async_input", "video_understanding", "video_generation_input"},
"list": {"voice_clone", "prompt_audio", "t2a_async_input", "video_generation_input"},
"delete": {
"voice_clone", "prompt_audio", "t2a_async",
"t2a_async_input", "video_generation",
},
}
def require_purpose(operation, purpose):
if purpose not in PURPOSES[operation]:
raise ValueError(f"Unsupported {operation} purpose: {purpose}")
def normalize_file_id(file_id):
value = int(str(file_id))
if value <= 0:
raise ValueError("file_id must be a positive integer")
return value
def parse_minimax_json(response, operation):
response.raise_for_status()
body = response.json()
base = body.get("base_resp") or {}
if base.get("status_code") != 0:
raise RuntimeError(
f"{operation} failed: MiniMax {base.get('status_code')} — "
f"{base.get('status_msg', 'No status message')}"
)
return body
def upload_file(path, purpose):
require_purpose("upload", purpose)
path = Path(path)
with path.open("rb") as handle:
response = requests.post(
f"{API_BASE}/v1/files/upload",
headers=HEADERS,
data={"purpose": purpose},
files={"file": (path.name, handle)},
timeout=(10, 120),
)
body = parse_minimax_json(response, "upload")
return body["file"]
def list_files(purpose):
require_purpose("list", purpose)
response = requests.get(
f"{API_BASE}/v1/files/list",
headers=HEADERS,
params={"purpose": purpose},
timeout=30,
)
return parse_minimax_json(response, "list").get("files", [])
def retrieve_file(file_id):
file_id = normalize_file_id(file_id)
response = requests.get(
f"{API_BASE}/v1/files/retrieve",
headers=HEADERS,
params={"file_id": str(file_id)},
timeout=30,
)
return parse_minimax_json(response, "retrieve metadata")["file"]
def download_generated_file(file_id, output_path):
retrieve_file(file_id)
output_path = Path(output_path)
with requests.get(
f"{API_BASE}/v1/files/retrieve_content",
headers=HEADERS,
params={"file_id": str(file_id)},
timeout=(10, 120),
stream=True,
) as response:
response.raise_for_status()
with output_path.open("xb") as output:
for chunk in response.iter_content(chunk_size=1024 * 1024):
if chunk:
output.write(chunk)
def delete_file(file_id, purpose, confirmed=False):
if not confirmed:
raise RuntimeError("Deletion requires explicit confirmation")
require_purpose("delete", purpose)
file_id = normalize_file_id(file_id)
response = requests.post(
f"{API_BASE}/v1/files/delete",
headers={**HEADERS, "Content-Type": "application/json"},
json={"file_id": file_id, "purpose": purpose},
timeout=30,
)
return parse_minimax_json(response, "delete")["file_id"]
Errors, retries, and reconciliation
The official MiniMax error-code reference maps codes such as 1001 to request timeout, 1002 to rate limit, 1004 and 2049 to credential problems, 1008 to insufficient balance, and 2013 to invalid parameters. Use the returned code and message rather than matching human-readable text alone.
| Failure class | Application action |
|---|---|
| Timeout, rate limit, or documented retry-later server error | Use capped exponential backoff with jitter. Reconcile state before repeating Upload or Delete. |
| Invalid or inactive key | Stop, verify the credential source, and rotate a leaked key. Do not loop. |
| Insufficient balance or unavailable plan resources | Stop the job and route it to billing or quota handling. |
| Invalid parameters | Check the endpoint-specific purpose allowlist, file ID, multipart field, and JSON body. |
An upload retry can create another file record if the first response was lost after the server accepted the bytes. Record an application upload fingerprint, then list the appropriate purpose and reconcile by ID, filename, byte count, and job state before resending. Do not assume those fields alone prove identical content. For Delete, retrieve or list the intended record before the call and record the result after success.
Production security checklist
- Keep MiniMax credentials and all File API calls on a trusted server.
- Authorize every file ID against the signed-in user or workload; an opaque ID is not an access-control rule.
- Allow only the endpoint-specific purpose values required by the feature.
- Validate the local asset’s type, size, duration, and consent requirements against the downstream API before upload.
- For voice material, obtain the necessary permission and block impersonation or unauthorized cloning workflows.
- Use request and download timeouts, private output directories, non-overwriting writes, malware scanning where relevant, and controlled file permissions.
- Redact API keys, file contents, sensitive filenames, and download URLs from logs and analytics.
- Define deletion rules based on your product’s legal and operational requirements; do not invent a platform retention guarantee.
Frequently asked questions
Can the MiniMax File API analyze a document?
No. File Management stores or returns assets for supported downstream workflows. The model or generation endpoint performs the actual understanding, synthesis, or generation step.
Is Retrieve the same as Retrieve Content?
No. Retrieve returns JSON metadata for a documented generated file ID. Retrieve Content returns the generated file body. Use the first for validation and metadata, and the second when your server needs the bytes.
Can I list every file without a purpose?
The documented List endpoint requires a purpose query parameter. Query only a supported listed purpose and merge results in your application if more than one category is needed.
Should the upload request set multipart Content-Type?
Let the HTTP library construct that header with its generated multipart boundary. Set the Bearer authorization header, the purpose form field, and the file part.
Does HTTP 200 prove a JSON operation succeeded?
No. Validate base_resp.status_code === 0 and the required result field. Keep HTTP validation as a separate check.
Can the browser upload directly with my MiniMax key?
Do not expose a reusable MiniMax credential in client-side code. Send the user’s permitted asset to your authenticated back end, apply validation and consent rules there, and let that server call MiniMax.
