#### Files & Collections

# Managing Files

The Files API provides a complete set of operations for managing your files. If your files are publicly accessible, you can reference them directly by URL in chat conversations — see [Attaching Files](/developers/model-capabilities/files/chat-with-files#attaching-files). For files that aren't publicly accessible, upload them using one of the methods described below.

You can also view and manage all of your uploaded files from the [Files page](https://console.x.ai/team/default/files?utm_source=docs\&utm_medium=referral\&utm_campaign=developers-files-managing-files\&utm_content=files) on the xAI Console.

## Uploading Files

You can upload files in several ways: from a file path, raw bytes, BytesIO object, or an open file handle.

### Upload from File Path

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# Upload a file
with open("/path/to/your/document.pdf", "rb") as f:
    file = client.files.create(
        file=f,
        purpose="assistants"
    )

print(f"File ID: {file.id}")
print(f"Filename: {file.filename}")
```

```pythonRequests
import os
import requests

url = "https://api.x.ai/v1/files"
headers = {
    "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}

with open("/path/to/your/document.pdf", "rb") as f:
    files = {"file": f}
    data = {"purpose": "assistants"}
    response = requests.post(url, headers=headers, files=files, data=data)

file_data = response.json()
print(f"File ID: {file_data['id']}")
print(f"Filename: {file_data['filename']}")
```

```javascriptOpenAISDK
import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

// Upload a file
const file = await client.files.create({
    file: fs.createReadStream("/path/to/your/document.pdf"),
    purpose: "assistants",
});

console.log("File ID: " + file.id);
console.log("Filename: " + file.filename);
```

```javascriptWithoutSDK
import fs from "fs";

const formData = new FormData();
formData.append("file", new Blob([fs.readFileSync("/path/to/your/document.pdf")]), "document.pdf");
formData.append("purpose", "assistants");

const response = await fetch("https://api.x.ai/v1/files", {
  method: "POST",
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
  body: formData,
});

const file = await response.json();
console.log("File ID: " + file.id);
console.log("Filename: " + file.filename);
```

```bash
curl https://api.x.ai/v1/files \\
  -H "Authorization: Bearer $XAI_API_KEY" \\
  -F file=@/path/to/your/document.pdf \\
  -F purpose=assistants
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Upload a file from disk
file = client.files.upload("/path/to/your/document.pdf")

print(f"File ID: {file.id}")
print(f"Filename: {file.filename}")
print(f"Size: {file.size} bytes")
print(f"Created at: {file.created_at}")
```

### Upload from Bytes

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Upload file content directly from bytes
content = b"This is my document content.\\nIt can span multiple lines."
file = client.files.upload(content, filename="document.txt")

print(f"File ID: {file.id}")
print(f"Filename: {file.filename}")
```

### Upload from file object

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Upload a file directly from disk
file = client.files.upload(open("document.pdf", "rb"), filename="document.pdf")

print(f"File ID: {file.id}")
print(f"Filename: {file.filename}")
```

## Upload with Expiration (TTL)

Files default to permanent storage until you delete them. To have the platform automatically delete a file after a fixed window, set `expires_after` at upload time. This is useful for short-lived attachments, ephemeral session data, and compliance windows.

### How it works

`expires_after` is set in seconds, measured from upload time. It must be between `3600` (1 hour) and `2592000` (30 days), inclusive. Omit the field to keep the file permanently.

The response includes `expires_at`, the absolute UTC timestamp at which the file will be deleted. Once that time passes, the file is gone: it no longer appears in list responses, retrieving its metadata or content returns `not found`, and it can no longer be referenced by `id` in chat attachments.

You can also delete the file manually at any time before its TTL elapses.

> [!WARNING]
>
> **Multipart field ordering matters**: `expires_after` must appear **before** the `file` field in the multipart body. Requests that send `expires_after` after `file` are rejected with `400`.

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# expires_after follows the OpenAI shape: an object anchored at "created_at".
with open("/path/to/document.pdf", "rb") as f:
    file = client.files.create(
        file=f,
        purpose="assistants",
        expires_after={"anchor": "created_at", "seconds": 86400},  # 24 hours
    )

print(f"File ID: {file.id}")
print(f"Expires at: {file.expires_at}")  # Unix seconds, 24h from now
```

```pythonRequests
import os
import requests

url = "https://api.x.ai/v1/files"
headers = {
    "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}

# expires_after MUST appear before file in the multipart body — see the note below.
with open("/path/to/document.pdf", "rb") as f:
    response = requests.post(
        url,
        headers=headers,
        data=[
            ("expires_after", "86400"),  # 24 hours in seconds
            ("purpose", "assistants"),
        ],
        files={"file": f},
    )

file_data = response.json()
print(f"File ID: {file_data['id']}")
print(f"Expires at: {file_data['expires_at']}")
```

```javascriptOpenAISDK
import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

const file = await client.files.create({
    file: fs.createReadStream("/path/to/document.pdf"),
    purpose: "assistants",
    expires_after: { anchor: "created_at", seconds: 86400 },  // 24 hours
});

console.log("File ID: " + file.id);
console.log("Expires at: " + file.expires_at);
```

```javascriptWithoutSDK
import fs from "fs";

const formData = new FormData();
// expires_after MUST be appended before the file field.
formData.append("expires_after", "86400");  // 24 hours in seconds
formData.append("purpose", "assistants");
formData.append(
  "file",
  new Blob([fs.readFileSync("/path/to/document.pdf")]),
  "document.pdf",
);

const response = await fetch("https://api.x.ai/v1/files", {
  method: "POST",
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
  body: formData,
});

const file = await response.json();
console.log("File ID: " + file.id);
console.log("Expires at: " + file.expires_at);
```

```bash
# -F fields are sent in declaration order.
# expires_after must come before file in the form body.
curl https://api.x.ai/v1/files \\
  -H "Authorization: Bearer $XAI_API_KEY" \\
  -F expires_after=86400 \\
  -F purpose=assistants \\
  -F file=@/path/to/document.pdf
```

```pythonXAI
import os
from datetime import timedelta
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Upload a file that will be auto-deleted in 24 hours.
# expires_after accepts an int (seconds) or a datetime.timedelta.
file = client.files.upload(
    "/path/to/document.pdf",
    expires_after=timedelta(hours=24),
)

print(f"File ID: {file.id}")
print(f"Expires at: {file.expires_at.ToDatetime()}")
```

## Upload with Progress Tracking

Track upload progress for large files using callbacks or progress bars.

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Define a custom progress callback
def progress_callback(bytes_uploaded: int, total_bytes: int):
    percentage = (bytes_uploaded / total_bytes) * 100 if total_bytes else 0
    mb_uploaded = bytes_uploaded / (1024 * 1024)
    mb_total = total_bytes / (1024 * 1024)
    print(f"Progress: {mb_uploaded:.2f}/{mb_total:.2f} MB ({percentage:.1f}%)")

# Upload with progress tracking
file = client.files.upload(
    "/path/to/large-file.pdf",
    on_progress=progress_callback
)

print(f"Successfully uploaded: {file.filename}")
```

## Listing Files

Retrieve a list of your uploaded files with pagination and sorting options.

### Available Options

* **`limit`**: Maximum number of files to return. If not specified, uses the server default of 100. Maximum is 100.
* **`order`**: Sort order. Either `"asc"` (ascending) or `"desc"` (descending). Defaults to `"desc"`.
* **`sort_by`**: Field to sort by. Options: `"created_at"`, `"filename"`, or `"size"`. Defaults to `"filename"`.
* **`pagination_token`**: Pass the `pagination_token` returned by the previous response to fetch the next page. Omit it for the first page.

When more files remain, the response includes a `pagination_token`. On the last page it is `null` or empty.

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# List files
files = client.files.list()

for file in files.data:
    print(f"File: {file.filename} (ID: {file.id})")
```

```pythonRequests
import os
import requests

url = "https://api.x.ai/v1/files"
headers = {
    "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}

response = requests.get(url, headers=headers)
files = response.json()

for file in files.get("data", []):
    print(f"File: {file['filename']} (ID: {file['id']})")
```

```javascriptOpenAISDK
import OpenAI from "openai";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

// List files
const files = await client.files.list();

for (const file of files.data) {
    console.log(\`File: \${file.filename} (ID: \${file.id})\`);
}
```

```javascriptWithoutSDK
const response = await fetch("https://api.x.ai/v1/files", {
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
});

const files = await response.json();

for (const file of files.data) {
  console.log(\`File: \${file.filename} (ID: \${file.id})\`);
}
```

```bash
curl https://api.x.ai/v1/files \\
  -H "Authorization: Bearer $XAI_API_KEY"
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# List files with pagination and sorting
response = client.files.list(
    limit=10,
    order="desc",
    sort_by="created_at"
)

for file in response.data:
    expires = file.expires_at.ToDatetime() if file.HasField("expires_at") else "never"
    print(f"File: {file.filename} (ID: {file.id}, Size: {file.size} bytes, Expires: {expires})")
```

### Paginating Through All Files

The List endpoint returns at most `limit` files per call (capped at 100). To enumerate every file, keep calling the endpoint with the `pagination_token` from the previous response until the response no longer includes one.

```pythonRequests
import os
import requests

url = "https://api.x.ai/v1/files"
headers = {"Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"}

page_size = 100
params = {"limit": page_size, "order": "desc", "sort_by": "created_at"}
all_files = []

while True:
    response = requests.get(url, headers=headers, params=params).json()
    all_files.extend(response.get("data", []))
    if not response.get("pagination_token"):
        break
    params["pagination_token"] = response["pagination_token"]

print(f"Total files: {len(all_files)}")
```

```javascriptWithoutSDK
const pageSize = 100;
const baseParams = { limit: String(pageSize), order: "desc", sort_by: "created_at" };
const allFiles = [];
let token;

while (true) {
  const params = new URLSearchParams({ ...baseParams, ...(token ? { pagination_token: token } : {}) });
  const response = await fetch(\`https://api.x.ai/v1/files?\${params}\`, {
    headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
  });
  const page = await response.json();
  allFiles.push(...page.data);
  token = page.pagination_token;
  if (!token) break;
}

console.log(\`Total files: \${allFiles.length}\`);
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Walk every page until the API stops returning a pagination token.
page_size = 100
token = None
all_files = []

while True:
    response = client.files.list(
        limit=page_size,
        order="desc",
        sort_by="created_at",
        pagination_token=token,
    )
    all_files.extend(response.data)
    token = response.pagination_token
    if not token:
        break

print(f"Total files: {len(all_files)}")
```

## Getting File Metadata

Retrieve detailed information about a specific file.

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# Get file metadata
file = client.files.retrieve("file-abc123")

print(f"Filename: {file.filename}")
print(f"Size: {file.bytes} bytes")
# Unix seconds, or None if the file does not expire.
print(f"Expires at: {file.expires_at}")
```

```pythonRequests
import os
import requests

file_id = "file-abc123"
url = f"https://api.x.ai/v1/files/{file_id}"
headers = {
    "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}

response = requests.get(url, headers=headers)
file = response.json()

print(f"Filename: {file['filename']}")
print(f"Size: {file['bytes']} bytes")
```

```javascriptOpenAISDK
import OpenAI from "openai";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

// Get file metadata
const file = await client.files.retrieve("file-abc123");

console.log("Filename: " + file.filename);
console.log("Size: " + file.bytes + " bytes");
```

```javascriptWithoutSDK
const response = await fetch("https://api.x.ai/v1/files/file-abc123", {
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
});

const file = await response.json();
console.log("Filename: " + file.filename);
console.log("Size: " + file.bytes + " bytes");
```

```bash
curl https://api.x.ai/v1/files/file-abc123 \\
  -H "Authorization: Bearer $XAI_API_KEY"
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Get file metadata by ID
file = client.files.get("file-abc123")

print(f"Filename: {file.filename}")
print(f"Size: {file.size} bytes")
print(f"Created: {file.created_at}")
# expires_at is only set when the file was uploaded with expires_after
if file.HasField("expires_at"):
    print(f"Expires at: {file.expires_at.ToDatetime()}")
```

## Getting File Content

Download the raw bytes of an uploaded file. The endpoint streams the response, so it works for files of any supported size without buffering the whole payload in memory at the API layer.

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# Stream content straight to disk
client.files.content("file-abc123").write_to_file("downloaded.pdf")
```

```pythonRequests
import os
import requests

file_id = "file-abc123"
url = f"https://api.x.ai/v1/files/{file_id}/content"
headers = {"Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"}

# Stream to disk so large files don't sit in memory
with requests.get(url, headers=headers, stream=True) as response:
    response.raise_for_status()
    with open("downloaded.pdf", "wb") as f:
        for chunk in response.iter_content(chunk_size=1024 * 1024):
            f.write(chunk)
```

```javascriptOpenAISDK
import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

const response = await client.files.content("file-abc123");
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("downloaded.pdf", buffer);
```

```javascriptWithoutSDK
import fs from "fs";

const response = await fetch("https://api.x.ai/v1/files/file-abc123/content", {
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
});

const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("downloaded.pdf", buffer);
```

```bash
curl https://api.x.ai/v1/files/file-abc123/content \\
  -H "Authorization: Bearer $XAI_API_KEY" \\
  --output downloaded.pdf
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Returns the complete file content as bytes.
content = client.files.content("file-abc123")

# Save to disk
with open("downloaded.pdf", "wb") as f:
    f.write(content)

print(f"Saved {len(content)} bytes")
```

## Deleting Files

Remove files when they're no longer needed.

```pythonOpenAISDK
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("XAI_API_KEY"),
    base_url="https://api.x.ai/v1",
)

# Delete a file
delete_response = client.files.delete("file-abc123")

print(f"Deleted: {delete_response.deleted}")
print(f"File ID: {delete_response.id}")
```

```pythonRequests
import os
import requests

file_id = "file-abc123"
url = f"https://api.x.ai/v1/files/{file_id}"
headers = {
    "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}

response = requests.delete(url, headers=headers)
result = response.json()

print(f"Deleted: {result['deleted']}")
print(f"File ID: {result['id']}")
```

```javascriptOpenAISDK
import OpenAI from "openai";

const client = new OpenAI({
    apiKey: process.env.XAI_API_KEY,
    baseURL: "https://api.x.ai/v1",
});

// Delete a file
const deleteResponse = await client.files.delete("file-abc123");

console.log("Deleted: " + deleteResponse.deleted);
console.log("File ID: " + deleteResponse.id);
```

```javascriptWithoutSDK
const response = await fetch("https://api.x.ai/v1/files/file-abc123", {
  method: "DELETE",
  headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` },
});

const result = await response.json();
console.log("Deleted: " + result.deleted);
console.log("File ID: " + result.id);
```

```bash
curl -X DELETE https://api.x.ai/v1/files/file-abc123 \\
  -H "Authorization: Bearer $XAI_API_KEY"
```

```pythonXAI
import os
from xai_sdk import Client

client = Client(api_key=os.getenv("XAI_API_KEY"))

# Delete a file
delete_response = client.files.delete("file-abc123")

print(f"Deleted: {delete_response.deleted}")
print(f"File ID: {delete_response.id}")
```

## The File Object

Every Files API endpoint that returns metadata (Upload, List, Get Metadata) returns the same `file` object shape:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Unique file identifier (e.g. `file_a128090d-f0c9-4873-bd84-e499777e7417`). Use this anywhere a `file_id` is expected, including chat attachments. |
| `filename` | string | Original filename you supplied at upload time. |
| `bytes` | integer | File size in bytes. |
| `created_at` | integer | Upload time as a Unix timestamp (seconds). |
| `expires_at` | integer or null | Unix timestamp at which the file will be deleted. `null` for permanent files; set when the file was uploaded with `expires_after`. |
| `object` | string | Always `"file"`. Returned for OpenAI compatibility. |
| `purpose` | string | Echoes the `purpose` value sent at upload time. SpaceXAI does not enforce or interpret this field; it is stored for OpenAI SDK compatibility. Setting `"assistants"` is the conventional choice. |

## Limitations and Considerations

### File Size Limits

* **Maximum file size**: 512 MB per file
* **Processing time**: Larger files may take longer to process

### File Retention

* **Cleanup**: Delete files when no longer needed to manage storage
* **Access**: Files are scoped to your team/organization

### Supported Formats

While many text-based formats are supported, the system works best with:

* Structured documents (with clear sections, headings)
* Plain text and markdown
* Documents with clear information hierarchy

Supported file types include:

* Plain text files (.txt)
* Markdown files (.md)
* Code files (.py, .js, .java, etc.)
* CSV files (.csv)
* JSON files (.json)
* PDF documents (.pdf)
* And many other text-based formats

## Next Steps
