Skip to content

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/files
GET /v1/files
GET /v1/files/{file_id}
GET /v1/files/{file_id}/content
DELETE /v1/files/{file_id}
PurposeSingle-shot /v1/filesTotal via /v1/uploadsAllowed mime types
vision100 MB100 MBimage/png, image/jpeg, image/webp, image/gif
video100 MB8 GBvideo/mp4, video/webm, video/quicktime
audio100 MB1 GBaudio/wav, audio/x-wav, audio/mpeg, audio/mp3, audio/flac, audio/ogg, audio/webm, audio/mp4, audio/x-m4a
document100 MB1 GBapplication/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.

POST /v1/files

Multipart form upload.

FieldTypeRequiredDescription
filefileYesThe asset bytes
purposestringNoOne 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
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...
{
"id": "file-abc123def456",
"object": "file",
"bytes": 482301,
"purpose": "vision",
"filename": "photo.png",
"created_at": 1719450000,
"expires_at": 1722042000
}
FieldTypeDescription
idstringStable id with file- prefix. Use this in chat completion content parts.
objectstringAlways "file"
bytesintegerFile size in bytes
purposestringWhat you uploaded it for
filenamestring | nullOriginal filename if provided
created_atintegerUnix timestamp
expires_atinteger | nullUnix timestamp; defaults to 30 days from upload
page_countintegerOnly present on document uploads — number of pages in the PDF

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)
{
"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.

{
"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.

{
"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.

GET /v1/files
Query paramTypeDescription
purposestringFilter by purpose
limitintegerPage size, 1–100 (default 20)
afterstringCursor: pass the id of the last item from the previous page

Returns files in reverse-chronological order (newest first).

Terminal window
curl "https://api.aiand.com/v1/files?purpose=vision&limit=50" \
-H "Authorization: Bearer sk-your-api-key"
{
"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.

GET /v1/files/{file_id}

Returns the same shape as the upload response.

Terminal window
curl https://api.aiand.com/v1/files/file-abc123 \
-H "Authorization: Bearer sk-your-api-key"
GET /v1/files/{file_id}/content

Returns 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())
DELETE /v1/files/{file_id}

Deletes the file. Subsequent inference requests referencing the id will fail.

Terminal window
curl -X DELETE https://api.aiand.com/v1/files/file-abc123 \
-H "Authorization: Bearer sk-your-api-key"
{
"id": "file-abc123",
"object": "file",
"deleted": true
}

Returns 404 if the file is missing or already deleted (idempotent semantics).

Statuserror.codeWhen
400invalid_request_errorMissing file/purpose, unknown purpose, mime not in allowlist, file exceeds size cap
400model_capability_mismatchChat completion references a file whose purpose requires a capability the model lacks
401invalid_api_keyMissing or invalid Authorization header
404file_not_foundUnknown id, or id belongs to a different organization
410file_expiredFile past its expires_at
500server_errorInternal 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.