Open data API
No key · no quota · CC BY 4.0
Construction calculator data as a free JSON API
Every input, bound, unit and test vector behind the 1,151 construction calculators on this site, with the 73 job takeoffs, the codes they cite, a cross-market glossary and the corrections ledger — as plain JSON files on a CDN. No key, no quota and no rate limit, because there is nothing to meter.
- 1,151Calculators, one file each
- 3,302Published test vectors
- 7,830Links between pages

Quick start
Three requests to a working integration
Discover, list, then fetch the one file you need. Every request is a plain GET for a static file.
Read the index
index.json names every endpoint and carries the current counts, so nothing has to be hard-coded.
/api/v1/index.jsonList the calculators
calculators.json has one row per calculator: its slug, category, title, page and its own file's address.
/api/v1/calculators.jsonFetch one calculator
Each calculator's file holds every input with its dimension, bounds and default, its sources and its test vectors.
/api/v1/calculators/concrete-calculator.json
# 1. What exists, and how much of it
curl -s https://craftquantities.com/api/v1/index.json
# 2. The catalogue: one row per calculator
curl -s https://craftquantities.com/api/v1/calculators.json
# 3. One calculator in full: inputs, bounds, sources, test vectors
curl -s https://craftquantities.com/api/v1/calculators/concrete-calculator.jsonconst base = "https://craftquantities.com/api/v1";
async function getJson(path) {
const response = await fetch(`${base}/${path}`);
// A missing file is the site's HTML 404 page, not JSON — check first.
if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`);
return response.json();
}
const index = await getJson("index.json");
const concrete = await getJson("calculators/concrete-calculator.json");
console.log(index.counts.calculators, concrete.inputs.map((input) => input.id));import json
import urllib.request
BASE = "https://craftquantities.com/api/v1"
def get_json(path):
# Name your client: the CDN refuses urllib's default User-Agent with a 403.
request = urllib.request.Request(f"{BASE}/{path}", headers={"User-Agent": "my-app/1.0"})
with urllib.request.urlopen(request) as response:
return json.load(response)
index = get_json("index.json")
concrete = get_json("calculators/concrete-calculator.json")
print(index["counts"]["calculators"], [i["id"] for i in concrete["inputs"]])13 endpoints
Every endpoint
All GET, all JSON, all under /api/v1/. A path in braces is a pattern; its link opens one real file.
| Endpoint | Returns | Use it for |
|---|---|---|
| IndexGET/api/v1/index.json | Every endpoint's address and the current counts. Fetch it first. | Discovering what exists, and reading counts instead of hard-coding them. |
| Calculator catalogueGET/api/v1/calculators.json | One row per calculator: slug, category, title, description, page URL, its own file's URL, and how many inputs, test vectors and sources it has. | Building a search or an index, or checking a slug exists before linking to it. |
| One calculatorGET/api/v1/calculators/{slug}.json | The catalogue row plus every input (type, dimension, default, bounds, options, which inputs hold money), its sources, its test vectors with worked steps, a citation block, and the takeoffs that run it. | Driving or re-implementing one calculation, and checking it against the published vectors. |
| Job takeoffsGET/api/v1/assemblies.json | Each job takeoff and the calculators it runs, in order, with the reason each one is part of the job. | Seeing which calculations a whole job needs, and in what order. |
| Codes and standards registryGET/api/v1/standards.json | The code families (title, abbreviations, publisher, what each governs, the jurisdiction it is written for, published editions) and the bodies that publish them. | Resolving an abbreviation in a specification to the document and its publisher. |
| One standardGET/api/v1/standards/{body}/{id}.json | What one cited code governs, its editions, and every calculator that cites it — grouped by section where the citation names one. | Finding every calculation that relies on a given code, section by section. |
| Reference tableGET/api/v1/reference/{table}.json | One reference data table with each row's source kept beside it, and the calculators that compute with each row. | Using a cited construction dataset — densities, conductor ampacities, labour rates — without losing its sources. |
| Corrections ledgerGET/api/v1/corrections.json | The confirmed errors the ledger records: what was wrong, what is right, what now prevents it, and the date it was found. Append-only. | Auditing the error record, or citing one correction by its id. |
| Units and conversion factorsGET/api/v1/units.json | Every dimension and unit with its exact factor to the dimension's base unit, and the offset the temperature scales need. | Converting a value into the base unit the inputs and vectors use, exactly as the site does. |
| Cross-market glossaryGET/api/v1/glossary.json | Each construction concept with its name in US, Canadian, UK and Australian usage, and how close each name really is. | Translating a construction term between markets without assuming two words mean the same thing. |
| Validation corpusGET/api/v1/validation.json | Every test vector the test suite runs, with worked steps and a measured account of the corpus's own gaps. | Checking the maths in bulk. For one calculator, its own file is a few kilobytes instead. |
| Link graphGET/api/v1/link-graph.json | Every link between a calculator and a page of another kind — guide, standard, takeoff, glossary term, worked example — grouped by page. | Finding every page that names a calculator, or every calculator a page names. |
| Worked examplesGET/api/v1/examples.json | Each worked job: its measurements, and every step with the calculator it runs, the inputs in base units, the answer and the figure the page prints. | Testing a re-implementation against whole jobs rather than single calculations, in the order a trade does them. |
Anatomy of a file
What every file carries, and what one calculator holds
Trimmed from the real files. Open any link above for the whole thing.
{
"version": "v1",
"source": "https://craftquantities.com",
"licence": "CC BY 4.0",
"licenceUrl": "https://creativecommons.org/licenses/by/4.0/",
"attribution": "Craft Quantities — craftquantities.com",
"documentation": "https://craftquantities.com/api/",
"versioning": "Fields are added, never removed or repurposed, within a version. A breaking change becomes /api/v2/ and v1 keeps being built."
}{
"endpoints": {
"calculators": "https://craftquantities.com/api/v1/calculators.json",
"calculator": "https://craftquantities.com/api/v1/calculators/{slug}.json",
"units": "https://craftquantities.com/api/v1/units.json",
"validation": "https://craftquantities.com/api/v1/validation.json"
},
"counts": {
"calculators": 1151,
"assemblies": 73,
"validationVectors": 3302,
"linkGraphLinks": 7830
}
}{
"slug": "concrete-calculator",
"category": "materials-quantities",
"title": "Concrete Calculator",
"inputs": [
{
"id": "slabLength",
"label": "Slab Length",
"type": "number",
"dimension": "length",
"defaultValue": 3,
"min": 0.01,
"max": 50,
"step": 0.1
}
],
"testCases": [
{
"name": "3x3m slab, 0.1m thick, 10% waste",
"inputs": { "slabLength": 3, "slabWidth": 3, "slabThickness": 0.1, "wasteFactorPercent": 10 },
"expected": 1.29,
"toleranceDecimalPlaces": 1,
"unit": "cubic yards"
}
]
}Values are in base units. A dimensioned input’s default, bounds and step, and every test vector’s inputs, are in its dimension’s base unit — slabLength’s 3 is metres. A money input whose default is an empty string is a rate the caller must supply, not zero. toleranceDecimalPlaces counts decimal places, and is two where absent.
Units
Every dimensioned value is in its base unit
Convert anything else with units.json: base value = (value + offsetToBase) × toBase. Only the temperature scales carry an offset.
length
Meters m
1 ft = 0.3048 m
area
Square Meters m²
1 sq ft = 0.09290304 m²
volume
Liters L
1 gal = 3.785411784 L
weight
Kilograms kg
1 lb = 0.45359237 kg
temperature
Celsius °C
°C = (°F − 32) × 0.5555555556
pressure
Pascals Pa
1 psf = 47.88025898 Pa
flow
Liters per minute L/min
1 gal/min = 3.785411784 L/min
density
Kilograms per cubic meter kg/m³
1 pcf = 16.01846337 kg/m³
Licence and credit
CC BY 4.0. Use it commercially, build on it and redistribute it; credit Craft Quantities and link back. The licence and the credit line are repeated inside every file, for whoever has the JSON open and not this page. It covers these data files, not the site’s software or pages, which are not licensed for reuse.
Data: <a href="https://craftquantities.com/">Craft Quantities</a>, CC BY 4.0Caching and change
Files are cached for an hour and carry an ETag: a request with If-None-Match returns 304 when nothing has changed. The build that produced them is named at /build-info.json.
Inside v1, fields are only ever added. An incompatible change would become /api/v2/, and v1 would keep being built.
Calling it from a browser
Every file is sent with Access-Control-Allow-Origin: *, so a plain fetch works from any origin. Keep to simple GET or HEAD requests: custom headers trigger a CORS preflight, which a static host does not answer.
A missing file returns the site’s HTML 404 page, not JSON — check response.ok before parsing, as the quick start does.
Limits
What it does not do
It does not compute
The formulas live in the site's code. What is published — every input, bound and test vector — is enough to check a re-implementation against, not a compute endpoint.
It holds no prices
The site publishes none: every price on it is one a visitor typed, and it stays on their device. The API says which inputs hold money, and whether each is a rate the caller supplies or an amount of their own.
It holds no adoption data
standards.json says what each document is and the jurisdiction it is written for, not who has adopted which edition. Adoption is local and lags publication, and this site does not track it.
Versioning
Changes inside v1
Additive only, newest first. Nothing listed here removed or renamed a field.
examples.json: every worked example, step by step, with each computed step's inputs, answer and printed figure. units.json gains offsetToBase on the temperature scales, so a Fahrenheit value converts correctly; select inputs whose options differ by unit system gain optionsBySystem; each takeoff step in assemblies.json gains the note saying why the calculator is part of the job.
link-graph.json: every link between a calculator and a page of another kind, grouped by page.
glossary.json: the cross-market construction glossary, one record per concept.
One file per cited standard (standards/{body}/{id}.json), one per reference table (reference/{table}.json), the corrections ledger, and a citation block in every calculator, standard and table file.
v1 published: index, calculators, one file per calculator, takeoffs, standards, units and the validation corpus.
The same data, on pages
Check the maths before you build on it
The validation page runs the published vectors in the open and says how to report a failure.
Generated from the site's own build
The files come from the same bundle the pages run, so they cannot drift from them.
A corrections ledger, in the open
Confirmed errors are published with the fix and what now prevents each one.
Standards named, never paraphrased
Each calculator cites its code or standard; the standards files list who cites what.
Frequently asked questions
- Is there a free API for construction calculations?
- Yes. Every input, bound, unit and published test vector behind 1,151 construction calculators is a static JSON file under https://craftquantities.com/api/v1/, licensed CC BY 4.0. There is no key, no quota and no rate limit, because there is nothing to meter — the files are served from a CDN like any other page.
- Do I need an API key or an account?
- No. Fetch the files directly. Nothing is logged against a key because there is no key, and nothing can be switched off for one caller, which is the point of publishing files rather than running a service.
- Can I call it from JavaScript in a browser?
- Yes, with a plain fetch. Every file is served with Access-Control-Allow-Origin: *. Only simple GET and HEAD requests work: a request with custom headers triggers a CORS preflight, which a static host does not answer. A missing file returns the site's HTML 404 page, so check response.ok before parsing.
- Does the API compute results?
- No. It publishes what a calculation needs — every input with its dimension, bounds and default, the sources, and the test vectors with expected results and tolerances — which is enough to check a re-implementation. The formulas themselves live in the site's code and are not an endpoint.
- How do I know when the data changes?
- Each file carries an ETag, and a request with If-None-Match returns 304 when nothing has changed. Files are cached for an hour. The build that produced them is named at /build-info.json. Inside v1, fields are only ever added; an incompatible change would become /api/v2/ while v1 keeps being built.
Base address: https://craftquantities.com/api/v1/index.json