Costs vertical
Federal-data price and labor metrics — BLS PPI for 15 construction-material categories, OEWS+QCEW hourly wages for 11 trades at MSA/county/ZIP granularity, and Census BPS building permits for ~3,100 counties + ~930 CBSAs.
What's in it
BuildCalc API's costs vertical exposes federal-data price and labor metrics so AI agents and developers can ground cost estimates against an authoritative source instead of hallucinating dollar values.
- Materials (live) — 15 construction categories backed by 18 hand-curated BLS Producer Price Index series. National-level, refreshed monthly on the 16th, ~12 months of trailing history per category.
- Labor — MSA path (live) — BLS OEWS for 11 construction trades at
MSA granularity (~530 MSAs). Median + p25 + p75 hourly wages +
employment counts.
confidence: "measured"(direct OEWS). Refreshed annually in April after the BLS May-reference-year release. - Labor — county + ZIP paths (live) — OEWS x QCEW geographic-
scaled estimates for all ~3,100 US counties and all 5-digit US ZIPs.
4-level confidence taxonomy (
measured/computed/computed_with_fallback/computed_low_coverage) withconfidence_reason,methodology,inputs.*for full audit. See Methodology for the formula and the 7 known limitations. - Permits (live) — Census Building Permits Survey for ~3,100 US counties + ~930 CBSAs. 4 structure-type breakouts (1u / 2u / 3-4u / 5+u) for buildings + units + valuation, refreshed monthly.
All endpoints carry the data:costs tag so they're exposed through the MCP
server alongside the codes and calculator tools.
Auth + rate limits
Same as the rest of the API — Authorization: Bearer bcapi_… header, per-tier
rate limits, metered overage billing. Every authenticated request to
/v1/costs/* records one usage event.
Endpoints
All paths are prefixed https://api.buildcalcapi.dev.
GET /v1/costs/list
Discovery — returns the catalog of cost data plus a dynamic status per
vertical. Status flips from coming_soon to live automatically when the
corresponding cron has ingested data; no code change required.
curl https://api.buildcalcapi.dev/v1/costs/list \
-H "Authorization: Bearer bcapi_..."{
"verticals": {
"materials": {
"status": "live",
"categories": [
"concrete", "doors-windows", "drywall", "electrical", "flooring",
"gutters", "hvac", "insulation", "lumber", "masonry", "paint",
"plumbing", "roofing", "site", "stairs"
],
"coverage": "US national PPI indices (15 categories, 18 BLS series)",
"refresh": "monthly"
},
"labor": {
"status": "live",
"trades": [
"brickmason", "carpenter", "construction-laborer", "drywall-installer",
"electrician", "hvac-tech", "painter", "plumber", "roofer",
"sheet-metal-worker", "stonemason"
],
"coverage": "All US MSAs (~530) + all ~3,100 US counties + all 5-digit ZIPs via OEWS x QCEW geographic scaling. 4-level confidence taxonomy.",
"refresh": "annual (OEWS) + quarterly (QCEW)"
},
"permits": {
"status": "live",
"jurisdictions": "~3,100 US counties + ~930 CBSAs (monthly)",
"coverage": "Census BPS 4 structure-type breakouts (1u / 2u / 3-4u / 5+u) for buildings, units, and valuation.",
"refresh": "monthly"
}
},
"unsupported_categories": {
"misc": "Catch-all category; no federal source maps to 'miscellaneous'."
},
"methodology": "/docs/methodology/costs"
}GET /v1/costs/materials
Returns the latest Producer Price Index value for a construction material category plus 12 months of trailing history.
| Param | Type | Description |
|---|---|---|
category | string (required) | One of the 15 supported categories — see /v1/costs/list |
period | string (optional) | YYYY-MM (e.g. 2026-04); default = latest |
Multi-series categories (flooring = wood millwork + soft floor coverings;
doors-windows = wood doors + metal doors; site = sand/gravel + ready-mix
concrete) average their contributing series with equal weight per period.
Use /v1/costs/sources/ppi/{series_id} if you need the raw per-series data.
# Latest lumber PPI
curl 'https://api.buildcalcapi.dev/v1/costs/materials?category=lumber' \
-H "Authorization: Bearer bcapi_..."
# Flooring at a specific historical month
curl 'https://api.buildcalcapi.dev/v1/costs/materials?category=flooring&period=2026-01' \
-H "Authorization: Bearer bcapi_..."Response shape:
{
"category": "lumber",
"period": "2026-04",
"index_value": 283.04,
"base_period": "1982",
"source": "BLS Producer Price Index",
"source_series_id": "WPU0811",
"source_url": "https://data.bls.gov/timeseries/WPU0811",
"history": [
{"period": "2026-04", "index_value": 283.04},
{"period": "2026-03", "index_value": 281.17},
{"period": "2026-02", "index_value": 278.92},
"..."
],
"note": "PPI is a national-level relative price index. Use for time-series trends and category comparisons; NOT absolute pricing in dollars."
}Multi-series example (flooring):
{
"category": "flooring",
"period": "2026-04",
"index_value": 216.32,
"base_period": "varies",
"source": "BLS Producer Price Index (avg of 2 series)",
"source_series_id": "PCU337212337212, WPU0922",
"source_url": "https://data.bls.gov/timeseries/PCU337212337212",
"history": [
{"period": "2026-04", "index_value": 216.32},
"..."
],
"note": "..."
}GET /v1/costs/sources/ppi/{series_id}
Raw escape hatch — returns the most-recent 24 monthly rows for one BLS
series. For auditors who want to verify the multi-series averaging in
/v1/costs/materials against the raw ingestion, or who need a specific
BLS series outside our category mapping.
# Softwood lumber raw data
curl https://api.buildcalcapi.dev/v1/costs/sources/ppi/WPU0811 \
-H "Authorization: Bearer bcapi_..."[
{
"series_id": "WPU0811",
"period": "2026-04",
"index_value": 283.04,
"base_period": "1982",
"source_url": "https://data.bls.gov/timeseries/WPU0811",
"fetched_at": "2026-05-19T16:57:49.366000+00:00"
},
"..."
]GET /v1/costs/labor
Returns hourly wage for a construction trade at one of three geographic levels: MSA (direct OEWS), county (OEWS x QCEW scaled), or ZIP (resolved to dominant county then scaled).
| Param | Type | Description |
|---|---|---|
trade | string (required) | One of the 11 supported trades — see /v1/costs/list |
msa | string | 5-digit CBSA code (e.g. 19100 = Dallas-Fort Worth-Arlington) |
county | string | 5-digit county FIPS (e.g. 48113 = Dallas County, TX) |
zip | string | 5-digit ZIP code (e.g. 75201) |
Exactly one of msa / county / zip must be provided.
MSA path
Direct OEWS row; confidence: "measured".
curl 'https://api.buildcalcapi.dev/v1/costs/labor?trade=electrician&msa=19100' \
-H "Authorization: Bearer bcapi_..."{
"trade": "electrician",
"msa_code": "19100",
"msa_name": "Dallas-Fort Worth-Arlington, TX",
"period": "2025",
"median_hourly_usd": 28.37,
"p25_hourly_usd": 22.85,
"p75_hourly_usd": 34.32,
"employment_count": 20930,
"confidence": "measured",
"source": "BLS OEWS",
"source_soc_code": "47-2111",
"source_url": "https://www.bls.gov/oes/current/oes472111.htm"
}p25_hourly_usd, p75_hourly_usd, and employment_count may be null
when BLS suppresses small-cell estimates. Median is always present.
County path
OEWS MSA median x QCEW county-to-MSA ratio for the matching NAICS. Full response shape with the 4-level confidence taxonomy + the spec's audit fields:
curl 'https://api.buildcalcapi.dev/v1/costs/labor?trade=electrician&county=48113' \
-H "Authorization: Bearer bcapi_..."{
"trade": "electrician",
"county_fips": "48113",
"resolved_msa_code": "19100",
"estimated_median_hourly_usd": 38.45,
"confidence": "computed",
"confidence_reason": "NAICS 23821 reported at county and MSA; 5-digit ratio used",
"methodology": "OEWS MSA median scaled by QCEW county-to-MSA NAICS wage ratio (5-digit)",
"inputs": {
"oews_msa_median_hourly_cents": 3542,
"oews_msa_soc_code": "47-2111",
"oews_period": "2025",
"qcew_county_naics_avg_weekly_wage_cents": 131800,
"qcew_msa_naics_avg_weekly_wage_cents": 118400,
"qcew_naics_code": "23821",
"qcew_naics_title": "Electrical Contractors",
"geographic_adjustment_ratio": 1.1132,
"county_naics_coverage_pct": 91
},
"paired_trades": null,
"all_trades_share_ratio": false,
"degenerate_msa": false,
"fallback_used": null,
"systematic_error_pct_range": [8, 15],
"bid_markup_factor_range": [1.4, 2.2],
"limitations_note": "Geographic ratio assumes the county and MSA share occupational mix within the industry. ...",
"wage_vs_bid_note": "This is a wage estimate (what workers earn), NOT a bid estimate ...",
"sources": [
"https://www.bls.gov/oes/current/oes472111.htm",
"https://www.bls.gov/cew/"
],
"see_also": "https://docs.buildcalcapi.dev/docs/methodology/costs"
}confidence is one of:
| Value | When |
|---|---|
measured | MSA path OR final fallback to OEWS MSA when no QCEW data |
computed | 5-digit NAICS reported at county + MSA, coverage >=50% |
computed_with_fallback | 5-digit suppressed at county; using state-level or parent NAICS 238 |
computed_low_coverage | County has under 50% trade-NAICS coverage |
Pair (plumber+hvac-tech, brickmason+stonemason) shares the same NAICS;
paired_trades is set when applicable. When the fallback drops to
parent NAICS 238, all_trades_share_ratio=true. See
Methodology for the formula + 7 known
limitations.
ZIP path
ZIP -> dominant county via HUD ZIP-CBSA crosswalk (max residential
delivery ratio), then identical to the county path. Response surfaces
resolved_county_fips + resolved_msa_code so the resolution is
auditable.
curl 'https://api.buildcalcapi.dev/v1/costs/labor?trade=electrician&zip=75201' \
-H "Authorization: Bearer bcapi_..."{
"trade": "electrician",
"zip": "75201",
"resolved_county_fips": "48113",
"resolved_msa_code": "19100",
"estimated_median_hourly_usd": 38.45,
"confidence": "computed",
"...": "..."
}Rural ZIPs (cbsa_code = 99999 in the HUD crosswalk) fall back to
county-level QCEW without OEWS MSA scaling; the response sets
rural_note: true.
GET /v1/costs/sources/oews
Raw escape hatch — returns the single cost_labor_oews row for one (MSA, SOC)
combination at the latest available period, with wages in integer cents
(no float rounding). For auditors who need to verify the labor endpoint's
USD conversions against the raw BLS data.
| Param | Type | Description |
|---|---|---|
msa | string (required) | 5-digit CBSA code |
soc | string (required) | SOC code with hyphen (e.g. 47-2111) |
curl 'https://api.buildcalcapi.dev/v1/costs/sources/oews?msa=19100&soc=47-2111' \
-H "Authorization: Bearer bcapi_..."{
"soc_code": "47-2111",
"trade": "electrician",
"msa_code": "19100",
"msa_name": "Dallas-Fort Worth-Arlington, TX",
"period": "2025",
"median_hourly_cents": 2837,
"p25_hourly_cents": 2285,
"p75_hourly_cents": 3432,
"employment_count": 20930,
"source_url": "https://www.bls.gov/oes/current/oes472111.htm",
"fetched_at": "2026-05-19T17:46:30.000+00:00"
}GET /v1/costs/sources/qcew
Raw QCEW row escape hatch for one (NAICS, area) combination at the latest available period.
| Param | Type | Description |
|---|---|---|
area | string (required) | 5-digit area: county FIPS / CBSA / state FIPS / US00000 |
naics | string (required) | 5-digit specialty trade NAICS (e.g. 23821) or parent 238 |
curl 'https://api.buildcalcapi.dev/v1/costs/sources/qcew?area=48113&naics=23821' \
-H "Authorization: Bearer bcapi_..."GET /v1/costs/permits
Returns Census Building Permits Survey data for one jurisdiction (county or MSA) at one period — 4 structure-type breakouts plus aggregates.
| Param | Type | Description |
|---|---|---|
jurisdiction | string (required) | US-{state}-{county_fips} (e.g. US-TX-48113), US-MSA-{cbsa} (e.g. US-MSA-19100), or bare 5-digit FIPS/CBSA |
period | string (optional) | YYYY-MM (default = latest available) |
curl 'https://api.buildcalcapi.dev/v1/costs/permits?jurisdiction=US-TX-48113' \
-H "Authorization: Bearer bcapi_..."{
"jurisdiction_code": "48113",
"jurisdiction_type": "county",
"jurisdiction_name": "Dallas County",
"state_code": "48",
"period": "2026-03",
"structure_types": [
{
"code": "101",
"label": "1-unit (single family)",
"buildings": 215,
"units": 215,
"valuation_usd": 95012000
},
{
"code": "103",
"label": "2-unit (duplex)",
"buildings": 2,
"units": 4,
"valuation_usd": 850000
},
{
"code": "104",
"label": "3-4 unit",
"buildings": 0,
"units": 0,
"valuation_usd": 0
},
{
"code": "105",
"label": "5+ unit (multifamily)",
"buildings": 8,
"units": 412,
"valuation_usd": 65300000
}
],
"total_units": 631,
"total_valuation_usd": 161162000,
"source": "Census Building Permits Survey",
"source_url": "https://www.census.gov/construction/bps/",
"fetched_at": "2026-05-19T19:46:50.000+00:00"
}GET /v1/costs/sources/permits/{jurisdiction_code}
Raw cost_permits_bps row — no aggregation, no rollups.
curl 'https://api.buildcalcapi.dev/v1/costs/sources/permits/48113' \
-H "Authorization: Bearer bcapi_..."Methodology
See the Methodology page for the full description of the OEWS x QCEW geographic scaling formula, the 4-level confidence taxonomy, the fallback chain, and the 7 known limitations (occupational composition, NAICS suppression bias, trade-pair coupling, dominant-county degeneracy, wage-vs-bid distinction, time-series misalignment, geographic completeness over precision).
Quick summary of the formula for the county/ZIP paths:
estimated_median_hourly = oews_msa_median × (qcew_county / qcew_msa)PPI is a relative price index, not a dollar value. The 18 series IDs we ingest cover 15 calculator-relevant categories chosen from the BLS WPU (commodity-based), WPS (sector-stage processing), and PCU (industry-based) taxonomies.
Refresh schedule
| Cron | Schedule | Heartbeat grace |
|---|---|---|
bcapi-etl-bls-ppi (Materials) | day 16 monthly, 14:00 UTC | 1 day |
bcapi-etl-bls-oews (Labor MSA) | April 3 annually, 14:00 UTC | 7 days |
bcapi-etl-bls-qcew (Labor county/ZIP) | day 7 quarterly (Mar/Jun/Sep/Dec), 08:00 UTC | 7 days |
bcapi-etl-census-bps (Permits) | day 20 monthly, 14:00 UTC | 1 day |
Every cron pings a dedicated Better Stack heartbeat on rc=0 only; missed runs trigger an email alert.
Roadmap
Phase 6 costs vertical is COMPLETE as of v0.2.0 (2026-05-19). Track future verticals (products, benchmarks, calculators) in CHANGELOG.md.