BuildCalc API

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) with confidence_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.

ParamTypeDescription
categorystring (required)One of the 15 supported categories — see /v1/costs/list
periodstring (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).

ParamTypeDescription
tradestring (required)One of the 11 supported trades — see /v1/costs/list
msastring5-digit CBSA code (e.g. 19100 = Dallas-Fort Worth-Arlington)
countystring5-digit county FIPS (e.g. 48113 = Dallas County, TX)
zipstring5-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:

ValueWhen
measuredMSA path OR final fallback to OEWS MSA when no QCEW data
computed5-digit NAICS reported at county + MSA, coverage >=50%
computed_with_fallback5-digit suppressed at county; using state-level or parent NAICS 238
computed_low_coverageCounty 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.

ParamTypeDescription
msastring (required)5-digit CBSA code
socstring (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.

ParamTypeDescription
areastring (required)5-digit area: county FIPS / CBSA / state FIPS / US00000
naicsstring (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.

ParamTypeDescription
jurisdictionstring (required)US-{state}-{county_fips} (e.g. US-TX-48113), US-MSA-{cbsa} (e.g. US-MSA-19100), or bare 5-digit FIPS/CBSA
periodstring (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

CronScheduleHeartbeat grace
bcapi-etl-bls-ppi (Materials)day 16 monthly, 14:00 UTC1 day
bcapi-etl-bls-oews (Labor MSA)April 3 annually, 14:00 UTC7 days
bcapi-etl-bls-qcew (Labor county/ZIP)day 7 quarterly (Mar/Jun/Sep/Dec), 08:00 UTC7 days
bcapi-etl-census-bps (Permits)day 20 monthly, 14:00 UTC1 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.

On this page