Theme
Language

Files API and PDFs

The OpenAI-compatible GetVibe API Files API and PDF input: base64, a link or a file_id on chat/completions, responses and messages, which models see the pictures inside a PDF, limits and errors.

OpenAI-compatible Files API for uploading, reading and deleting files. Every operation requires an API key; a file is accessible only to the same client account that uploaded it.

Upload a file

POSThttps://getvibeapi.com/v1/files

Send multipart/form-data with the required file and purpose fields. Allowed purposes: assistants, batch, fine-tune, vision and user_data. The maximum file size is 50 MiB (52 428 800 bytes); exceeding it returns 413.

curl -X POST https://getvibeapi.com/v1/files \
  -H "Authorization: Bearer $GETVIBE_API_KEY" \
  -F "purpose=user_data" \
  -F "file=@./report.pdf"

Get metadata, content or delete

GEThttps://getvibeapi.com/v1/files/{file_id}
GEThttps://getvibeapi.com/v1/files/{file_id}/content
DELETEhttps://getvibeapi.com/v1/files/{file_id}

Upload returns a file object with an opaque id, name, size, purpose and creation time. GET without /content returns this metadata; GET /content downloads the raw bytes. After a successful DELETE both GETs return 404. Another account's existing file returns 403, and an unknown identifier 404.

Safe workflow

  1. Upload the file and keep only the returned file_id.
  2. Verify the metadata with the same key; when working with Responses pass this id in input_file.file_id.
  3. Do not upload secrets and delete the file after you finish. A deleted file cannot be restored.

PDF in model requests

Every text model accepts a PDF on all three API formats. In short: send the file in any of the three ways below — by default we extract its text for free and pass that to the model. The model reads the text and tables, but not the pictures inside the PDF or scans. When pictures, layout or a scan matter, pick a model from the table below and turn text extraction off. Everything on this page was verified with live requests on November 2, 2026, using a three-page PDF with text, a table and a picture.

Three ways to send a PDF

MethodHowWhen to use it
base64 in the requestChat: {"type":"file","file":{"filename":"report.pdf","file_data":"data:application/pdf;base64,…"}}; Responses: {"type":"input_file","filename":"report.pdf","file_data":"data:…"}; Messages: {"type":"document","source":{"type":"base64","media_type":"application/pdf","data":"…"}}A one-off document. Works on all three endpoints. Files up to 20 MiB.
Public linkChat: "file":{"file_url":"https://…"}; Responses: {"type":"input_file","file_url":"https://…"}; Messages: "source":{"type":"url","url":"https://…"}The file is already online. We download it ourselves: HTTPS only, no login or password, the server must answer with application/pdf, up to 20 MiB, within 10 seconds and at most 3 redirects.
file_id after uploadUpload with POST /v1/files and purpose=user_data, then: Chat "file":{"file_id":"file-pv-…"}, Responses {"type":"input_file","file_id":"file-pv-…"}, Messages "source":{"type":"file","file_id":"file-pv-…"}One document across several requests: no need to resend megabytes each time, and an identical request prefix is cached. The file is kept for 7 days and is visible only to your account.

Ready-to-run examples

The key is read from the GETVIBE_API_KEY environment variable. The Python examples use the official openai and anthropic SDKs unchanged — only the address differs.

/v1/chat/completions

# PDF inline (base64); jq builds the body so a large file does not hit the command-line length limit
# macOS: replace base64 -w0 report.pdf with base64 -i report.pdf
jq -n --rawfile b64 <(base64 -w0 report.pdf) '{model: "deepseek-v4.1-flash", messages: [{role: "user", content: [
    {type: "text", text: "Summarise the document"},
    {type: "file", file: {filename: "report.pdf", file_data: ("data:application/pdf;base64," + $b64)}}]}]}' |
curl https://getvibeapi.com/v1/chat/completions \
  -H "Authorization: Bearer $GETVIBE_API_KEY" -H "Content-Type: application/json" -d @-

