# PDFs and files

> Attach PDF documents to chat requests with the file content part. PDFs go only to endpoints that read files natively.

Source: https://0.0.0.0:3000/docs/multimodal/pdfs
Documentation index: https://0.0.0.0:3000/docs/llms.txt

Add a `file` content part to a `user` message, with the PDF as an HTTPS URL or a base64 `data:` URL. Requests with a PDF go only to endpoints that read files natively; PDFs aren't converted to text or images for other models. The examples use `openai/gpt-6-luna`; replace it with a model that has an endpoint whose `input_modalities` include `file`.

## Send a PDF by URL

```request
{
  "method": "POST",
  "path": "/chat/completions",
  "body": {
    "model": "openai/gpt-6-luna",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "Summarize the key findings of this report in five bullet points." },
          {
            "type": "file",
            "file": {
              "filename": "annual-report.pdf",
              "file_data": "https://example.com/documents/annual-report.pdf"
            }
          }
        ]
      }
    ]
  }
}
```

The URL must be publicly reachable over HTTPS and return a PDF, served as `application/pdf`, `application/octet-stream` or without a `Content-Type`.

## Send a local PDF

Encode the file as a `data:application/pdf;base64,` URL:

:::code-group
```python title="Python"
import base64
import os
from pathlib import Path

from openai import OpenAI

def pdf_data_url(path: str) -> str:
    return "data:application/pdf;base64," + base64.b64encode(Path(path).read_bytes()).decode("ascii")

client = OpenAI(base_url="https://api.numes.ai/api/v1", api_key=os.environ["NUMES_API_KEY"])

completion = client.chat.completions.create(
    model="openai/gpt-6-luna",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Which payment terms does this contract set?"},
                {"type": "file", "file": {"filename": "contract.pdf", "file_data": pdf_data_url("contract.pdf")}},
            ],
        }
    ],
)
print(completion.choices[0].message.content)
```

```typescript title="TypeScript"
import { readFile } from "node:fs/promises";
import OpenAI from "openai";

async function pdfDataUrl(path: string): Promise<string> {
  return `data:application/pdf;base64,${(await readFile(path)).toString("base64")}`;
}

const client = new OpenAI({ baseURL: "https://api.numes.ai/api/v1", apiKey: process.env.NUMES_API_KEY });

const completion = await client.chat.completions.create({
  model: "openai/gpt-6-luna",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "Which payment terms does this contract set?" },
        { type: "file", file: { filename: "contract.pdf", file_data: await pdfDataUrl("contract.pdf") } },
      ],
    },
  ],
});
console.log(completion.choices[0]?.message.content);
```
:::

Inline PDFs count toward the 10 MiB chat body limit, and base64 adds about a third to their size. Send PDFs larger than about 7.5 MiB as HTTPS URLs.

## The file part

:::fields Content part
- `type` `"file"` required — Identifies a file part.
- `file` `object` required — The document to attach.
  - `file_data` `string` required — An `https://` URL that serves a PDF, or a `data:application/pdf;base64,` URL.
  - `filename` `string` — Optional name, at most 255 bytes, without control characters. It isn't forwarded to the provider; put instructions in a text part.
  - `file_id` `string` — Not supported; there's no file storage. Any `file_id` is rejected with 400.
:::

You can attach several PDFs to one message; they reach the model in the order you list them. Only PDFs are accepted. For Word documents, spreadsheets or plain text, extract the text yourself and send it in a `text` part.

## Which endpoints accept PDFs

Routing considers only endpoints whose `input_modalities` include `file`, and never drops the PDF to answer from the text alone. Check a model's endpoints first:

```request
{
  "method": "GET",
  "path": "/models/author/document-model/endpoints"
}
```

:::note No PDF parsing for text-only models
Some routers extract text from PDFs for models that can't read them, using a `plugins` array. Numes doesn't: `plugins` is rejected with 400. To use a text-only model, extract the text yourself.
:::

## Limits

| Limit | Value |
| --- | --- |
| Format | PDF only |
| One PDF | 8 MiB decoded |
| All media in one request | 24 MiB decoded, shared with images and audio |
| Chat request body | 10 MiB, including inline `data:` URLs |

How many pages the model can use depends on the model and its context length. See [Sending media](/docs/multimodal/overview#sending-media) for the rules every media URL follows.

## Billing

A request with a PDF is charged like any other chat completion. `usage.cost` is the exact charge; see [Usage accounting](/docs/account/usage-accounting).

## Troubleshooting

File problems return 400 `invalid_request` unless the table says otherwise. The message names the failing part, such as `messages[0].content[1].file.file_data`.

| Symptom | Cause and fix |
| --- | --- |
| 400 with `file_id` | Send `file_data` instead. |
| 400 with `plugins` | Remove `plugins` and use a model with file input. |
| 400 with a `data:` URL | The media type isn't `application/pdf`, the bytes aren't a PDF, or the data isn't standard base64. |
| 400 with an HTTPS URL | The server returned an error status, an HTML page, a compressed response or a mismatched `Content-Type`, or the URL isn't public HTTPS. |
| 400 for a large file | The PDF exceeds 8 MiB, or all media together exceed 24 MiB. Split or compress it. |
| 413 `payload_too_large` | An inline PDF pushed the body over 10 MiB. Send an HTTPS URL. |
| 503 `provider_unavailable` | No endpoint for the model accepts file input under your routing preferences. |

## Next steps

:::cards
- {image} [Image inputs](/docs/multimodal/images) Send images to vision models.
- {boxes} [Multimodal](/docs/multimodal/overview) Formats, media rules and limits for every modality.
- {layers} [Embeddings](/docs/multimodal/embeddings) Embed text, images and PDFs for retrieval.
- {network} [Provider routing](/docs/routing/provider-routing) Restrict or order the endpoints that serve you.
:::
