File inputs
Sending a file to an operation — inline base64, or a URL the operation fetches.
Some operations take a file: an image to run OCR on, a PDF to extract text from, an audio file to transcribe. There are two ways an operation can ask for one, and which one it uses is declared in its input schema. You can tell them apart at a glance, and the difference decides how large a file you can send.
1. Inline base64
The operation declares a string parameter carrying contentEncoding: "base64":
{
"type": "object",
"properties": {
"image": {
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/png",
"description": "The image to read text from"
}
},
"required": ["image"]
}Send the file's bytes base64-encoded, with no data: URL prefix:
curl -X POST https://api.upapi.io/image-ocr \
-H 'x-api-key: upapi_YOUR_KEY' \
-H 'content-type: application/json' \
-d "{\"image\": \"$(base64 -w0 receipt.png)\"}"const bytes = await readFile('receipt.png');
await fetch('https://api.upapi.io/image-ocr', {
method: 'POST',
headers: { 'x-api-key': key, 'content-type': 'application/json' },
body: JSON.stringify({ image: bytes.toString('base64') }),
});contentMediaType, when present, tells you which formats the operation accepts.
Size limit — do the math before you send
The request body is capped at 5 MB, and base64 inflates a file by 4 bytes for every 3 (about 33%). A little headroom is reserved for the rest of the JSON body — the other parameters, the key names, the quoting — which leaves roughly:
| Request body cap | 5 MB |
| JSON envelope reserve | 64 KB |
| Largest real file | ≈ 3.7 MB |
Sending more returns 413 PAYLOAD_TOO_LARGE at the edge, before the operation
runs. If your file is bigger, use an operation that takes a URL instead — or
host the file yourself and pass its URL.
The Try it panel does this for you
The "Try it" panel on an operation's marketplace page renders a file picker for these parameters, checks the size before it encodes anything, and does the base64 encoding for you.
2. A URL the operation fetches
Larger inputs — audio, video, long PDFs — are passed by reference. The
operation declares a uri parameter and fetches the file itself, so nothing
large crosses the API gateway and the 5 MB cap does not apply:
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"x-upapi-upload": true,
"description": "Publicly reachable URL of the audio to transcribe (e.g. https://example.com/talk.mp3)"
}
},
"required": ["url"]
}curl -X POST https://api.upapi.io/audio-transcribe.post \
-H 'x-api-key: upapi_YOUR_KEY' \
-H 'content-type: application/json' \
-d '{"url": "https://example.com/talk.mp3"}'The URL must be reachable from the public internet — a localhost address, a
private-network address or a link behind a login will fail.
x-upapi-upload
"x-upapi-upload": true marks a URL parameter as one whose value may be a file
of yours, as opposed to a URL that is inherently someone else's (the page a
scraper reads, for example). It changes nothing about how the API validates your
request — x- keywords are ignored by JSON Schema validators — and you can
always just pass a URL you already have.
What it does change is the "Try it" panel: for a marked parameter, when hosted uploads are enabled on the deployment you are using, the panel offers an Upload a file button that stores your file and fills the field with its public URL. When they are not enabled, the field is an ordinary URL box and nothing else differs.
Reading a file back out
An operation that returns a file uses the same base64 convention on its output schema. The response is bounded the same way, so operations that produce large artifacts return a URL instead.
Summary
| Inline base64 | URL reference | |
|---|---|---|
| Schema marker | contentEncoding: "base64" | format: "uri" (+ x-upapi-upload) |
| Max size | ≈ 3.7 MB | Bounded by the operation, not the gateway |
| You send | The encoded bytes | A public URL |
| Good for | Images, small documents | Audio, video, large documents |