CostCandor

= published prices × your house

A formula we admitted we invented, checked against 88 Cambridge permits

Published 2026-08-07 · records retrieved 2026-08-07 · our band fixed as of 2026-07 · how we build these numbers

Run your own numbers in the repipe calculator →

Our repipe calculator does not price pipe by the foot. It counts plumbing fixtures and multiplies. The count comes out of a formula in our own data file — fixtures = round(5 + 3.5 × bathrooms + 0.5 × (sqft / 1000)) — and beside it, in the same file, a sentence we wrote and could never defend: This is CostCandor's own estimator — no public survey of per-home fixture counts exists. Every dollar it prints is that output times a per-fixture band; if the count is wrong, nothing downstream is.

Cambridge, Massachusetts publishes the survey we said did not exist. Start with what it costs us. Refit to 88 new-construction single-family permits, our square-footage term does not shrink — it changes sign. We add half a fixture per thousand square feet; Cambridge subtracts about four tenths of one. On the only public sample we have found, a house does not acquire plumbing by getting larger. It acquires bathrooms, and the two travel together closely enough that we mistook one for the other.

The other two terms held. That is the smaller half of this, and it is below in full.

1. What this record is

Two datasets, joined, both from the City of Cambridge open-data portal. The numerator is Plumbing Permits, resource 8793-tet2, unusual in breaking fixtures out by type: separate columns for toilets, lavatories, bathtubs, shower stalls, kitchen sinks, dishwashers, washing machines, water heaters by fuel, and hose bibs. The denominator is the Cambridge Property Database FY2026, waa7-ibdu, from the Assessing Department: interior_livingarea, interior_fullbaths, interior_halfbaths, property class. Both retrieved 2026-08-07.

Licensing is settled on one side and merely unobstructed on the other. The permit file's Socrata license object names the Open Data Commons Public Domain Dedication and License 1.0, which sets no condition on derived statistics. The assessor file's license is null; we downloaded the portal's disclaimer PDF and read it — no warranties, a liability limitation, a public-records statement, nothing restricting reuse.

# license and freshness, straight from the portal metadata
curl -s 'https://data.cambridgema.gov/api/views/8793-tet2.json' | python3 -c \
  "import sys,json,datetime as dt; d=json.load(sys.stdin); print(d.get('license')); \
   print(dt.datetime.fromtimestamp(d['rowsUpdatedAt'], dt.UTC).isoformat())"
# => {'name': 'Open Data Commons Public Domain Dedication and License',
#     'termsLink': 'http://opendatacommons.org/licenses/pddl/1.0/'}
#    2026-08-06T11:03:41+00:00

# the numerator. The cap matters: this file takes about 3 permits a day, so
# without it your n will not be ours tomorrow.
curl -s -G 'https://data.cambridgema.gov/resource/8793-tet2.json' \
  --data-urlencode "\$where=issue_date < '2026-08-06'" \
  --data-urlencode '$limit=20000' -o camb_plumb.json

# the denominator. Owner name and owner address are deliberately not selected.
curl -s -G 'https://data.cambridgema.gov/resource/waa7-ibdu.json' \
  --data-urlencode '$select=map_lot,propertyclass,interior_livingarea,interior_fullbaths,interior_halfbaths' \
  --data-urlencode '$limit=60000' -o camb_prop.json

That permit pull is a whole-file pull: it brings down the applicant's name, the plumber's company name — often an individual's — the street address, and coordinates. Those columns are discarded before any arithmetic and appear nowhere on this site. Add a $select for the fixture columns, mbl, and type_of_work_to_be_performed and none of it lands on your disk.

2. What it measures — and what it quietly does not

There is no money in this file. No valuation column, no declared cost, no fee. Our per-fixture bands — $500 / $750 / $1,000 for PEX, $1,050 / $1,450 / $1,850 for copper — are untouched by everything that follows. This record checks the multiplicand and nothing else. A reader leaving with “the repipe calculator was checked against public records” has taken more than happened.

The fixture definition is ours, not Cambridge's. The file has its own fixture_count column, but it is a fee total counting floor and roof drains, which our estimator does not mean by a fixture. We ignored it and re-summed by type, matching our base-5 definition item for item: kitchen sink, dishwasher, laundry, water heater, hose bib. Reproducing our numbers means reproducing that choice.

A permit records what was filed, not what the house holds. This is the failure mode that would have quietly wrecked the check, and the data measures it. Take the 480 permits whose fixture list is shaped like a whole house — a toilet, a kitchen sink, a tub or shower — and split by work type. The ratio of filed fixtures to the count our formula predicts runs 1.06 on new construction (68 permits), 0.85 on renovations (382), and 0.60 on replacements (30) — a stricter cut than the 88-permit sample below. That gradient is what under-filing looks like, and it is why only new construction is usable.

