Process PDFs with Claude
Send your first PDF request
Start with a simple example using the Messages API. You can provide PDFs to Claude in three ways:
- As a URL reference to a PDF hosted online
- As a base64-encoded PDF in
documentcontent blocks - By a
file_idfrom the Files API
On Amazon Bedrock and Google Cloud, only base64-encoded sources are currently available. On Microsoft Foundry, the Files API is not supported for deployments hosted on Azure.
Option 1: URL-based PDF document
The simplest approach is to reference a PDF directly from a URL:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [{
"type": "document",
"source": {
"type": "url",
"url": "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
}
},
{
"type": "text",
"text": "What are the key findings in this document?"
}]
}]
}'ant messages create --transform content --format yaml <<'YAML'
model: claude-opus-5
max_tokens: 1024
messages:
- role: user
content:
- type: document
source:
type: url
url: https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf
- type: text
text: What are the key findings in this document?
YAMLclient = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "url",
"url": "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf",
},
},
{"type": "text", "text": "What are the key findings in this document?"},
],
}
],
)
print(message.content)const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "url",
url: "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
}
},
{
type: "text",
text: "What are the key findings in this document?"
}
]
}
]
});
console.log(response);var client = new AnthropicClient();
// Create document block with URL
var documentParam = new DocumentBlockParam
{
Source = new UrlPdfSource
{
Url = "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf",
},
};
// Create a message with document and text content blocks
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new List<ContentBlockParam>
{
documentParam,
new TextBlockParam("What are the key findings in this document?"),
},
},
],
});
Console.WriteLine(string.Join("\n", message.Content));client := anthropic.NewClient()
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewDocumentBlock(anthropic.URLPDFSourceParam{
URL: "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf",
}),
anthropic.NewTextBlock("What are the key findings in this document?"),
),
},
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Create document block with URL
DocumentBlockParam documentParam = DocumentBlockParam.builder()
.source(
UrlPdfSource.builder()
.url(
"https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
)
.build()
)
.build();
// Create a message with document and text content blocks
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(
List.of(
ContentBlockParam.ofDocument(documentParam),
ContentBlockParam.ofText(
TextBlockParam.builder()
.text("What are the key findings in this document?")
.build()
)
)
)
.build();
Message message = client.messages().create(params);
System.out.println(message.content());$client = new Client();
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'document',
'source' => [
'type' => 'url',
'url' => 'https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf',
],
],
[
'type' => 'text',
'text' => 'What are the key findings in this document?',
],
],
],
],
model: 'claude-opus-5',
);
echo $message;anthropic = Anthropic::Client.new
message = anthropic.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "url",
url: "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
}
},
{type: "text", text: "What are the key findings in this document?"}
]
}
]
)
puts(message.content)The response returns Claude's analysis as text blocks in content, with token consumption in usage:
{
"id": "msg_01Hfp8YuFjQ55VgWbpdHDehB",
"type": "message",
"role": "assistant",
"model": "claude-opus-5",
"content": [
{
"type": "text",
"text": "This document is an addendum to the Claude 3 model card, reporting updated evaluation results. The key findings include..."
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 45000,
"output_tokens": 300
}
}Option 2: Base64-encoded PDF document
If you need to send PDFs from your local system or when a URL isn't available:
# Method 1: Fetch and encode a remote PDF
curl -sL "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf" | base64 | tr -d '\n' > pdf_base64.txt
# Method 2: Encode a local PDF file
# base64 document.pdf | tr -d '\n' > pdf_base64.txt
# Create a JSON request file using the pdf_base64.txt content
jq -n --rawfile PDF_BASE64 pdf_base64.txt '{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [{
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": $PDF_BASE64
}
},
{
"type": "text",
"text": "What are the key findings in this document?"
}]
}]
}' > request.json
# Send the API request using the JSON file
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d @request.jsonant messages create \
--model claude-opus-5 \
--max-tokens 1024 \
--transform content \
--format yaml <<'YAML'
messages:
- role: user
content:
- type: document
source:
type: base64
media_type: application/pdf
data: "@./document.pdf"
- type: text
text: What are the key findings in this document?
YAMLimport base64
import httpx2
# First, load and encode the PDF
pdf_url = "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
pdf_data = base64.standard_b64encode(
httpx2.get(pdf_url, follow_redirects=True).content
).decode("utf-8")
# Alternative: Load from a local file
# with open("document.pdf", "rb") as f:
# pdf_data = base64.standard_b64encode(f.read()).decode("utf-8")
# Send to Claude using base64 encoding
client = anthropic.Anthropic()
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_data,
},
},
{"type": "text", "text": "What are the key findings in this document?"},
],
}
],
)
print(message.content)// Method 1: Fetch and encode a remote PDF
const pdfURL =
"https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf";
const pdfResponse = await fetch(pdfURL);
const arrayBuffer = await pdfResponse.arrayBuffer();
const pdfBase64 = Buffer.from(arrayBuffer).toString("base64");
// Method 2: Load from a local file
// import { readFile } from "node:fs/promises";
// const pdfBase64 = (await readFile('document.pdf')).toString('base64');
// Send the API request with base64-encoded PDF
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "base64",
media_type: "application/pdf",
data: pdfBase64
}
},
{
type: "text",
text: "What are the key findings in this document?"
}
]
}
]
});
console.log(response);var client = new AnthropicClient();
// Method 1: Download and encode a remote PDF
var pdfUrl = "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf";
using var httpClient = new HttpClient();
var pdfBase64 = Convert.ToBase64String(await httpClient.GetByteArrayAsync(pdfUrl));
// Method 2: Load from a local file
// var pdfBase64 = Convert.ToBase64String(await File.ReadAllBytesAsync("document.pdf"));
// Create document block with base64 data
var documentParam = new DocumentBlockParam
{
Source = new Base64PdfSource { Data = pdfBase64 },
};
// Create a message with document and text content blocks
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new List<ContentBlockParam>
{
documentParam,
new TextBlockParam("What are the key findings in this document?"),
},
},
],
});
Console.WriteLine(string.Join("\n", message.Content));// First, load and encode the PDF
pdfURL := "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
resp, err := http.Get(pdfURL)
if err != nil {
panic(err)
}
defer resp.Body.Close()
pdfBytes, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
pdfBase64 := base64.StdEncoding.EncodeToString(pdfBytes)
// Alternative: Load from a local file (add "os" to the imports)
// pdfBytes, err := os.ReadFile("document.pdf")
// pdfBase64 := base64.StdEncoding.EncodeToString(pdfBytes)
// Send to Claude using base64 encoding
client := anthropic.NewClient()
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewDocumentBlock(anthropic.Base64PDFSourceParam{
Data: pdfBase64,
}),
anthropic.NewTextBlock("What are the key findings in this document?"),
),
},
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Method 1: Download and encode a remote PDF
String pdfUrl =
"https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf";
HttpClient httpClient = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(pdfUrl)).GET().build();
HttpResponse<byte[]> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofByteArray()
);
String pdfBase64 = Base64.getEncoder().encodeToString(response.body());
// Method 2: Load from a local file
// byte[] fileBytes = Files.readAllBytes(Path.of("document.pdf"));
// String pdfBase64 = Base64.getEncoder().encodeToString(fileBytes);
// Create document block with base64 data
DocumentBlockParam documentParam = DocumentBlockParam.builder()
.source(Base64PdfSource.builder().data(pdfBase64).build())
.build();
// Create a message with document and text content blocks
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(
List.of(
ContentBlockParam.ofDocument(documentParam),
ContentBlockParam.ofText(
TextBlockParam.builder()
.text("What are the key findings in this document?")
.build()
)
)
)
.build();
Message message = client.messages().create(params);
System.out.println(message.content());$client = new Client();
// First, load and encode the PDF
$pdf_url = 'https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf';
$pdf_data = base64_encode(file_get_contents($pdf_url));
// Alternative: Load from a local file
// $pdf_data = base64_encode(file_get_contents('document.pdf'));
// Send to Claude using base64 encoding
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'document',
'source' => [
'type' => 'base64',
'media_type' => 'application/pdf',
'data' => $pdf_data,
],
],
[
'type' => 'text',
'text' => 'What are the key findings in this document?',
],
],
],
],
model: 'claude-opus-5',
);
echo $message;require "open-uri"
# First, load and encode the PDF
pdf_url = "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf"
pdf_bytes = URI.open(pdf_url, "rb") { |f| f.read }
pdf_data = [pdf_bytes].pack("m0") # Base64-encode without newlines
# Alternative: Load from a local file
# pdf_data = [File.binread("document.pdf")].pack("m0")
# Send to Claude using base64 encoding
anthropic = Anthropic::Client.new
message = anthropic.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "base64",
media_type: "application/pdf",
data: pdf_data
}
},
{type: "text", text: "What are the key findings in this document?"}
]
}
]
)
puts(message.content)Option 3: Files API
For PDFs you'll use repeatedly, or when you want to avoid encoding overhead, use the Files API:
# First, upload your PDF to the Files API
FILE_ID=$(curl -sS -X POST https://api.anthropic.com/v1/files \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-F "file=@document.pdf" | jq -r '.id')
# Then use the returned file_id in your message
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d @- <<EOF
{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [{
"type": "document",
"source": {
"type": "file",
"file_id": "$FILE_ID"
}
},
{
"type": "text",
"text": "What are the key findings in this document?"
}]
}]
}
EOF# First, upload your PDF to the Files API
FILE_ID=$(ant files upload \
--file ./document.pdf \
--transform id \
--raw-output)
# Then use the returned file_id in your message
ant messages create \
--transform content \
--format yaml <<YAML
model: claude-opus-5
max_tokens: 1024
messages:
- role: user
content:
- type: document
source:
type: file
file_id: $FILE_ID
- type: text
text: What are the key findings in this document?
YAMLclient = anthropic.Anthropic()
# Upload the PDF file
with open("/path/to/document.pdf", "rb") as f:
file_upload = client.files.upload(file=("document.pdf", f, "application/pdf"))
# Use the uploaded file in a message
message = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document",
"source": {"type": "file", "file_id": file_upload.id},
},
{"type": "text", "text": "What are the key findings in this document?"},
],
}
],
)
print(message.content)import Anthropic, { toFile } from "@anthropic-ai/sdk";
import fs from "node:fs";
const anthropic = new Anthropic();
// Upload the PDF file
const fileUpload = await anthropic.files.upload({
file: await toFile(fs.createReadStream("/path/to/document.pdf"), undefined, {
type: "application/pdf"
})
});
// Use the uploaded file in a message
const response = await anthropic.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "file",
file_id: fileUpload.id
}
},
{
type: "text",
text: "What are the key findings in this document?"
}
]
}
]
});
console.log(response);var client = new AnthropicClient();
// Upload the PDF file
var fileUpload = await client.Files.Upload(new FileUploadParams
{
File = new BinaryContent
{
Stream = File.OpenRead("/path/to/document.pdf"),
FileName = "document.pdf",
ContentType = new("application/pdf"),
},
});
// Use the uploaded file in a message
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new List<ContentBlockParam>
{
new DocumentBlockParam
{
Source = new FileDocumentSource { FileID = fileUpload.ID },
},
new TextBlockParam("What are the key findings in this document?"),
},
},
],
});
Console.WriteLine(string.Join("\n", message.Content));client := anthropic.NewClient()
// Upload the PDF file
pdfFile, err := os.Open("/path/to/document.pdf")
if err != nil {
panic(err)
}
defer pdfFile.Close()
fileUpload, err := client.Files.Upload(context.TODO(), anthropic.FileUploadParams{
File: anthropic.File(pdfFile, "document.pdf", "application/pdf"),
})
if err != nil {
panic(err)
}
// Use the uploaded file in a message
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewDocumentBlock(anthropic.FileDocumentSourceParam{
FileID: fileUpload.ID,
}),
anthropic.NewTextBlock("What are the key findings in this document?"),
),
},
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Upload the PDF file
FileMetadata file = client
.files()
.upload(FileUploadParams.builder().file(Path.of("/path/to/document.pdf")).build());
// Use the uploaded file in a message
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(
List.of(
ContentBlockParam.ofDocument(
DocumentBlockParam.builder().fileSource(file.id()).build()
),
ContentBlockParam.ofText(
TextBlockParam.builder()
.text("What are the key findings in this document?")
.build()
)
)
)
.build();
Message message = client.messages().create(params);
System.out.println(message.content());use Anthropic\Core\FileParam;
$client = new Client();
// Upload the PDF file
$file_upload = $client->files->upload(
file: FileParam::fromResource(fopen('/path/to/document.pdf', 'r'), contentType: 'application/pdf'),
);
// Use the uploaded file in a message
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'document',
'source' => [
'type' => 'file',
'fileID' => $file_upload->id,
],
],
[
'type' => 'text',
'text' => 'What are the key findings in this document?',
],
],
],
],
model: 'claude-opus-5',
);
echo $message;anthropic = Anthropic::Client.new
# Upload the PDF file
file_upload = File.open("/path/to/document.pdf", "rb") do |f|
anthropic.files.upload(
file: Anthropic::FilePart.new(f, filename: "document.pdf", content_type: "application/pdf")
)
end
# Use the uploaded file in a message
message = anthropic.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {type: "file", file_id: file_upload.id}
},
{type: "text", text: "What are the key findings in this document?"}
]
}
]
)
puts(message.content)How PDF support works
When you send a PDF to Claude, the following steps occur:
* The system converts each page of the document into an image.
* The text from each page is extracted and provided alongside each page's image.
* Documents are provided as a combination of text and images for analysis.
* This allows users to ask for insights on visual elements of a PDF, such as charts, diagrams, and other non-textual content.
Claude can reference both textual and visual content when it responds. You can further improve performance by integrating PDF support with:
* [Use prompt caching](https://platform.claude.com/docs/en/build-with-claude/pdf-support#use-prompt-caching): To improve performance for repeated analysis.
* [Process document batches](https://platform.claude.com/docs/en/build-with-claude/pdf-support#process-document-batches): For high-volume document processing.
* [Tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview): To extract specific information from documents for use as tool inputs.
Estimate your costs
The token count of a PDF file depends on the total text extracted from the document and the number of pages:
- Text token costs: Each page typically uses 1,500–3,000 tokens per page depending on content density. Standard API pricing applies with no additional PDF fees.
- Image token costs: Because each page is converted into an image, the same image-based cost calculations are applied.
You can use token counting to estimate costs for your specific PDFs.