OpenAI Files API and Uploads API
Status: DOCUMENTED · LIVE_VERIFIED (all 5 Files endpoints and all 4 Uploads endpoints called on 2026-09-18 with a project key; one LIVE_DISCOVERED behaviour on Uploads purposes).
Sources: Files reference · Uploads reference · File inputs guide · Batch guide · Retrieval guide · OpenAPI openapi-master.yaml.
Last verified: 2026-09-18. Machine-readable twins: generated/fragments/endpoints/openai-files-vectorstores-batch-finetuning-evals.json, generated/fragments/parameters/openai-files.json, openai-uploads.json, generated/fragments/objects/openai-platform-objects.json, generated/fragments/status-lifecycles/openai-lifecycles.json.
Files are the storage primitive shared by Batch, Fine-tuning, Evals, vector stores / file_search, and file inputs (input_file) to Responses. Uploads is the multipart variant for large files (> what a single POST /v1/files request can carry).
1. Endpoints
| Method / path | SDK (python / node) | Notes | 2026-09-18 |
|---|---|---|---|
POST /v1/files (multipart) |
client.files.create / client.files.create |
fields file, purpose, optional expires_after[anchor]=created_at, expires_after[seconds] |
200, purposes user_data, batch, evals, assistants |
GET /v1/files |
files.list |
?purpose=, limit 1–10 000 (default 10 000), order asc/desc, after cursor |
200 |
GET /v1/files/{file_id} |
files.retrieve |
200; 404 No such File object after delete |
|
DELETE /v1/files/{file_id} |
files.delete |
→ {object:"file", id, deleted:true} |
200 |
GET /v1/files/{file_id}/content |
files.content / files.retrieveContent |
raw bytes | 200 for batch_output; 400 Not allowed to download files of purpose: user_data |
POST /v1/uploads |
client.uploads.create |
JSON {filename, purpose, bytes, mime_type, expires_after?} |
200, status pending |
POST /v1/uploads/{id}/parts (multipart) |
uploads.parts.create |
field data (bytes chunk) → {object:"upload.part", id} |
200 |
POST /v1/uploads/{id}/complete |
uploads.complete |
{part_ids:[ordered], md5?} → status completed, file populated |
200 |
POST /v1/uploads/{id}/cancel |
uploads.cancel |
status cancelled; further parts → 400 |
200 |
No streaming, no beta header. All list endpoints use cursor pagination (after, limit) and return {object:"list", data, first_id, last_id, has_more}.
2. Purposes
purpose (request) |
Consumed by | Content rules | Download (/content) |
|---|---|---|---|
assistants |
vector stores / file_search, Assistants (legacy), code interpreter |
supported MIME list (see §5), ≤ 5M tokens for vector stores | not allowed for user content (docs) |
batch |
Batch API input_file_id |
.jsonl, ≤ 200 MB, ≤ 50 000 lines, one model per file |
input: not needed; outputs (batch_output) downloadable (verified) |
fine-tune |
training_file / validation_file |
.jsonl chat / preference / RFT formats |
results (fine-tune-results) downloadable |
vision |
vision fine-tuning images | JPEG/PNG/WEBP | — |
user_data |
Responses input_file, generic storage, vector stores |
any | 400 not allowed (verified) |
evals |
Evals run data_source.source.type=file_id |
.jsonl lines {"item":{…},"sample"?:{…}} |
— |
response-only: assistants_output, batch_output, fine-tune-results |
produced by the platform | downloadable |
Observation: evals is accepted by the API and by the guide, and the OpenAPI enum lists it as well; the File object enum (response side) does not list evals, yet the live response returned "purpose": "evals".
3. Limits (documented) and retention
| Item | Value | Source |
|---|---|---|
Max size per file (POST /v1/files) |
512 MB | Files reference |
| Project storage cap | 2.5 TB per project; no org-wide cap | Files reference |
| Upload rate limit | 1 000 requests/min per authenticated user | Files reference |
| Batch input | .jsonl ≤ 200 MB; ≤ 50 000 requests |
Files reference / Batch guide |
| Vector store ingestion | ≤ 512 MB and ≤ 5 000 000 tokens per file; 2 000 attached files/min/org | Retrieval guide / Files reference |
Responses input_file |
≤ 50 MB per file and per request (all files combined) | File inputs guide |
| Uploads API | one Upload ≤ 8 GB total; each Part ≤ 64 MB; Upload expires 1 hour after creation | Uploads reference (observed expires_at = created_at + 3600) |
expires_after.seconds |
3 600 – 2 592 000 (1 h – 30 d), anchor created_at only |
OpenAPI |
| Default expiry | purpose=batch files: 30 days (observed expires_at = created_at + 30 d); others: never (expires_at: null) |
Files reference (observed) |
Observed on 2026-09-18: expires_after given as multipart fields (expires_after[anchor], expires_after[seconds]=3600) produced expires_at = created_at + 3600 on a user_data file. On POST /v1/uploads, passing expires_after {seconds: 3600} produced the same expires_at as without it (upload TTL is 1 h either way); the resulting File had expires_at: null — so expires_after on Uploads is UNVERIFIED as a File expiry mechanism.
4. Objects
File (object: "file"): id (file-…), bytes, created_at, expires_at (int | null), filename, purpose, status (deprecated: uploaded | processed | error — observed processed immediately), status_details (deprecated).
Upload (object: "upload"): id (upload_…), bytes (declared), created_at, expires_at, filename, purpose, status (pending | completed | cancelled | expired), file (File, only after complete).
UploadPart (object: "upload.part"): id (part_…), created_at, upload_id.
Lifecycles: see generated/fragments/status-lifecycles/openai-lifecycles.json (upload, file).
5. Supported formats for retrieval (assistants / user_data into vector stores)
.c .cpp .cs .css .doc .docx .go .html .java .js .json .md .pdf .php .pptx .py .rb .sh .tex .ts .txt with the MIME types listed in the Retrieval guide; text/* must be utf-8 / utf-16 / ascii. Images and audio are not indexable. (Responses input_file accepts a wider set incl. rich documents; see the tools agent's docs/tools/ pages.)
6. Uploads protocol (large files)
POST /v1/uploads{filename, purpose, bytes, mime_type}→ Uploadpending. Declaredbytesmust equal the sum of parts at completion.POST /v1/uploads/{id}/partsfor each chunk (≤ 64 MB each, any order, parallel allowed) — multipart fielddata.POST /v1/uploads/{id}/complete{part_ids: [in order], md5?}→completed,file.idusable everywhere a File is.- Or
POST …/cancel→cancelled(parts can no longer be added; verified 400).
Observed: the OpenAPI enum for purpose on POST /v1/uploads is assistants | batch | fine-tune | vision, yet user_data was accepted (200) and propagated to the resulting File → LIVE_DISCOVERED.
7. Errors seen
| Call | HTTP | Body |
|---|---|---|
GET /files/{id}/content (user_data) |
400 | {"error":{"message":"Not allowed to download files of purpose: user_data","type":"invalid_request_error"}} |
GET /files/{deleted} |
404 | {"error":{"message":"No such File object: file-…","type":"invalid_request_error","param":"id"}} |
POST /uploads/{cancelled}/parts |
400 | Upload status is already in a cancelled state. Must be in a 'pending' state. |
8. Cost
File storage itself is not billed (vector-store storage is, see vector-stores.md). The 2026-09-18 tests cost $0 for this area.