ck_sandbox_...) for this walkthrough; it returns instant
example results and is never billed. The same requests work unchanged with
your live key.
1. Register the document
Tell us the filename, type, and exact size in bytes. The response includes a short-lived upload URL.id (the document id) and upload.url from the response.
Why the exact byte size? It’s built into the upload URL’s signature, so
storage rejects any upload that doesn’t match what was registered - and
files over the size limit are rejected here, before you spend time
uploading. Your HTTP client already knows the size (it sends it as
Content-Length). The filename’s extension tells us how to process the
file (.xlsx/.xls are handled natively as spreadsheets). content_type
is optional; when provided it’s also pinned into the upload signature.2. Upload the file
PUT the raw bytes to the upload URL. The upload must match the registered
byte size exactly.
3. Start the extraction
This validates the document and charges its fixed rate before processing - pricing is always known up front. Rejected documents are never charged.asset_class is required. It decides which standard columns a rent roll is
consolidated onto - market_rent, bedrooms and bathrooms exist for
multifamily; unit size and climate control for self-storage. Send the
property’s real class: one of multifamily, self-storage,
industrial-outdoor-storage, commercial, industrial, office, retail
or hotel.
The response is 202 Accepted with an extraction id and the billed amount.
4. Wait for completion
Poll the extraction untilstatus is completed — instant in sandbox, and
for live documents usually 2-5 minutes for a T-12 or an offering memorandum.
Rent rolls run a second consolidation phase and take longer: most finish
within 10 minutes, a large one can run to 30. Add wait to have the API hold
the request until the job finishes instead of sleeping between calls:
wait accepts 0-45 seconds. If the job is still running when the wait
elapses you get a normal 200 with status: processing — that’s not a
timeout, just call again. While a job is in flight the response carries
poll_after_seconds and a matching Retry-After header; use those rather
than a fixed interval. Nothing runs longer than 60 minutes: past that the
extraction fails and every charge is refunded in full.
Prefer push? Register a webhook endpoint and receive
extraction.completed instead of polling.
5. Fetch the result
schema_version and returns extracted values with
confidence scores and source locations (sheet cell or page), so you always
know where a number came from.