/v1/responses

# FILE_ID comes from the upload, see the "curl · file_id" tab above
curl https://getvibeapi.com/v1/responses \
  -H "Authorization: Bearer $GETVIBE_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"gpt-6.1-sol","input":[{"role":"user","content":[
    {"type":"input_text","text":"What were the sales in Kazan in August?"},
    {"type":"input_file","file_id":"'"$FILE_ID"'"}]}]}'
# base64 instead of file_id:
#   {"type":"input_file","filename":"report.pdf","file_data":"data:application/pdf;base64,…"}

/v1/messages

curl https://getvibeapi.com/v1/messages \
  -H "x-api-key: $GETVIBE_API_KEY" -H "anthropic-version: 2023-06-01" -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-5.5","max_tokens":1024,"messages":[{"role":"user","content":[
    {"type":"document","source":{"type":"file","file_id":"'"$FILE_ID"'"}},
    {"type":"text","text":"Summarise the document"}]}]}'
# other sources:
#   "source":{"type":"base64","media_type":"application/pdf","data":"<base64>"}
#   "source":{"type":"url","url":"https://example.com/report.pdf"}

What the model sees: text or the file itself

By default, extracted text. We take the PDF's text layer and pass it to the model instead of the file. This is free and works with any model, even one without PDF support. The model sees words and numbers, including tables (without borders, but with the right values). What it does not see: pictures, charts, stamps, signatures, colours and layout. Up to 60,000 characters of text per request are passed on (about 28 dense pages); the rest is cut and the model is told the document was truncated. The x-pv-document-shim response header says what happened: applied:text_extracted means the model got the text, not-needed:lane_reads_pdf means it got the file itself.

The file itself. To have the model see whole pages, with pictures and layout, add "plugins":[{"id":"documents","enabled":false}] to the request body (the header then reads not-needed:disabled_by_client). Only do this with models marked "yes" in the table: the others answer 400 or say they see no file, and the file's input tokens are still billed.

# curl: add the field to the request body
"plugins": [{"id": "documents", "enabled": false}]

# Python (openai SDK)
r = client.chat.completions.create(
    model="gpt-6.1-sol",
    messages=[...],
    extra_body={"plugins": [{"id": "documents", "enabled": False}]},
)
ModelBy defaultSees the file itself (extraction off)
gpt-6.1-solthe file itselfyes — on /chat/completions and /responses
gpt-6-lunatextyes — on /chat/completions and /responses
gpt-5.6-soltextnot confirmed — keep the default
claude-sonnet-5.5, claude-opus-5.5textno — the model cannot see the file, so even with the plugin off we extract the text (header x-getvibe-warnings: plugin_overridden:documents); for the file itself use gpt-6.1-sol
claude-haiku-5.5textno — the model does not see the file
gemini-4-argon, gemini-3.8-flashtextno — the model does not see the file, or 400
qwen3.8-flashtextyes — on /chat/completions; 400 on /responses
deepseek-v4.1-flash, glm-5.3, kimi-k3textno — 400

How we tested: the model was asked for a word from page 1, a number from a table cell, and to describe a picture (shapes, colours and a number drawn as an image). "Text" means it named the word and the number correctly and said it could not see the picture. "Yes" means it described the picture correctly too. Send PDFs to GPT, Gemini and Qwen models through /v1/chat/completions or /v1/responses: on /v1/messages they do not see the file itself.

Scans. A scan has no text layer, so there is nothing to extract and the model gets the original file (header not-needed:no_text_layer_unmeasured_lane). It then depends on the model: gpt-6.1-sol and qwen3.8-flash read our scan, DeepSeek models answer 400. If your model cannot read a scan, run OCR on your side and send the text, or send the pages as PNG/JPEG images via image_url to a model that accepts images.

Limits, tokens and time