Numerator and denominator age at different speeds. The permit file was refreshed the day before we pulled it and runs through 2026-08-05; 199 permits landed in the preceding 60 days. The assessor file was last refreshed 2025-12-10, annually per its metadata. So on a new-construction permit the matching assessor row can still describe the building torn down to make room for it — a stale denominator in exactly the cohort we depend on. Cambridge is also one city, with unusually old and dense housing stock.

3. The cohort, the join, and where 12,793 becomes 88

The join key is the weak point, so here is the ladder with what each step costs:

StepRows leftWhat went, and why
Plumbing permits in the file12,793Issue dates 2018-09-21 to 2026-08-05
Carrying a parcel identifier (mbl)12,533260 have no mbl to join on
Landing on a single-family parcel1,785The assessor file holds 30,156 parcels, only 3,745 classed SNGL-FAM-RES
Work type New, both fixture families present, living area and full baths on record88Renovations and replacements dropped for the reason in section 2

The single-family restriction is where the file mostly goes, and that is not a flaw — Cambridge is a multifamily city, our estimator a single-family one. The join is approximate. A permit's mbl reads as map, block, lot, and unit; the assessor's map_lot carries only the first two segments, so we truncate and match, and the unit distinction disappears. An accessory dwelling gets its fixtures attributed to the house.

How often that goes badly is measurable. Among the 480 whole-house-shaped permits, 27 come in at more than three times the fixtures our formula predicts — a parcel carrying more building than the assessor thinks. In the 88-permit sample there are two. We left both in; dropping outliers that flatter us is the edit this section exists to prevent.

# the join, and the fixture definition. Both are choices, so both are printed.
key = lambda mbl: '-'.join((mbl or '').split('-')[:2])   # 'MAP-BLK-LOTU' -> 'MAP-BLK'

sf = {r['map_lot']: r for r in prop
      if r.get('propertyclass') == 'SNGL-FAM-RES' and r.get('map_lot')}

NONBATH = ['num_kitchen_sinks', 'num_of_kitchen_sinks', 'num_of_dishwashers',
           'num_of_washing_machines', 'num_of_water_heater_s_gas',
           'num_of_water_heater_s_electric', 'num_of_water_heater_s_tankless',
           'num_of_water_heater_s_indirect', 'num_of_sill_cocks_hose_bib']
BATH    = ['num_of_lavatories', 'num_of_toilets',
           'num_of_bathtubs', 'num_of_shower_stalls']
tot = lambda r, cols: sum(float(r.get(c) or 0) for c in cols)

rows = []
for r in permits:
    if r.get('type_of_work_to_be_performed') != 'New':      # see section 2
        continue
    p = sf.get(key(r.get('mbl')))
    if not p:
        continue
    area, full = float(p.get('interior_livingarea') or 0), float(p.get('interior_fullbaths') or 0)
    half       = float(p.get('interior_halfbaths') or 0)
    nb, bf     = tot(r, NONBATH), tot(r, BATH)
    if area <= 0 or full <= 0 or bf < 1 or nb < 1:
        continue
    rows.append((nb, bf, full + 0.5 * half, area))
# => len(rows) == 88

4. Our formula and Cambridge's on the same axis

We fitted ordinary least squares on the design matrix [1, bathrooms, sqft / 1000], solving the normal equations by Gaussian elimination, no external library. The dependent variable is total supply fixtures, bath plus non-bath, per section 2; half baths count 0.5, as in our calculator.

TermOur valueCambridge, n=88
Base non-bath fixtures5Median 4.0, mean 5.4, fitted intercept 6.09
Per bathroom3.5Ratio median 3.43, fitted coefficient 3.81
Per 1,000 sq ft+0.5−0.39 — sign reversed
Whole formula5.00 + 3.50 × baths + 0.50 × (sqft/1000)6.09 + 3.81 × baths − 0.39 × (sqft/1000)

Feed both formulas the same three houses, including our calculator's headline scenario:

HouseOur fixture countCambridge refitDifference
1,500 sq ft, 2.0 baths1313.1+1%
2,000 sq ft, 2.5 baths — headline scenario1514.8−1%
2,500 sq ft, 3.0 baths1716.5−3%

The base and per-bathroom terms survive, but not as a coefficient match. Our 3.5 per bathroom sits between the observed ratio median of 3.43 and the fitted 3.81; our base of 5 sits between the observed median of 4.0 and the mean of 5.4. Both fitted figures come in above ours, and both have to: a fit that subtracts for floor area must recover that plumbing somewhere. What holds is the plainer comparison — the observed distributions bracket both of our values.

