Files API (OpenAI Compatible)¶
Upload and manage files via an OpenAI-compatible interface. Files are stored in Amazon S3 and can be referenced directly in Chat Completions requests. Large files can be uploaded in parts using the Uploads API.
-
Simple Upload
Upload any file with a singlemultipart/form-datarequest. Files are immediately available for use in inference. -
Multipart Upload
Stream large files in parts via the Uploads API. Parts are assembled by S3 native multipart upload. -
Optional Expiry
Setexpires_afterto automatically expire files after a configurable number of seconds (1 hour to 30 days). -
Paginated Listing
List files with ascending or descending order and cursor-based pagination using theafterparameter. -
Chat Integration
Reference uploaded files directly in Chat Completions messages using"type": "file"content parts.
Available Endpoints¶
| Endpoint | Method | Description |
|---|---|---|
/v1/files |
POST |
Upload a file |
/v1/files |
GET |
List files with pagination |
/v1/files/{file_id} |
GET |
Retrieve file metadata |
/v1/files/{file_id} |
DELETE |
Delete a file |
/v1/files/{file_id}/content |
GET |
Download raw file bytes |
/v1/uploads |
POST |
Create a multipart upload session |
/v1/uploads/{upload_id}/parts |
POST |
Add a part to an upload session |
/v1/uploads/{upload_id}/complete |
POST |
Complete the upload and produce a file |
/v1/uploads/{upload_id}/cancel |
POST |
Cancel a pending upload session |
Feature Compatibility¶
| Feature | Status | Notes |
|---|---|---|
| Upload | ||
file (multipart) |
Required binary form field | |
purpose |
Accepted as any string; informational only | |
expires_after[anchor] |
Only "created_at" is accepted |
|
expires_after[seconds] |
Range: 3 600 – 2 592 000 (1 hour – 30 days) | |
| Listing | ||
order=asc / order=desc |
Ascending and descending supported; default desc |
|
after cursor |
Forward cursor pagination | |
limit |
1 – 10 000; default 10 000 | |
purpose filter |
Filter results by uploaded purpose | |
| File size cap | No artificial limit; S3 object limit (~5 TB) | |
| Expiry enforcement | Expired files return 404 at read time; S3 Lifecycle as backstop | |
| Chat integration | Use file_id in type: "file" content parts |
|
status field |
Always "processed" — no async processing pipeline |
Legend:
- Supported — Fully compatible with OpenAI API
- Partial — Supported with limitations or differences
- Unsupported — Not available in this implementation
- Extra Feature — Enhanced capability beyond OpenAI API
Quick Start¶
Upload a File¶
curl -X POST "$BASE/v1/files" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F "file=@document.pdf;type=application/pdf" \
-F "purpose=assistants"
Response:
{
"id": "file-0190c51c7de7455d9b8c2efe27dfbf67",
"object": "file",
"bytes": 102400,
"created_at": 1745000000,
"filename": "document.pdf",
"purpose": "assistants",
"status": "processed"
}
Upload with Expiry¶
curl -X POST "$BASE/v1/files" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F "file=@temp.txt;type=text/plain" \
-F "purpose=assistants" \
-F "expires_after[anchor]=created_at" \
-F "expires_after[seconds]=3600"
Expiry Semantics
Expiry is enforced lazily at read time: calls to retrieve metadata, download content, or reference the file in inference return HTTP 404 once the expiry time has passed. S3 Lifecycle rules clean up expired objects as a background backstop.
Retrieve Metadata¶
curl "$BASE/v1/files/file-0190c51c7de7455d9b8c2efe27dfbf67" \
-H "Authorization: Bearer $OPENAI_API_KEY"
List Files¶
# Default (newest first, up to 10 000 files)
curl "$BASE/v1/files" \
-H "Authorization: Bearer $OPENAI_API_KEY"
# Ascending, limit 20
curl "$BASE/v1/files?order=asc&limit=20" \
-H "Authorization: Bearer $OPENAI_API_KEY"
# Next page using after cursor
curl "$BASE/v1/files?order=asc&limit=20&after=file-0190c51c7de7455d9b8c2efe27dfbf67" \
-H "Authorization: Bearer $OPENAI_API_KEY"
# Filter by purpose
curl "$BASE/v1/files?purpose=fine-tune" \
-H "Authorization: Bearer $OPENAI_API_KEY"
Download Content¶
curl "$BASE/v1/files/file-0190c51c7de7455d9b8c2efe27dfbf67/content" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-o downloaded.pdf
Delete a File¶
curl -X DELETE "$BASE/v1/files/file-0190c51c7de7455d9b8c2efe27dfbf67" \
-H "Authorization: Bearer $OPENAI_API_KEY"
Response:
{
"id": "file-0190c51c7de7455d9b8c2efe27dfbf67",
"object": "file",
"deleted": true
}
Uploads API¶
The Uploads API lets you stream large files to S3 in parts without buffering the entire file in memory. Each upload session is backed by an S3 native multipart upload.
Upload ID format¶
Upload IDs use the same base32-encoded payload as file IDs. The prefix is swapped (upload_ → file-) when the upload is completed, so the resulting file ID is known upfront.
Create an Upload Session¶
curl -X POST "$BASE/v1/uploads" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"bytes": 6291456,
"filename": "large_dataset.bin",
"mime_type": "application/octet-stream",
"purpose": "assistants"
}'
Response:
{
"id": "upload_0190c51c7de7455d9b8c2efe27dfbf67",
"object": "upload",
"status": "pending",
"bytes": 6291456,
"filename": "large_dataset.bin",
"purpose": "assistants",
"created_at": 1745000000,
"expires_at": 1745086400
}
Add Parts¶
Each part except the last must be at least 5 MiB (S3 minimum part size). The last part may be any size.
curl -X POST "$BASE/v1/uploads/upload_0190c51c7de7455d9b8c2efe27dfbf67/parts" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F "data=@part1.bin"
Response:
{
"id": "part_0190c51c7de700010001abcdef012345",
"object": "upload.part",
"upload_id": "upload_0190c51c7de7455d9b8c2efe27dfbf67",
"created_at": 1745000001
}
Complete the Upload¶
curl -X POST "$BASE/v1/uploads/upload_0190c51c7de7455d9b8c2efe27dfbf67/complete" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"part_ids": [
"part_0190c51c7de700010001abcdef012345",
"part_0190c51c7de700020002fedcba987654"
]
}'
Response: A completed Upload object with the file field populated.
{
"id": "upload_0190c51c7de7455d9b8c2efe27dfbf67",
"object": "upload",
"status": "completed",
"bytes": 6291456,
"filename": "large_dataset.bin",
"purpose": "assistants",
"created_at": 1745000000,
"expires_at": 1745086400,
"file": {
"id": "file-0190c51c7de7455d9b8c2efe27dfbf67",
"object": "file",
"bytes": 6291456,
"created_at": 1745000005,
"filename": "large_dataset.bin",
"purpose": "assistants",
"status": "processed"
}
}
Cancel an Upload¶
curl -X POST "$BASE/v1/uploads/upload_0190c51c7de7455d9b8c2efe27dfbf67/cancel" \
-H "Authorization: Bearer $OPENAI_API_KEY"
Upload sessions expire after 1 day
If an upload is not completed within 1 day of creation it is automatically aborted. Parts uploaded to an expired session are discarded by S3.
Uploads Feature Compatibility¶
| Feature | Status | Notes |
|---|---|---|
bytes (declared size) |
Validated at completion against actual assembled size | |
filename |
Carried through to the final file object | |
mime_type |
Set as the S3 ContentType for the assembled object |
|
purpose |
Echoed to the final file object | |
| Part ordering | Caller controls order via part_ids list at completion |
|
md5 checksum |
Accepted but not validated | |
| Session TTL | 1 day from creation |
Using the OpenAI Python SDK (Uploads)¶
from openai import OpenAI
import io
client = OpenAI(base_url="http://localhost:8000/v1", api_key="...")
MIN_PART = 5 * 1024 * 1024 # 5 MiB — S3 minimum non-last part size
with open("large_file.bin", "rb") as fh:
content = fh.read()
# Create upload session
upload = client.uploads.create(
bytes=len(content),
filename="large_file.bin",
mime_type="application/octet-stream",
purpose="assistants",
)
# Upload parts (first part >= 5 MiB, last part any size)
part_a = client.uploads.parts.create(
upload_id=upload.id, data=io.BytesIO(content[:MIN_PART])
)
part_b = client.uploads.parts.create(
upload_id=upload.id, data=io.BytesIO(content[MIN_PART:])
)
# Complete — file is immediately available
completed = client.uploads.complete(
upload_id=upload.id, part_ids=[part_a.id, part_b.id]
)
file_id = completed.file.id
print(f"File ready: {file_id}")
# Cleanup
client.files.delete(file_id)
Chat Completions Integration¶
Reference an uploaded file inside a POST /v1/chat/completions message using "type": "file":
curl -X POST "$BASE/v1/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic.claude-haiku-4-5-20251001-v1:0",
"max_tokens": 512,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Summarise this document."
},
{
"type": "file",
"file": {
"file_id": "file-0190c51c7de7455d9b8c2efe27dfbf67"
}
}
]
}
]
}'
Model Support
Document and image file types are supported by models that accept those input modalities. Use a vision-capable model (e.g. Claude Haiku or Sonnet) when passing PDFs or images via file_id. Amazon Nova models do not currently support document inputs.
Using the OpenAI Python SDK¶
from openai import OpenAI
import io
client = OpenAI(base_url="http://localhost:8000/v1", api_key="...")
# Upload
with open("document.pdf", "rb") as f:
file = client.files.create(file=f, purpose="assistants")
# Reference in chat
response = client.chat.completions.create(
model="anthropic.claude-haiku-4-5-20251001-v1:0",
max_tokens=512,
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What is the key finding in this document?"},
{"type": "file", "file": {"file_id": file.id}},
],
}
],
)
print(response.choices[0].message.content)
# Cleanup
client.files.delete(file.id)
Error Reference¶
| HTTP | Cause |
|---|---|
| 400 | Invalid expires_after range, bad filename, size mismatch, or unknown part ID |
| 404 | File or upload not found, already deleted, expired, or not pending |
| 503 | AWS_S3_BUCKET is not configured |
Configuration¶
Files are stored in S3 under the prefix configured by AWS_S3_FILES_PREFIX (default: files/). Configure S3 Lifecycle rules on this prefix to automatically delete expired objects and apply Intelligent-Tiering.
Store files once, use them across requests. See Anthropic Files API for the Anthropic-compatible equivalent.