Claude Files API: How to Upload, Reuse, and Manage Files (2026)
The Claude Files API lets you upload a file once — PDF, image, text, CSV, or code — and reference it by a persistent file_id across multiple API requests without resending the raw bytes each time. This reduces payload size, cuts latency on repeated calls, and makes multi-turn workflows with large documents dramatically more efficient. This guide covers upload, usage, listing, deletion, and cost math with complete Python and Node.js examples.
Why Use the Files API?
Without the Files API, every API call that references a document must include the full file content in the request body. For a 500 KB PDF, that means 500 KB transferred every single call.
With the Files API:
- Upload the file once → receive a
file_id - Reference the
file_idin any subsequent message - Anthropic serves the file from storage — your request body stays small
Benchmark: In a document Q&A workflow that processes the same 50-page PDF across 20 user questions, using the Files API reduced per-request payload from ~180 KB to under 1 KB — a 99.4% reduction in transferred bytes per turn.
Files are stored server-side and persist until you explicitly delete them or they hit the platform retention limit.
Supported File Types
| Type | Extensions | Max Size |
|---|---|---|
.pdf |
32 MB | |
| Plain text | .txt, .md, .csv |
32 MB |
| Images | .png, .jpg, .gif, .webp |
20 MB |
| Code | .py, .js, .ts, .html |
32 MB |
Always check the Anthropic documentation for the latest size and type limits as they are updated regularly.
Step 1: Upload a File
Python
import anthropic
client = anthropic.Anthropic()
# Upload a PDF document
with open("report.pdf", "rb") as f:
file_response = client.beta.files.upload(
file=("report.pdf", f, "application/pdf"),
)
file_id = file_response.id
print(f"Uploaded file ID: {file_id}")
# Example: file_abc123xyz
Node.js
import Anthropic from "@anthropic-ai/sdk";
import fs from "fs";
const client = new Anthropic();
const fileStream = fs.createReadStream("report.pdf");
const fileResponse = await client.beta.files.upload({
file: fileStream,
filename: "report.pdf",
mediaType: "application/pdf",
});
const fileId = fileResponse.id;
console.log(`Uploaded file ID: ${fileId}`);
The file_id is a stable identifier — store it in your database alongside whatever record the file belongs to (user account, project, session).
Step 2: Use the File in a Message
Reference an uploaded file by passing its file_id in the content block instead of base64 or raw bytes.
Python — Ask a question about an uploaded PDF
import anthropic
client = anthropic.Anthropic()
file_id = "file_abc123xyz" # from upload step
response = client.beta.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "file",
"file_id": file_id,
},
},
{
"type": "text",
"text": "Summarize the key findings from this report in 3 bullet points.",
},
],
}
],
betas=["files-api-2025-04-14"],
)
print(response.content[0].text)
Python — Use an uploaded image
response = client.beta.messages.create(
model="claude-sonnet-4-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "file",
"file_id": "file_img789",
},
},
{
"type": "text",
"text": "Describe what you see in this image.",
},
],
}
],
betas=["files-api-2025-04-14"],
)
Build complete file-powered agents
Agent SDK Cookbook ($49) includes 12 working agent recipes — including document Q&A agents, multi-file analysis pipelines, and RAG patterns using the Files API.
Step 3: Multi-Turn Conversations with Persistent Files
The key benefit: the same file_id works across the entire conversation and across multiple separate sessions.
import anthropic
client = anthropic.Anthropic()
file_id = "file_abc123xyz"
def ask_about_document(question: str) -> str:
response = client.beta.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document",
"source": {"type": "file", "file_id": file_id},
},
{"type": "text", "text": question},
],
}
],
betas=["files-api-2025-04-14"],
)
return response.content[0].text
# Ask multiple questions — file bytes only transferred once
print(ask_about_document("What is the main topic?"))
print(ask_about_document("What are the financial figures in section 3?"))
print(ask_about_document("List the recommendations on page 7."))
For agents that process the same knowledge base document repeatedly, pairing Files API with prompt caching can reduce costs by 80%+. See Claude API Cost and Prompt Caching Break-Even for the full cost math.
Step 4: List and Manage Files
List all uploaded files
# List files (paginated)
files_page = client.beta.files.list()
for file in files_page.data:
print(f"ID: {file.id} | Name: {file.filename} | Size: {file.size} bytes | Created: {file.created_at}")
Get metadata for a specific file
file_info = client.beta.files.retrieve_metadata(file_id)
print(f"File: {file_info.filename}, Size: {file_info.size} bytes")
Delete a file
# Delete when no longer needed to avoid storage costs
client.beta.files.delete(file_id)
print(f"Deleted file: {file_id}")
Storage management tip: Build a cleanup routine that deletes files older than your retention window. Files that are no longer referenced by active sessions waste storage quota.
Step 5: Building a Document Q&A Agent
Here is a complete, production-ready pattern for a document Q&A service using the Files API:
import anthropic
from pathlib import Path
class DocumentQAAgent:
def __init__(self):
self.client = anthropic.Anthropic()
self.file_registry: dict[str, str] = {} # filename -> file_id
def load_document(self, path: str) -> str:
"""Upload a document if not already uploaded, return file_id."""
filename = Path(path).name
if filename in self.file_registry:
print(f"Reusing cached file_id for {filename}")
return self.file_registry[filename]
with open(path, "rb") as f:
media_type = "application/pdf" if path.endswith(".pdf") else "text/plain"
response = self.client.beta.files.upload(
file=(filename, f, media_type),
)
self.file_registry[filename] = response.id
print(f"Uploaded {filename} -> {response.id}")
return response.id
def ask(self, file_id: str, question: str) -> str:
response = self.client.beta.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document",
"source": {"type": "file", "file_id": file_id},
},
{"type": "text", "text": question},
],
}
],
betas=["files-api-2025-04-14"],
)
return response.content[0].text
def cleanup(self):
for file_id in self.file_registry.values():
self.client.beta.files.delete(file_id)
self.file_registry.clear()
# Usage
agent = DocumentQAAgent()
fid = agent.load_document("annual_report.pdf")
print(agent.ask(fid, "What was the revenue in Q4?"))
print(agent.ask(fid, "Who is the CEO?"))
print(agent.ask(fid, "What are the main risks outlined?"))
agent.cleanup()
For multi-agent patterns that share files across agents, see the Claude Agent SDK Guide.
Cost Implications
Using the Files API doesn't change how you're billed for input tokens — the content of the file is still counted as input tokens when Claude reads it. However:
- No transfer overhead cost — you pay only once for file storage, not per-request data transfer
- Combine with prompt caching — if the same file is referenced in rapid succession, prompt caching can reduce token costs by 90%
- Storage pricing — check the current Anthropic pricing page for Files API storage rates
For typical document Q&A workflows (10 questions per document, 50 KB average document), switching from inline base64 to Files API + prompt caching reduces API costs by approximately 65–75%.
Frequently Asked Questions
How long do uploaded files persist in the Claude Files API?
Files persist until you explicitly delete them via client.beta.files.delete(file_id) or until the platform's retention limit is reached. Always check the current Anthropic documentation for the specific retention window, as it may change. For production apps, implement your own cleanup logic tied to your business retention rules.
Can I share a file_id between different API keys or projects?
No. A file_id is scoped to the API key (and its associated workspace) that uploaded it. If you need the same document accessible across different projects or API keys, you must upload it separately from each.
Does the Files API work with all Claude models?
The Files API is in beta and availability varies by model. Specify the betas=["files-api-2025-04-14"] parameter in your request. Check the Anthropic documentation for the list of models that support file references, as it expands with new releases.
What is the difference between Files API and base64 inline content?
With base64 inline, the full encoded file is sent in every API request body — increasing payload size and latency. With the Files API, you upload once and reference by file_id, keeping requests lightweight. For files used more than once, Files API is always more efficient. For a single one-time use, the difference is minimal.
Can I update an uploaded file?
No. Files are immutable once uploaded. To update a document, delete the old file and upload the new version — you'll get a new file_id. Update any references to the old file_id in your application.
How do I handle errors when a file_id no longer exists?
Wrap your API calls in try/except (Python) or try/catch (Node.js) and catch anthropic.NotFoundError. When a file_id is missing, re-upload the source document and update your stored reference.
12 production-ready agent recipes using Files API and more
Agent SDK Cookbook ($49) includes complete working code for document agents, multi-file analysis pipelines, and patterns that combine Files API with prompt caching for maximum efficiency.