The square-footage term is different, and we will not overclaim in either direction. Floor area and bathroom count are strongly collinear, and no sample of 88 can separate them, so we are not asserting that square feet remove plumbing. The narrower claim still runs against us: our positive coefficient finds no support here, and once bathrooms are in the model the residual pull of floor area is, if anything, the other way.

It survives as a total because it is small. At 2,000 square feet our term contributes one whole fixture and Cambridge's −0.78; the intercept and bathroom coefficient absorb the difference. Mind the rounding in that column: our side is the integer the calculator prints, Cambridge's the raw fit. Put both formulas through our rounding step and all three rows return the same count. That is how totals land within 3% while one of three parts points backwards — and why agreement at the total is not agreement in the parts.

5. Verdict

Verdict

(A) Band confirmed — in part, and the part that failed is the more interesting half.

Three things failed or went unchecked, and they come first. The perThousandSqFt coefficient reverses sign, so a third of our formula is unsupported by the only outside record we have found. No dollar figure was tested — this file has no money in it, so our per-fixture bands are untouched. And the check rests on 88 rows from one old, dense city, joined on a truncated parcel key.

What passed carried the estimator. The base and per-bathroom terms both land inside the observed spread, and at all three spec points the two formulas round to the same fixture count — the integer the calculator actually uses. Nothing here justifies moving a number, and we have not moved one. The formula above stands as it did on the 2026-07 stamp of our repipe data, which predates the 2026-08-07 retrieval.

The correction this record forces is not numeric. Our repipe data file states that no public survey of per-home fixture counts exists. That sentence is now false, and it was doing work: it justified the estimator being unsourced. Replacing it, and recording that one term failed, is filed separately rather than written back over what you just read.

6. If you are collecting repipe quotes

The lesson transfers even though the data does not. Both formulas agree on which input matters: bathrooms dominate, floor area barely registers, and ours may have been handing square footage credit that belongs to bathroom count. So do not let a bidder scale the job off house size. Count your own fixtures, by type — kitchen sink, dishwasher, laundry, water heater, hose bibs, then per bathroom the sinks, toilet, tub, shower. Ask whether the bid is per fixture or per foot, and which fixtures were counted; the base items are what quietly fall out, because nobody looks outside a bathroom.

Then use the paperwork the way this record does. In Cambridge the permit lists fixtures by type, so the filing cross-checks the bid for free — if the filing lists fewer fixtures than the bid counted, have that conversation before work starts. Ask your own jurisdiction whether its plumbing permit captures a breakdown or one fee total. And if you are pulling neighbors' permits to sanity-check a price, take only new construction: renovation and replacement filings describe the corner somebody touched, and understate a house by the margin in section 2. Our slab leak guide covers the case where a repipe is not elective.

Frequently asked questions

Can I look up how many plumbing fixtures a house has from public records?

In a few jurisdictions, yes. Cambridge, Massachusetts is one: its plumbing permit file carries a separate column for toilets, lavatories, bathtubs, shower stalls, kitchen sinks, dishwashers, washing machines, water heaters by fuel type, and hose bibs. Most permit files publish a single fixture total computed for the fee, or nothing at all. The catch is that a permit lists what was filed, not what the building contains — only new-construction filings describe a whole house, and even those have to be joined to an assessor file before you know how many bathrooms and square feet the fixtures belong to.

Does a bigger house need more repiping?

Less than we assumed, once bathrooms are accounted for. Our estimator adds half a fixture per thousand square feet on top of the bathroom count. Refit to the Cambridge sample the same term comes back at −0.39 — the sign reverses. Floor area and bathroom count move together, so no single sample can cleanly separate them, and we would not read that negative coefficient as a real effect. What it does say is that our positive one is not supported here: on this evidence what drives a fixture count is bathrooms, and square footage was riding along with them.

Why does this record only use new-construction permits?

Because renovation and replacement permits list the fixtures that were touched, not the fixtures in the house, and the data shows exactly that. Across 480 Cambridge permits that describe a whole-house-shaped job, the ratio of filed fixtures to the count our formula predicts runs 1.06 on new construction, 0.85 on renovations, and 0.60 on replacements. Padding the sample with those two categories would multiply its size and pull every coefficient down for a reason that has nothing to do with plumbing.

Did this change the repipe calculator?

Not the numbers. The fixture totals our formula produces land within 3% of the Cambridge refit at 1,500, 2,000, and 2,500 square feet, and at all three the two formulas round to the same fixture count. What has to change is a sentence: our repipe data file states that no public survey of per-home fixture counts exists. This record is one. That correction is dated separately from the formula above, which is printed as it stood before the check.

Cost math by CostCandor