Files
The Files API lets you upload an asset once and reference it in inference requests via a file_id, instead of re-sending the bytes (or base64) on every turn. Fully compatible with the OpenAI Files API shape, and dual-emits the Anthropic Files API shape when the request carries an anthropic-version header — so both the official openai and anthropic SDKs work against it without any extension.
POST /v1/filesGET /v1/filesGET /v1/files/{file_id}GET /v1/files/{file_id}/contentDELETE /v1/files/{file_id}Limits
Section titled “Limits”| Purpose | Single-shot /v1/files | Total via /v1/uploads | Allowed mime types |
|---|---|---|---|
vision | 100 MB | 100 MB | image/png, image/jpeg, image/webp, image/gif |
video | 100 MB | 8 GB | video/mp4, video/webm, video/quicktime |
audio | 100 MB | 1 GB | audio/wav, audio/x-wav, audio/mpeg, audio/mp3, audio/flac, audio/ogg, audio/webm, audio/mp4, audio/x-m4a |
document | 100 MB | 1 GB | application/pdf |
Uploads default to a 30-day expiry — expires_at is returned on every file object. After expiry, references will fail with 410 file_expired. Files are also subject to soft delete via DELETE.
Upload a file
Section titled “Upload a file”POST /v1/filesMultipart form upload.
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The asset bytes |
purpose | string | No | One of vision, video, audio, document. Determines limits + which models can reference it. If omitted, inferred from the file’s MIME type — Anthropic SDK uploads (which don’t send purpose) work transparently |
Example
Section titled “Example”from openai import OpenAI
client = OpenAI( api_key="sk-your-api-key", base_url="https://api.aiand.com/v1",)
with open("photo.png", "rb") as f: file = client.files.create(file=f, purpose="vision")
print(file.id) # → file-abc123...import OpenAI from "openai";import fs from "node:fs";
const client = new OpenAI({ apiKey: "sk-your-api-key", baseURL: "https://api.aiand.com/v1",});
const file = await client.files.create({ file: fs.createReadStream("photo.png"), purpose: "vision",});
console.log(file.id); // → file-abc123...curl https://api.aiand.com/v1/files \ -H "Authorization: Bearer sk-your-api-key" \ -F "file=@photo.png" \ -F "purpose=vision"Response
Section titled “Response”{ "id": "file-abc123def456", "object": "file", "bytes": 482301, "purpose": "vision", "filename": "photo.png", "created_at": 1719450000, "expires_at": 1722042000}| Field | Type | Description |
|---|---|---|
id | string | Stable id with file- prefix. Use this in chat completion content parts. |
object | string | Always "file" |
bytes | integer | File size in bytes |
purpose | string | What you uploaded it for |
filename | string | null | Original filename if provided |
created_at | integer | Unix timestamp |
expires_at | integer | null | Unix timestamp; defaults to 30 days from upload |
page_count | integer | Only present on document uploads — number of pages in the PDF |
Reference a file in chat completions
Section titled “Reference a file in chat completions”Use a type: "file" content part inside a user message. Your client only ever sends the id; the bytes never leave the server again.
{ "model": "<vision-capable-model>", "messages": [{ "role": "user", "content": [ { "type": "text", "text": "What's in this image?" }, { "type": "file", "file": { "file_id": "file-abc123def456" } } ] }]}completion = client.chat.completions.create( model="<vision-capable-model>", messages=[{ "role": "user", "content": [ {"type": "text", "text": "What's in this image?"}, {"type": "file", "file": {"file_id": file.id}}, ], }],)
print(completion.choices[0].message.content)const completion = await client.chat.completions.create({ model: "<vision-capable-model>", messages: [{ role: "user", content: [ { type: "text", text: "What's in this image?" }, { type: "file", file: { file_id: file.id } }, ], }],});
console.log(completion.choices[0].message.content);Video example
Section titled “Video example”{ "model": "<video-capable-model>", "messages": [{ "role": "user", "content": [ { "type": "text", "text": "Describe what happens in this clip." }, { "type": "file", "file": { "file_id": "file-vid789..." } } ] }]}The wire shape is identical to images — you always send type: "file" and the file’s purpose determines how the model receives it.
Audio example
Section titled “Audio example”{ "model": "<audio-capable-model>", "messages": [{ "role": "user", "content": [ { "type": "text", "text": "Transcribe and summarize this clip." }, { "type": "file", "file": { "file_id": "file-aud456..." } } ] }]}Same shape. If you prefer to inline audio rather than upload it first, you can also send an OpenAI-shape { "type": "input_audio", "input_audio": { "data": "<base64>", "format": "wav" } } part directly.
Document (PDF) example
Section titled “Document (PDF) example”{ "model": "<vision-capable-model>", "messages": [{ "role": "user", "content": [ { "type": "text", "text": "Summarize this paper." }, { "type": "file", "file": { "file_id": "file-doc456..." } } ] }]}The wire shape is the same type: "file" reference. The model receives one image per page, so it must have the vision capability. Page count is available as page_count on GET /v1/files/{file_id}.
PDF processing happens at upload time, so inference latency is unaffected by document size.
List files
Section titled “List files”GET /v1/files| Query param | Type | Description |
|---|---|---|
purpose | string | Filter by purpose |
limit | integer | Page size, 1–100 (default 20) |
after | string | Cursor: pass the id of the last item from the previous page |
Returns files in reverse-chronological order (newest first).
Example
Section titled “Example”curl "https://api.aiand.com/v1/files?purpose=vision&limit=50" \ -H "Authorization: Bearer sk-your-api-key"Response
Section titled “Response”{ "object": "list", "data": [ { "id": "file-abc...", "object": "file", "bytes": 482301, "purpose": "vision", "filename": "photo.png", "created_at": 1719450000, "expires_at": 1722042000 } ], "has_more": false, "first_id": "file-abc...", "last_id": "file-abc..."}When has_more: true, pass last_id as the next request’s after. SDK paging (client.files.list().auto_paging_iter()) works automatically.
Retrieve file metadata
Section titled “Retrieve file metadata”GET /v1/files/{file_id}Returns the same shape as the upload response.
curl https://api.aiand.com/v1/files/file-abc123 \ -H "Authorization: Bearer sk-your-api-key"Download file content
Section titled “Download file content”GET /v1/files/{file_id}/contentReturns the raw bytes with Content-Type and Content-Disposition headers set from the upload metadata.
content = client.files.content("file-abc123")with open("downloaded.png", "wb") as f: f.write(content.read())curl https://api.aiand.com/v1/files/file-abc123/content \ -H "Authorization: Bearer sk-your-api-key" \ -o downloaded.pngDelete a file
Section titled “Delete a file”DELETE /v1/files/{file_id}Deletes the file. Subsequent inference requests referencing the id will fail.
curl -X DELETE https://api.aiand.com/v1/files/file-abc123 \ -H "Authorization: Bearer sk-your-api-key"Response
Section titled “Response”{ "id": "file-abc123", "object": "file", "deleted": true}Returns 404 if the file is missing or already deleted (idempotent semantics).
Errors
Section titled “Errors”| Status | error.code | When |
|---|---|---|
400 | invalid_request_error | Missing file/purpose, unknown purpose, mime not in allowlist, file exceeds size cap |
400 | model_capability_mismatch | Chat completion references a file whose purpose requires a capability the model lacks |
401 | invalid_api_key | Missing or invalid Authorization header |
404 | file_not_found | Unknown id, or id belongs to a different organization |
410 | file_expired | File past its expires_at |
500 | server_error | Internal failure |
- File ids are scoped to the organization that uploaded them. Cross-org references return
404. - For one-off small images you can still inline base64 via the standard
{type: "image_url", image_url: {url: "data:..."}}content part — but multi-turn conversations and large assets benefit from upload-once + reference.