HyperbotsHyperAPI/Docs
POST
/v1/documents/upload

Upload Document

Request a presigned S3 upload URL and receive a document_key. All inference APIs — Parse, Extract, Classify, and Split — accept this key instead of a raw file, so a single upload can be reused across multiple operations within 24 hours.

1

Get a presigned upload URL

Call POST /v1/documents/upload (through the API gateway) — returns a document_key and a presigned upload_url.

2

Upload the file bytes to S3

PUT directly to {upload_url}. No API key needed — the URL is pre-authenticated and valid for 15 minutes.

3

Call any inference API

Pass the document_key to any endpoint. The key is reusable for 24 hours.

Request

Headers

NameTypeRequiredDescription
X-API-Keystring
required
Your API key (hk_live_ or hk_test_).
Content-Typestring
required
Must be application/json.

Body application/json

NameTypeRequiredDescription
filenamestring
required
Original filename including extension (e.g. "invoice.pdf"). Used for MIME auto-detection if content_type is omitted.
content_typestring
optional
MIME type of the file. Supported: application/pdf; images (image/png, image/jpeg, image/webp, image/tiff, image/heic); and Office/text formats (Word, Excel, PowerPoint, CSV, TXT, RTF, JSON, XML). Defaults to application/pdf.
content_lengthinteger
optional
File size in bytes. Enables server-side size validation before the presigned URL is issued — the request is rejected with 413 above the applicable ceiling (50 MB by default; see Size limits below).

Size limits

An upload is capped at 50 MB, and /v1/parse in mode=advanced at 60 pages. Both ceilings are raised for one specific case:

CallerMax uploadMax pages (advanced parse)
Dashboard or browser upload50 MB60
API or SDK — any tier, synchronous50 MB60
API or SDK — custom tier, X-Async: true512 MiB500

The raised limits apply only when all three conditions hold: your organization is on the custom tier, the request comes directly from the API or an official SDK (uploads made through the dashboard or the browser do not qualify, whatever your tier), and the inference request carries X-Async: true. Miss any one of them and an over-cap request returns the same 413 it always has. The page cap applies to mode=advanced only — mode=fast is not page-capped.

Page limits by operation

The 50 MB upload ceiling applies to every operation; page ceilings differ per operation and are checked when you submit the document_key — before any billable processing runs.

OperationMax pagesNotes
Parse (fast)Never page-capped.
Parse (advanced)60Raised to 500 for the bulk case above.
Extract (fast)Never page-capped.
Extract (advanced)60Same cap as advanced parse.
ClassifyReads the first pages it needs; long documents are fine.
Split
Redact / Deidentify36Rejected up front with 413 before any processing is billed.

Rather than hardcoding these numbers, read them at runtime: GET /v1/limits returns your organization's effective per-operation ceilings, and the same limits object rides on every upload response. Asynchronous submissions return 200 when the job is accepted — the final verdict, including a limit rejection, lands on the job record you poll.

A document over the standard cap is processed in segments and can take tens of minutes, so it is only ever returned as a job you poll. See Large documents for the polling contract and how partial failures are reported.

Code examples

# Step 1: Request a presigned S3 upload URL
RESP=$(curl -sX POST "https://apis.hyperbots.com/v1/documents/upload" \
  -H "X-API-Key: hk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"filename":"document.pdf","content_type":"application/pdf"}')

KEY=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['document_key'])")
URL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['upload_url'])")

# Step 2: PUT the file directly to S3 (no API key needed — URL is pre-authenticated)
curl -X PUT "$URL" \
  -H "Content-Type: application/pdf" \
  -H "x-amz-server-side-encryption: AES256" \
  --data-binary @document.pdf

# document_key is now ready — pass it to /v1/parse, /v1/extract, etc.
echo "$KEY"  # 550e8400-e29b-41d4-a716-446655440000

Response

200 OK
application/json
FieldTypeDescription
document_key
key
stringUUID identifying this upload. Pass to any inference endpoint (Parse, Extract, Classify, Split) as a form field.
upload_urlstringPre-authenticated S3 PUT URL. Upload the file bytes directly here — no API key required. Valid for expires_in seconds.
expires_inintegerPresigned URL TTL in seconds (default 900 = 15 minutes). The file itself is retained in storage for 24 hours.
JSON
{
  "document_key": "550e8400-e29b-41d4-a716-446655440000",
  "upload_url": "https://uploads.example-cdn.com/uploads/org_abc/550e8400-...?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&...",
  "expires_in": 900
}