Construction takeoff API: send drawings, get structured quantities
What a construction takeoff API does, the upload-poll-fetch integration flow with working code, what the GeoJSON output looks like, and how to get an API key.
A construction takeoff API accepts construction drawings and returns structured quantity data - rooms with areas, walls with lengths, doors, windows, and fixtures as countable objects - as JSON your software can consume. Kamai's takeoff API does exactly this: upload a PDF sheet set, poll while foundational models trained on construction drawings process it, then fetch a GeoJSON FeatureCollection of everything they found.
What a takeoff API is for
A takeoff API is takeoff as infrastructure instead of takeoff as an app. Two kinds of teams reach for it. Product teams building software for the construction industry use it to add takeoff to their own product: their users upload drawings inside their UI, and the quantities come back from Kamai. And teams inside contractors and estimating firms use it to automate their own pipeline: drawings in from one end, structured quantities into the estimating database out the other, with no one tracing in between.
In both cases the value is the same: the model work - reading drawings, classifying geometry, detecting scale - is the hard part, and it arrives as an HTTP endpoint.
The integration flow
The flow is three calls: upload, poll, fetch.
- Create an API key in the Kamai Console. The secret is shown once at creation; store it securely.
- Upload a sheet set with
POST /v1/blueprints/upload. You get back ajob_idand aproject_idimmediately; processing is asynchronous. - Poll the project with
GET /v1/projects/{project_id}every few seconds until your job's status leavesPENDING/RUNNING. - Fetch the result with
GET /v1/blueprints/{blueprint_id}once the job isSUCCEEDED.
Complete and runnable, in Python:
import time
import httpx
API_KEY = "..." # created in the Kamai Console
client = httpx.Client(
base_url="https://api.kamai.io/v1",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=120,
)
# 1. Upload a sheet set
with open("floor-plan.pdf", "rb") as f:
up = client.post("/blueprints/upload", files={"file": f}).json()
# 2. Poll until the job finishes
while True:
project = client.get(f"/projects/{up['project_id']}").json()["project"]
job = project["jobs"][up["job_id"]]
if job["status"] not in ("PENDING", "RUNNING"):
break
time.sleep(5)
# 3. Fetch the structured result
assert job["status"] == "SUCCEEDED", job["status"]
blueprint = client.get(f"/blueprints/{job['blueprint_id']}").json()["blueprint"]
print(len(blueprint["geojson"]["features"]), "features detected")The API is under active development; endpoint shapes shown here reflect the current surface. For access details and the latest reference, start from the developer hub.
What the output looks like
The blueprint payload carries a standard GeoJSON FeatureCollection. Every feature is one detected element - a room, a wall segment, a fixture - with its geometry and a typed properties object:
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[120.5, 340.0], [480.0, 340.0], [480.0, 610.5], [120.5, 610.5], [120.5, 340.0]]]
},
"properties": {
"primaryType": "Areas",
"secondaryType": "Gross area",
"displayName": "Conference Room",
"area": 95200.5,
"perimeter": 1240.0
}
}Features classify into three families: areas (plan footprints, gross and net areas), lines (wall centerlines, wall perimeters, door openings), and objects (doors, windows, sinks, toilets, bathtubs, showers, and other fixtures). Coordinates live in blueprint space - the 2D plane of the processed sheet - and the payload includes the detected scale, so converting to real-world units is one ratio for lengths and its square for areas.
Because every record keys back to its sheet and its geometry, your application can do what an estimator does: show exactly where a number came from.
What teams build with it
Takeoff inside your product. Construction software that handles drawings eventually gets asked for quantities. The API lets you answer with a feature instead of a partnership referral. If you want the full review UI without building it, the embeddable takeoff widget (via @kamai/iframe-bridge) drops into your product, backed by the same models.
Internal estimating automation. Watch an inbox or a folder, push new sheet sets to the API, and land classified quantities in your estimating database before anyone opens the drawings.
Agent workflows. The API is the foundation; an MCP server is coming so Claude, Cursor, and other agent hosts can run takeoffs as tools.
Getting access
API keys are self-serve in the Kamai Console. Create a key, run the Python example on one of your own sheet sets, and look at what comes back - the GeoJSON is the spec sheet.