Claude Messages accepts a PDF in a document block with a base64 source, with and without a server prompt. For a large PDF, choose Opus/Sonnet with a 1M context window and respect the limits below. Chat Completions and Responses do not accept PDFs.
python
import base64
import anthropic
with open("report.pdf", "rb") as f:
pdf_b64 = base64.standard_b64encode(f.read()).decode("utf-8")
client = anthropic.Anthropic(
api_key=KEY,
base_url="https://api.guardrelay.ai",
)
message = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{"type": "document", "source": {
"type": "base64",
"media_type": "application/pdf",
"data": pdf_b64,
}},
{"type": "text", "text": "List the report's key findings"},
],
}],
)
print(message.content)- The PDF must be a standard, unencrypted document with no password.
- The full HTTP request, including base64, must fit within 12 MiB.
- server prompt also has a shared media budget: up to 4 items, 4 MiB of decoded data per item, and 8 MiB total.
- Without an applied server prompt, Messages allows up to 600 pages only with a 1M context window; with a smaller window the maximum is 100 pages. server prompt independently remains subject to its 4 MiB per-item media limit.
- Split a large or dense document across multiple requests.
- PDFs and text/content document sources count as input tokens. An invalid or oversized PDF with an applied server prompt is rejected before billable processing.
With an applied server prompt, the same document block can be nested in the tool_result.content array. Preserve the original tool_use_id; a PDF must have media_type: "application/pdf" and a base64 source. The same limits apply as for a regular user message.
- Kimi. Documents work differently: the file is uploaded with
POST /v1/filesfirst, then referenced in the chat request through aninput_fileblock. Formats, limits, quota and file lifetime are covered on Kimi · Chat Completions. - Composer and Grok. No documents. A document or file block in
contentis not recognised and returns a local400before any charge. Put the text you need into the message as plain text, keeping the 200K window of Composer and the 500K window of Grok in mind.