Lunos logoLunos

Multimodal PDFs

Lunos supports PDF inputs through POST /v1/chat/completions using a compatible message format.

You can send PDFs as:

  • Public URL
  • Base64 data URL (data:application/pdf;base64,...)

Endpoint

POST /v1/chat/completions

Authentication

Authorization: Bearer YOUR_SECRET_KEY
Content-Type: application/json

File content shape

{
  "type": "file",
  "file": {
    "filename": "document.pdf",
    "file_data": "https://example.com/document.pdf"
  }
}

Lunos accepts file_data, fileData, and url, then normalizes the payload before forwarding.

Plugin configuration (optional)

Use plugins to choose parser behavior:

{
  "plugins": [
    {
      "id": "file-parser",
      "pdf": {
        "engine": "cloudflare-ai"
      }
    }
  ]
}

Common engines:

  • cloudflare-ai
  • mistral-ocr
  • native

Pricing behavior

PDF cost can include:

  • Model token usage
  • Parser processing
  • OCR processing for scanned pages

Scanned PDFs are usually more expensive than text PDFs because OCR adds extra processing.

Example with PDF URL

cURL

curl -X POST "https://api.lunos.tech/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-pro",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "What are the main points in this document?" },
          {
            "type": "file",
            "file": {
              "filename": "bitcoin.pdf",
              "file_data": "https://bitcoin.org/bitcoin.pdf"
            }
          }
        ]
      }
    ],
    "plugins": [
      {
        "id": "file-parser",
        "pdf": { "engine": "mistral-ocr" }
      }
    ]
  }'

Python

import requests

payload = {
    "model": "google/gemini-2.5-pro",
    "messages": [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What are the main points in this document?"},
                {
                    "type": "file",
                    "file": {
                        "filename": "bitcoin.pdf",
                        "file_data": "https://bitcoin.org/bitcoin.pdf",
                    },
                },
            ],
        }
    ],
    "plugins": [
        {"id": "file-parser", "pdf": {"engine": "mistral-ocr"}}
    ],
}
response = requests.post(
    "https://api.lunos.tech/v1/chat/completions",
    headers={
        "Authorization": "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
    },
    json=payload,
)
print(response.json())
const response = await fetch("https://api.lunos.tech/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_SECRET_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "google/gemini-2.5-pro",
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "What are the main points in this document?" },
          {
            type: "file",
            file: {
              filename: "bitcoin.pdf",
              file_data: "https://bitcoin.org/bitcoin.pdf",
            },
          },
        ],
      },
    ],
    plugins: [{ id: "file-parser", pdf: { engine: "mistral-ocr" } }],
  }),
});
console.log(await response.json());

Example with base64 PDF

cURL

curl -X POST "https://api.lunos.tech/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemma-3-27b-it",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "Summarize this document" },
          {
            "type": "file",
            "file": {
              "filename": "document.pdf",
              "file_data": "data:application/pdf;base64,PASTE_BASE64_HERE"
            }
          }
        ]
      }
    ]
  }'
import base64
import requests

with open("document.pdf", "rb") as f:
    b64 = base64.b64encode(f.read()).decode("utf-8")

payload = {
    "model": "google/gemma-3-27b-it",
    "messages": [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Summarize this document"},
                {
                    "type": "file",
                    "file": {
                        "filename": "document.pdf",
                        "file_data": f"data:application/pdf;base64,{b64}",
                    },
                },
            ],
        }
    ],
}
response = requests.post(
    "https://api.lunos.tech/v1/chat/completions",
    headers={
        "Authorization": "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
    },
    json=payload,
)
print(response.json())

TypeScript

import fs from "node:fs/promises";

const pdf = await fs.readFile("document.pdf");
const b64 = pdf.toString("base64");

const response = await fetch("https://api.lunos.tech/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_SECRET_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "google/gemma-3-27b-it",
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "Summarize this document" },
          {
            type: "file",
            file: {
              filename: "document.pdf",
              file_data: `data:application/pdf;base64,${b64}`,
            },
          },
        ],
      },
    ],
  }),
});
console.log(await response.json());

Reuse annotations to skip re-parsing

Some responses include parsed file annotations in choices[].message.annotations. Reusing these annotations in later turns can reduce parsing time and cost.

Annotation schema (simplified)

{
  "type": "file",
  "file": {
    "hash": "abc123...",
    "name": "document.pdf",
    "content": [
      { "type": "text", "text": "Parsed text..." },
      { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
    ]
  }
}

Response format example

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "The document discusses...",
        "annotations": [
          {
            "type": "file",
            "file": {
              "hash": "abc123...",
              "name": "document.pdf",
              "content": [{ "type": "text", "text": "Parsed text..." }]
            }
          }
        ]
      }
    }
  ],
  "usage": {
    "prompt_tokens": 1000,
    "completion_tokens": 100,
    "total_tokens": 1100
  }
}

Troubleshooting

No useful extraction

  • Try OCR-oriented engine for scanned docs
  • Improve file quality
  • Split very large PDF into smaller chunks

Payload too large

  • Prefer URL instead of base64
  • Send only relevant pages when possible

Lunos checklist

  • Use POST /v1/chat/completions with type: "file"
  • Use URL for public/large PDFs; base64 for private/local PDFs
  • Set plugins only when you need specific parser behavior
  • Reuse annotations for multi-turn conversations on the same document