Docs/API

Files

How do I upload a file once and send it to any model?

Checked against the code on

Endpoints
POST   https://heyaskr.ai/v1/files
GET    https://heyaskr.ai/v1/files
GET    https://heyaskr.ai/v1/files/{id}
GET    https://heyaskr.ai/v1/files/{id}/content
DELETE https://heyaskr.ai/v1/files/{id}

Upload a file once, then name it by file_id in any message. The shape is OpenAI's files API, so client.files.create in the SDKs works as it is. Uploading is free and keeping a file costs nothing; a file costs input tokens on each call that carries it, at the model's rate. The same store sits behind the paperclip in the workspace, with the same limits and the same rules.

Upload, then ask

curl https://heyaskr.ai/v1/files \
  -H "Authorization: Bearer $ASKR_KEY" \
  -F "file=@report.pdf" \
  -F "purpose=user_data"

curl https://heyaskr.ai/v1/chat/completions \
  -H "Authorization: Bearer $ASKR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": [
      {"type": "text", "text": "Summarise this in five bullets."},
      {"type": "file", "file": {"file_id": "3f2a9c1e-7b4d-4e0a-9c1f-2d8e6a5b4c3d"}}
    ]}]
  }'

The part to use for an upload follows its kind: file for a PDF, a document or a text file, image_url for a picture, input_audio for sound, video_url for a clip, each with file_id in place of the inline data. Pictures and files in a message has every shape.

The five routes

MethodPathWhat it does
POST/v1/filesMultipart with a file field. purpose is accepted and ignored: every upload here is for a model to read. An optional name field sets the filename kept. Answers with the file object. 60 uploads a minute.
GET/v1/filesYour newest 100 files, as { "object": "list", "data": [...] }.
GET/v1/files/{id}One file object.
GET/v1/files/{id}/contentThe bytes, with the stored content type and the filename.
DELETE/v1/files/{id}{ "id": ..., "object": "file", "deleted": true }. A call that names the id afterwards gets 404.

The file object

200
{
  "id": "3f2a9c1e-7b4d-4e0a-9c1f-2d8e6a5b4c3d",
  "object": "file",
  "bytes": 482113,
  "created_at": 1789516800,
  "filename": "report.pdf",
  "purpose": "user_data",
  "askr": { "kind": "pdf", "mime": "application/pdf", "meta": { "pages": 12, "chars": 30412 } }
}
FieldMeaning
askr.kindWhat the bytes were found to be: image, pdf, text, document, audio or video. The name and the declared type are not trusted.
askr.mimeThe stored type, from the bytes.
askr.metaWhat was measured: width and height for a picture; pages for a PDF; seconds for audio and video; sheets or slides for a document; chars for anything read as words, with truncated: true when it was cut at 400,000 characters.

What is accepted

KindFormatsPer filePer messagePer request
PicturePNG, JPEG, WebP, GIF8 MB, 8,192 pixels a side620
PDFPDF32 MB36
Text and codePlain text, Markdown, CSV, JSON, HTML, YAML and source code in most languages5 MB510
DocumentWord (.docx), PowerPoint (.pptx), Excel (.xlsx)32 MB36
AudioMP3, WAV, M4A, OGG, FLAC, WebM25 MB, 20 minutes24
VideoMP4, MOV, WebM100 MB, 10 minutes12

Per request counts every file_id on every message in messages. A kind not in the table is refused with 415, and an empty or damaged file with 400, at upload. Pictures are not resized on the API: send them at the size you want the model to see, under 8 MB.

How a file reaches the model

Every model reads words. Only some read files. A file by file_id goes to the model as the file where the model's inputs on GET /v1/models include its kind, and as its words where they do not:

  • Pictures go to a model with image in its inputs and are refused on any other (W203). 212 chat models see pictures, on the catalogue of 24 September 2026.
  • PDFs of up to 100 pages go as the file to a model with file in its inputs, 113 of them: inline under 8 MB, by a link the provider fetches above it. On every other model, and over 100 pages on every model, a PDF goes as its text. A scanned PDF with no text layer works only on a model that reads files (W611 otherwise).
  • Text, code and documents always go as words, between lines that name the file. Word is read as its paragraphs, PowerPoint one block per slide, Excel one sheet at a time as CSV. Up to 400,000 characters a file, about 100,000 tokens; a longer file is cut there and the model is told.
  • Audio goes as sound to a model with audio in its inputs, 32 of them, when it is an MP3 or a WAV under 8 MB. Anything else, and any audio for a model that cannot hear, goes as a transcript, made once by a speech-to-text model and kept beside the file. If none can be made, W608.
  • Video goes as the clip to a model with video in its inputs, 66 of them: inline under 8 MB, by a link above it. There is no words version of a video; on any other model it is refused (W609).

What it costs

A file on a call is input tokens at the model's input rate. The hold reserves the top of what providers bill per unit and settles to the gateway's real figure, in askr.credits_charged as always. Holds and settlement.

Sent asReserved
A picture1,600 tokens
A PDF, as the file1,600 tokens a page
Audio, as sound32 tokens a second
Video100 tokens a second
Words: a text file, a document, a PDF's text, a transcriptThe text's own token count

Only the three newest user messages that name uploads carry them in full. On an older message the file is replaced by a one-line note saying it was attached there, so a client that resends the whole conversation, as OpenAI-shaped clients do, does not pay for a PDF on every call. Files count toward the model's window with everything else; a request that does not fit returns 413 with code context_length_exceeded before anything runs.

How long a file is kept

Used in a call
Kept on your account until you delete it. The workspace's Files page shows the same list.
Never used
Swept 7 days after upload.
The words read from it
Extracted text and any transcript are kept beside the file, so they are made once.
Where it goes
Where your words go: to the model's provider, through askr's gateway, for that call. A file over 8 MB sent as the file is fetched by the provider from a link that expires after an hour.

Errors

None of these charge anything. Refusals that share a message with the workspace carry its code in code, so Error codes reads for both.

StatusWhereMessageWhy
400UploadSend multipart/form-data with a file field.The body was not multipart.
415UploadThat file type is not accepted. Pictures (PNG, JPEG, WebP, GIF), PDFs, text and code files, Word, PowerPoint and Excel documents, audio (MP3, WAV, M4A, OGG) and video (MP4, MOV, WebM) are.code is W604. Judged from the bytes.
413UploadThat file is too large. The limit is 32 MB for a PDF.code is W605. The message names the limit for that kind.
400UploadThat file could not be read. It may be empty or damaged.code is W610.
404Files, chatNo file with id 'x'.Deleted, swept, or on another account.
413ChatAt most 6 pdf files per request.More than the per-request count for that kind.
400ChatThe model 'x' does not accept file input. Upload the file with POST /v1/files and send its file_id instead; askr then sends the model its text where it can.An inline part on a model that takes no such input. Also image, audio and video.
400ChatThat model cannot watch video. Pick one that does, or remove the video.A per-model refusal of an upload: W609 here; also W203, W605, W606, W607, W608, W611.
413ChatThis request, with its files, is about 180,000 tokens; the model 'x' takes 128,000.code is context_length_exceeded. Pick a bigger window, or send less.