●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Claude Files API Guide — Upload Once, Reference Anywhere in Your API Calls
Learn how to use the Claude Files API to upload PDFs, images, and text once and reference them across calls. Includes Python and TypeScript examples, a production-grade retry helper, real token cost estimates, and hard-won operational tips.
What Is the Claude Files API? — Upload Once, Use Everywhere
If you've been building with the Claude API, chances are you've run into a common pain point: re-uploading the same document every time you make a request. The Files API solves this by letting you upload a file once to Anthropic's secure storage and reference it by a unique file_id in any subsequent API call.
Here's why that matters for your workflow:
Lower bandwidth costs: No more sending the same 50-page PDF with every request
Cleaner code: Skip the Base64 encoding and multipart form data juggling
Team-friendly: Any API key in the same workspace can reference uploaded files
The Files API is currently in beta. To use it, include the header anthropic-beta: files-api-2025-04-14 with your requests.
As an indie developer, while organizing support replies for one of my apps, I noticed I was re-attaching the same bug-report screenshot to every request — sending the identical image over and over. After switching to the Files API, I could upload it once and reuse the same file_id for both classification and drafting a reply. It's a small change, but those small wins are what keep an operation stable over time.
Supported File Types and Size Limits
The Files API supports different file types, each mapped to a specific content block type:
Data analysis and visualization (with Code Execution Tool)
Size limits are generous:
Per file: Up to 500 MB
Total organization storage: Up to 500 GB
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A production-quality, idempotent upload helper (full working Python) that retries on failure and never double-registers a file
✦How to estimate the real cost savings from the Files API by working backward from per-page PDF token usage
✦Undocumented behavior — no server-side dedup, orphan file buildup — plus a use-case-by-use-case design guide
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
For images, use the image content block instead of document.
# Upload an imageimg = client.beta.files.upload( file=("screenshot.png", open("screenshot.png", "rb"), "image/png"),)# Ask Claude to analyze the imageresponse = client.beta.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ { "role": "user", "content": [ {"type": "text", "text": "What UI improvements would you suggest for this screenshot?"}, { "type": "image", "source": { "type": "file", "file_id": img.id, }, }, ], } ], betas=["files-api-2025-04-14"],)
4. Manage Your Files
You can list, inspect, and delete uploaded files at any time.
# List all uploaded filesfiles = client.beta.files.list()for f in files.data: print(f"{f.id}: {f.filename} ({f.size_bytes} bytes)")# Get metadata for a specific filemetadata = client.beta.files.retrieve_metadata("file_011CNha8iCJcU1wXNR6q4V8w")print(f"Created at: {metadata.created_at}")# Delete a fileclient.beta.files.delete("file_011CNha8iCJcU1wXNR6q4V8w")
Real-World Pattern — Batch Analysis Across Multiple Files
The real power of the Files API shows up when you need to reuse files across multiple requests. Consider a workflow where you upload 12 monthly reports and then run cross-document analysis.
import anthropicfrom pathlib import Pathclient = anthropic.Anthropic()# Upload 12 months of reports in one gofile_ids = []for month in range(1, 13): path = Path(f"reports/2025-{month:02d}.pdf") uploaded = client.beta.files.upload( file=(path.name, open(path, "rb"), "application/pdf"), ) file_ids.append(uploaded.id) print(f" Uploaded: {path.name} → {uploaded.id}")# Run cross-document trend analysiscontent_blocks = [ {"type": "text", "text": "Analyze all 12 monthly reports below. Identify revenue trends and areas for improvement."}]for fid in file_ids: content_blocks.append({ "type": "document", "source": {"type": "file", "file_id": fid}, })response = client.beta.messages.create( model="claude-sonnet-4-6", max_tokens=4096, messages=[{"role": "user", "content": content_blocks}], betas=["files-api-2025-04-14"],)print(response.content[0].text)# Cross-document analysis without re-uploading 12 PDFs each time
Writing an Upload Path That Survives Production
The basic code above assumes a stable network and that each file is registered exactly once. Real pipelines aren't that kind. Connections drop mid-upload, rate limits hit, and the same file gets registered twice. You need an upload path that expects all of this.
First, look at what the naive version gets wrong:
# Before — fragile, and can't prevent double-registrationdef upload_naive(client, path): uploaded = client.beta.files.upload( file=(path.name, open(path, "rb"), "application/pdf"), ) return uploaded.id
This has two weaknesses. First, the moment upload raises on a transient error (a dropped connection or a 429), the whole job stops. Second, because the Files API does not deduplicate content server-side, re-running this registers the same file under a brand-new file_id every time — quietly inflating your storage and management overhead.
The next version adds exponential-backoff retries and a local ledger to decide whether a file has already been uploaded.
# After — retries + content-hash idempotencyimport hashlibimport jsonimport timefrom pathlib import Pathimport anthropicdef file_sha256(path: Path) -> str: """Hash the file's contents (the key for dedup detection).""" h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest()def upload_idempotent( client: anthropic.Anthropic, path: Path, media_type: str, ledger_path: Path = Path(".files_ledger.json"), max_retries: int = 4,) -> str: """Upload each unique content hash once and record the file_id in a ledger.""" ledger = json.loads(ledger_path.read_text()) if ledger_path.exists() else {} digest = file_sha256(path) # If we've already registered this exact content, reuse the file_id if digest in ledger: print(f" reuse: {path.name} → {ledger[digest]}") return ledger[digest] last_error = None for attempt in range(1, max_retries + 1): try: uploaded = client.beta.files.upload( file=(path.name, open(path, "rb"), media_type), ) ledger[digest] = uploaded.id ledger_path.write_text(json.dumps(ledger, ensure_ascii=False, indent=2)) print(f" new: {path.name} → {uploaded.id}") return uploaded.id except (anthropic.APIConnectionError, anthropic.RateLimitError) as e: last_error = e wait = min(2 ** attempt, 30) # 2, 4, 8, 16 seconds (capped at 30) print(f" retry {attempt}/{max_retries} (waiting {wait}s): {e!r}") time.sleep(wait) except anthropic.APIStatusError: # Errors that won't fix themselves (e.g. 413 too large) — stop immediately raise raise RuntimeError(f"Upload failed: {path.name}") from last_error
Routing every upload through this helper gives you a pipeline that tolerates re-runs and never registers the same content twice. The ledger here is a single JSON file, but at scale you can swap it for SQLite or a KV store. The principle is what matters: key by content hash, and always check whether something is already uploaded before calling upload. For deeper dedup and reclaiming orphaned files, see Content-hash ledgers and orphan GC for the Files API.
What the Docs Don't Tell You — Production Lessons
Following the docs to a working call is easy. A few things only become clear once you run this continuously.
There is no automatic dedup. As noted above, identical content gets a fresh file_id on every upload. If you run a daily batch job without content-hash idempotency, the same PDF will pile up hundreds of times in storage. Preventing that at registration with a ledger is far easier than scrambling to delete after you hit the 500 GB cap.
Orphan files accumulate quietly. An uploaded file stays until you explicitly delete it. Any registration where you failed to record the file_id becomes an "orphan" you can only find via the list endpoint. A periodic job that runs files.list() and reclaims any file_id missing from your ledger will save you later.
You can't reference across workspaces. Most 404 File not found errors come from referencing a file_id registered with a key from a different workspace. If you split keys between production and development, design with the assumption that file_ids are scoped per workspace.
Watch for the missing beta header. If upload succeeds but messages.create fails, suspect a missing betas=["files-api-2025-04-14"] first — it's the fastest thing to rule out.
A Use-Case-by-Use-Case Design Guide
Which design fits comes down almost entirely to how many times you'll reference the file.
You use a file exactly once: Skip the Files API and embed it directly in the request as a document/image block. The upload-and-delete round trip is wasted effort.
You reference the same file a few to a few dozen times: This is the Files API's sweet spot. One upload plus file_id references lightens both bandwidth and code.
You reference the same large file very frequently with a shared prompt prefix: Combine the Files API with prompt caching. The Files API cuts re-transmission, but it does not reduce input-token billing; caching attacks the input-token cost.
You need image preprocessing: When resolution or noise makes recognition shaky, adding preprocessing before upload stabilizes results.
Common Errors and How to Fix Them
Here are the errors you're most likely to encounter, along with their solutions:
Error
Status
Cause & Fix
File not found
404
The file_id doesn't exist or belongs to a different workspace. Double-check the ID
Invalid file type
400
Mismatch between file format and content block (e.g., image in a document block). Use the correct block type
File too large
413
Exceeds the 500 MB limit. Split or compress the file
Storage limit exceeded
403
Your organization hit the 500 GB cap. Delete unused files
Common pitfall: Trying to use .docx or .csv files directly in a document block will fail. Convert .docx to PDF first, and for .csv files, either read them as plain text and include the content in your message body, or use the Code Execution Tool's container_upload block.
Pricing — Estimating the Real Cost
File management operations are completely free:
Upload, download, list, metadata retrieval, and deletion — all free
However, when you reference a file in a Messages request, the file's content counts as input tokens and is billed accordingly. If you reference the same file in 10 separate requests, you'll be charged for 10 sets of input tokens. There are no storage fees for keeping files on Anthropic's servers.
Misreading this throws off your estimates. The Files API cuts the bandwidth of re-transmission, but the input tokens Claude needs to read the content are incurred on every reference. PDF token counts vary with the ratio of text to figures, but for planning purposes it's safe to budget roughly 1,500–3,000 tokens per page.
For example, referencing a 30-page PDF once runs on the order of 50k–90k input tokens. An analysis pipeline that references it 20 times a day lands around 1–1.8M tokens daily. If your prompt prefix (instructions and shared context) is identical each time, route that portion through prompt caching to effectively compress the input cost. Cut bandwidth with the Files API and input tokens with prompt caching — two separate levers. That two-pronged setup is, in my view, the baseline for any high-frequency document pipeline.
Your Next Step
Grab one small PDF and upload it with this article's upload_idempotent helper. If running it twice reuses the file_id (instead of registering a new one), you're ready to wire it into a production pipeline. From there, to ground answers with citations, see Search-result content blocks and citation grounding; to orchestrate several tools at once, see Production patterns for parallel tool use.
Thanks for reading. I hope it gives a small foothold to anyone wrestling with the same cost-and-operations tradeoffs.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.