xAI Files API — upload, list, metadata, content, public URLs, attachments
Status: DOCUMENTED + LIVE_VERIFIED (2026-09-19: upload txt+png with TTL, list (+filter), get, content, public-url create/idempotent/anon fetch/revoke, delete, input_file attachment in Responses). Machine-readable: parameters/xai-files-collections-batches.json, endpoints/xai-inference.json, objects/xai-inference-objects.json (File, …).
Sources: https://docs.x.ai/developers/rest-api-reference/files/upload · …/files/manage · …/files/download · https://docs.x.ai/developers/files/managing-files · https://docs.x.ai/developers/files/public-urls · https://docs.x.ai/developers/model-capabilities/files/chat-with-files · https://docs.x.ai/developers/pricing#files-and-collections-pricing · OpenAPI UploadFileMultipartRequest, File, ListFilesParams, CreatePublicUrlRequest/Response, RevokePublicUrlResponse
Last verified: 2026-09-19
Endpoints (https://api.x.ai, inference key)
| Method | Path | Body / query | Response | Live |
|---|---|---|---|---|
| POST | /v1/files |
multipart: file (required), purpose? (ignored, echoed ""), expires_after? 3600–2592000 s — must precede file (also OpenAI deepObject expires_after[anchor]=created_at&expires_after[seconds]=N) |
File |
200 (200 B txt, 95 B png) |
| GET | /v1/files |
limit ≤100 (100), order asc|desc (desc), sort_by created_at|filename|size, pagination_token, filter (AIP-160: name, file_id, size_bytes, content_type, created_at, expires_at, upload_status, user_defined_id, public_url), after (compat) |
{data[], pagination_token} (null on last page) |
200 |
| GET | /v1/files/{id} |
File (404 {code:"not-found", error:"File not found"} after delete/expiry) |
200/404 | |
| GET | /v1/files/{id}/content |
format=original|text |
raw bytes application/octet-stream |
200 bytes identical; format=text on txt → 404 {error:"Failed to retrieve file"} |
| DELETE | /v1/files/{id} |
{id, deleted:true, object:"file"} |
200 | |
| POST | /v1/files/{id}/public-url |
{} or {expires_after} |
{public_url, expires_at?} |
200 png; txt → 400 unsupported type; TTL overflow → 400 |
| POST | /v1/files/{id}/public-url/revoke |
{id, revoked:true, public_url} / {id, revoked:false} |
200 | |
| POST | /v1/files:initialize, /v1/files:uploadChunks, PUT /v1/files/{id} |
listed in the reference without fields (chunked upload used by the SDK) | UNVERIFIED |
File object
{"id":"file_8377ab50-…","object":"file","bytes":95,"created_at":1789789835,"expires_at":null,"filename":"atlas-pixel2.png","purpose":"",
"public_url":"https://files-cdn.x.ai/e7nuu0ZNSMGJuFIq_MFXmA/file_8377ab50-….png","public_url_expires_at":1789793436}purpose is stored for OpenAI-SDK compatibility only (echoed empty). Limits: 50 MB (spec) / 512 MB (guide) per file; text-based formats (txt, md, code, csv, json, pdf …). Storage $0.025/GiB/day, downloads $0.20/GiB. Files are team-scoped and permanent unless expires_after set.
Public URLs (docs + live)
- Eligible types: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm, application/pdf (live error lists exactly these). ≤50 MiB; ≤1 000 active URLs per team.
- Expiry: omit → inherits the file's expiry (never for permanent files);
expires_after1 h–30 d and ≤ file's remaining lifetime (live: file TTL 3600 +expires_after: 3600→ 400 "Set expires_after to at most 3599s, or omit it"). - Idempotent: second create returns the same URL; a different
expires_afterupdates the expiry in place (live: same URL +expires_atadded). Revoke → next create issues a new token. Deleting the file revokes the URL. - Anonymous
GETof the CDN URL → 200image/png(live).
Using files in chat
- Responses only:
{"type":"input_file","file_id":"file_…"}or{"type":"input_file","file_url":"https://…"}insidecontent[]. Attaching a file turns the request into an agentic attachment search ($10 per 1k calls; no visible tool item inoutput[]; live answer "PINEAPPLE" from a 200-byte txt, 294 input tokens, 200 reasoning tokens). Multiple files, mixing withinput_image, and combining withcode_interpreterare documented. No batch (n>1) mode. Multi-turn:previous_response_idor encrypted content. - Chat Completions
{"type":"file","file":{"file_id"}}→ 400 "File content is not supported on /v1/chat/completions. Please use /v1/responses instead." input_imagewithfile_id(spec) → live 400 "image_url must either be a base64-encoded image or a URL" (use a data URL or a public URL — e.g. the file's public URL).- Batch API JSONL input: upload the
.jsonlthenPOST /v1/batches {name, input_file_id}. - Imagine outputs can be stored directly as files (
storage_options) — see the images agent.
Examples: examples/xai/files/ (sh/py/ts). Tests: tests/xai/test_files.py.