WhatLimit
A PDF in a model request (base64, link or file_id)up to 20 MiB per file; the file must start with %PDF-
Upload to POST /v1/filesup to 50 MiB (52,428,800 bytes), but only a PDF up to 20 MiB can go into a model request
file_id lifetime7 days (the expires_at field), then the file is deleted
Pagesno separate limit; with text extraction, up to 60,000 characters per request; with the file itself, up to the model’s context window
Download by linkup to 10 seconds and up to 3 redirects

How tokens are counted. A PDF is billed as ordinary input tokens of the chosen model at its price; there is no separate fee for the file or for text extraction. What it comes to (measured, including a short question):

  • Extracted text: roughly 700 tokens per dense page of text (10 pages — 7,300 tokens; 40 pages cut to 60,000 characters — 20,300 tokens). A page with a short table — 100–200 tokens.
  • The file itself on GPT models: about 600–700 tokens per page (10 pages — 5,900; 40 — 23,300); on gemini-4-argon about 1,200 per page; on Claude about 1,600 per page. A scanned page on gpt-6.1-sol — about 5,800 tokens.
  • Repeating the same document hits the cache when the start of the request does not change: a second request with the same file_id read 7,168 of 7,309 tokens from the cache.

Time. Text extraction takes a fraction of a second; an answer about a 40-page document came back in 4–12 seconds. For large documents and long answers use stream: true and set the client timeout to at least 300 seconds.

Errors and how to fix them

ResponseCauseWhat to do
400 invalid_pdfThe PDF must start with the %PDF- signature.This is not a PDF, or the base64 is broken. Make sure you encode the file itself, not its path, and that the type is application/pdf.
400 invalid_pdfThe PDF size must not exceed 20 MiB.Split the document or compress the images inside it.
413 invalid_request_errorOn POST /v1/files: the file is larger than 52,428,800 bytesThe file is over 50 MiB: split it.
400 invalid_request_errorThe file_id is unavailableThe file was deleted, expired (7 days), was uploaded by another account, or the id comes from a different service. Upload the file again with the same key.
400 invalid_pdfThe PDF URL must be a public HTTPS URL without credentials.The link is http://, has a login and password in it, uses a non-standard port or a domain that does not exist. Give a public HTTPS link or send base64.
400 invalid_pdfCould not safely fetch the PDF from the URL.The link returns 404, a login page, HTML instead of a PDF, or the server did not answer within 10 seconds. Open the link in a private browser window; if the file does not download right away, send base64.
400 unsupported_inputThe model cannot read PDFs and the file has no text (a scan).Use a model that reads scans (see above), or run OCR on your side.
400 invalid_request_errorInvalid request… with text extraction turned off, or with a scan.The model does not accept a PDF file. Remove plugins documents enabled:false so the model gets the text, or pick a model marked "yes" in the table.
200, but "I see no file"Extraction is off, and the model or channel does not read the file.Go back to the default text extraction, or pick a model marked "yes" in the table.

Best practices

  • Need the document in several requests? Upload it once and pass the file_id: the request is smaller, and the repeated prefix is cached and costs less.
  • Put the PDF and standing instructions at the start of the message and the changing question at the end: the cache then hits more often.
  • Split documents longer than ~28 dense pages by chapter: with text extraction, anything past 60,000 characters never reaches the model.
  • For contracts, reports and exports with a text layer, text extraction is enough — the cheapest option on any model, including deepseek-v4.1-flash.
  • Need charts, stamps or scans? Turn extraction off and use a model marked "yes" in the table, for example gpt-6.1-sol.
  • Non-Latin text (Russian, Chinese and others) in a PDF is read without any settings. Remove the password from a PDF before sending it.
  • Delete uploaded files with DELETE /v1/files/{file_id} once you no longer need them.
The Files API does not guarantee that any chosen model route accepts files. If a route does not support a file, fix the request shape or pick a compatible model; do not replace the returned file_id with a third-party identifier.
GetVibe API Files API and PDFs — upload files and send PDFs to models | GetVibe API