PDF Input
OpenRoute supports PDF processing through the /api/v1/chat/completions
API. PDFs can be sent as direct URLs or base64-encoded data URLs in the messages array, via the file content type. This feature works on any model on OpenRoute.
URL support: Send publicly accessible PDFs directly without downloading or encoding
Base64 support: Required for local files or private documents that aren't publicly accessible
PDFs also work in the chat room for interactive testing.
When a model supports file input natively, the PDF is passed directly to the model. When the model does not support file input natively, OpenRoute will parse the file and pass the parsed results to the requested model.
Plugin Configuration
To configure PDF processing, use the plugins
parameter in your request. OpenRoute provides several PDF processing engines with different capabilities and pricing:
{
plugins: [
{
id: 'file-parser',
pdf: {
engine: 'pdf-text', // or 'mistral-ocr' or 'native'
},
},
],
}
Pricing
OpenRoute provides several PDF processing engines:
"mistral-ocr"
: Best for scanned documents or PDFs with images ($0.50 per 1,000 pages)."pdf-text"
: Best for well-structured PDFs with clear text content (Free)."native"
: Only available for models that support file input natively (charged as input tokens).
If you don't explicitly specify an engine, OpenRoute will default first to the model's native file processing capabilities, and if that's not available, we will use the "pdf-text"
engine.
Using PDF URLs
For publicly accessible PDFs, you can send the URL directly without needing to download and encode the file:
import requests
import json
url = "https://openroute.cn/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What are the main points in this document?"
},
{
"type": "file",
"file": {
"filename": "document.pdf",
"file_data": "https://bitcoin.org/bitcoin.pdf"
}
},
]
}
]
# Optional: Configure PDF processing engine
plugins = [
{
"id": "file-parser",
"pdf": {
"engine": "mistral-ocr"
}
}
]
payload = {
"model": "anthropic/claude-sonnet-4",
"messages": messages,
"plugins": plugins
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
const response = await fetch('https://openroute.cn/api/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer YOUR_API_KEY`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'anthropic/claude-sonnet-4',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'What are the main points in this document?',
},
{
type: 'file',
file: {
filename: 'document.pdf',
file_data: 'https://bitcoin.org/bitcoin.pdf',
},
},
],
},
],
// Optional: Configure PDF processing engine
plugins: [
{
id: 'file-parser',
pdf: {
engine: 'mistral-ocr',
},
},
],
}),
});
const data = await response.json();
console.log(data);
PDF URLs work with all processing engines. For Mistral OCR, the URL is passed directly to the service. For other engines, OpenRoute fetches the PDF and processes it internally.
Using Base64 Encoded PDFs
For local PDF files or when you need to send PDF content directly, you can base64 encode the file:
import requests
import json
import base64
from pathlib import Path
def encode_pdf_to_base64(pdf_path):
with open(pdf_path, "rb") as pdf_file:
return base64.b64encode(pdf_file.read()).decode('utf-8')
url = "https://openroute.cn/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
# Read and encode the PDF
pdf_path = "path/to/your/document.pdf"
base64_pdf = encode_pdf_to_base64(pdf_path)
data_url = f"data:application/pdf;base64,{base64_pdf}"
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What are the main points in this document?"
},
{
"type": "file",
"file": {
"filename": "document.pdf",
"file_data": data_url
}
},
]
}
]
# Optional: Configure PDF processing engine
# PDF parsing will still work even if the plugin is not explicitly set
plugins = [
{
"id": "file-parser",
"pdf": {
"engine": "pdf-text" # defaults to "pdf-text". See Pricing above
}
}
]
payload = {
"model": "google/gemma-3-27b-it",
"messages": messages,
"plugins": plugins
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
async function encodePDFToBase64(pdfPath: string): Promise<string> {
const pdfBuffer = await fs.promises.readFile(pdfPath);
const base64PDF = pdfBuffer.toString('base64');
return `data:application/pdf;base64,${base64PDF}`;
}
// Read and encode the PDF
const pdfPath = 'path/to/your/document.pdf';
const base64PDF = await encodePDFToBase64(pdfPath);
const response = await fetch('https://openroute.cn/api/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer $YOUR_API_KEY`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'google/gemma-3-27b-it',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'What are the main points in this document?',
},
{
type: 'file',
file: {
filename: 'document.pdf',
file_data: base64PDF,
},
},
],
},
],
// Optional: Configure PDF processing engine
// PDF parsing will still work even if the plugin is not explicitly set
plugins: [
{
id: 'file-parser',
pdf: {
engine: 'pdf-text', // defaults to "pdf-text". See Pricing above
},
},
],
}),
});
const data = await response.json();
console.log(data);
Skip Parsing Costs
When you send a PDF to the API, the response may include file annotations in the assistant's message. These annotations contain structured information about the PDF document that was parsed. By sending these annotations back in subsequent requests, you can avoid re-parsing the same PDF document multiple times, which saves both processing time and costs.
Here's how to reuse file annotations:
import requests
import json
import base64
from pathlib import Path
# First, encode and send the PDF
def encode_pdf_to_base64(pdf_path):
with open(pdf_path, "rb") as pdf_file:
return base64.b64encode(pdf_file.read()).decode('utf-8')
url = "https://openroute.cn/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
# Read and encode the PDF
pdf_path = "path/to/your/document.pdf"
base64_pdf = encode_pdf_to_base64(pdf_path)
data_url = f"data:application/pdf;base64,{base64_pdf}"
# Initial request with the PDF
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What are the main points in this document?"
},
{
"type": "file",
"file": {
"filename": "document.pdf",
"file_data": data_url
}
},
]
}
]
payload = {
"model": "google/gemma-3-27b-it",
"messages": messages
}
response = requests.post(url, headers=headers, json=payload)
response_data = response.json()
# Store the annotations from the response
file_annotations = None
if response_data.get("choices") and len(response_data["choices"]) > 0:
if "annotations" in response_data["choices"][0]["message"]:
file_annotations = response_data["choices"][0]["message"]["annotations"]
# Follow-up request using the annotations (without sending the PDF again)
if file_annotations:
follow_up_messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What are the main points in this document?"
},
{
"type": "file",
"file": {
"filename": "document.pdf",
"file_data": data_url
}
}
]
},
{
"role": "assistant",
"content": "The document contains information about...",
"annotations": file_annotations
},
{
"role": "user",
"content": "Can you elaborate on the second point?"
}
]
follow_up_payload = {
"model": "google/gemma-3-27b-it",
"messages": follow_up_messages
}
follow_up_response = requests.post(url, headers=headers, json=follow_up_payload)
print(follow_up_response.json())
import fs from 'fs/promises';
async function encodePDFToBase64(pdfPath: string): Promise<string> {
const pdfBuffer = await fs.readFile(pdfPath);
const base64PDF = pdfBuffer.toString('base64');
return `data:application/pdf;base64,${base64PDF}`;
}
// Initial request with the PDF
async function processDocument() {
// Read and encode the PDF
const pdfPath = 'path/to/your/document.pdf';
const base64PDF = await encodePDFToBase64(pdfPath);
const initialResponse = await fetch(
'https://openroute.cn/api/v1/chat/completions',
{
method: 'POST',
headers: {
Authorization: `Bearer $YOUR_API_KEY`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'google/gemma-3-27b-it',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'What are the main points in this document?',
},
{
type: 'file',
file: {
filename: 'document.pdf',
file_data: base64PDF,
},
},
],
},
],
}),
},
);
const initialData = await initialResponse.json();
// Store the annotations from the response
let fileAnnotations = null;
if (initialData.choices && initialData.choices.length > 0) {
if (initialData.choices[0].message.annotations) {
fileAnnotations = initialData.choices[0].message.annotations;
}
}
// Follow-up request using the annotations (without sending the PDF again)
if (fileAnnotations) {
const followUpResponse = await fetch(
'https://openroute.cn/api/v1/chat/completions',
{
method: 'POST',
headers: {
Authorization: `Bearer $YOUR_API_KEY`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'google/gemma-3-27b-it',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'What are the main points in this document?',
},
{
type: 'file',
file: {
filename: 'document.pdf',
file_data: base64PDF,
},
},
],
},
{
role: 'assistant',
content: 'The document contains information about...',
annotations: fileAnnotations,
},
{
role: 'user',
content: 'Can you elaborate on the second point?',
},
],
}),
},
);
const followUpData = await followUpResponse.json();
console.log(followUpData);
}
}
processDocument();
When you include the file annotations from a previous response in your
subsequent requests, OpenRoute will use this pre-parsed information instead
of re-parsing the PDF, which saves processing time and costs. This is
especially beneficial for large documents or when using the mistral-ocr
engine which incurs additional costs.
Response Format
The API will return a response in the following format:
{
"id": "gen-1234567890",
"provider": "DeepInfra",
"model": "google/gemma-3-27b-it",
"object": "chat.completion",
"created": 1234567890,
"choices": [
{
"message": {
"role": "assistant",
"content": "The document discusses..."
}
}
],
"usage": {
"prompt_tokens": 1000,
"completion_tokens": 100,
"total_tokens": 1100
}
}
Last updated on