# The published data — files, fields and limits

URL: https://floorcostestimator.com/cost-data
Last updated: 2026-08-19
License: CC BY 4.0

1704 advertised US retail price observations of 839
hard-surface flooring products from 8 manufacturers, taken on August 19, 2026,
published as 5 static files under CC BY 4.0. Prices are per square foot, materials
only — no installation labor — and no price is attributed to a named retailer.

## How much evidence is behind this

839 products: 27 with 3+ observations (3%), 811 with 2 (97%), 1 with 1 (0%). 1704 observations: 1699 exact SKU match (100%), 5 likely match (0%).

## Files

| File | Holds | Size |
|---|---|---|
| `https://floorcostestimator.com/data/price-band-v1.json` (application/json) | Everything: coverage, sample, the pooled band, per-product observations, release confidence and the integrity manifest. Every figure on this site comes from here. | 839 products |
| `https://floorcostestimator.com/data/price-band-v1.csv` (text/csv) | One row per product — identity, box size, observation count, observed low and high, the publisher’s own price. The observations themselves are not in this file; use the JSON for those. | 839 rows |
| `https://floorcostestimator.com/data/history.json` (application/json) | The pooled band on every archived observation date, each row carrying the number of products and retailers it was measured over, plus a block counting how often an individual price changed from one day to the next. | 1 observation date |
| `https://floorcostestimator.com/data/cost-by-size.json` (application/json) | The band worked through job sizes: waste allowance, whole-box rounding, billed area and material cost at the lowest, median and highest observation. | 12 job sizes |
| `https://floorcostestimator.com/data/dictionary.json` (application/json) | The field dictionary as data: every field in the release with its type, unit, whether it may be null, and how many records in the current release actually carry it. | 49 documented fields |

No key, no account, no rate limit. Counts describe the current release, not the next one.

## Do these prices change

Not measurable on this release. The archive holds 1 observation date: it
restarted on 2026-08-19, when the index stopped taking its product list from one
retailer's shelf and started building it from the overlap between independent sellers. The earlier
archive measured a different set of products at a different set of sellers, so continuing that
series here would publish a change of method as a change of price. Day-over-day movement returns
with the second collection day.

The panel comparison that separates a price move from a panel change needs two dates as well, so
it is absent here rather than approximated. Every row below still carries the panel it was measured
over, which is what makes that comparison possible once there is a second row.

| Observed | Low | Median | High | Products | Sources |
|---|---|---|---|---|---|
| 2026-08-19 | $1.13 | $3.99 | $16.79 | 839 | 3 |

## Field dictionary

Two facts per field, kept apart: *null allowed* is the schema contract, *filled* is a measurement of
this release. A field can be nullable and full — `low` is null whenever a product has no
observations, and every product in this release has some.

### Release level — top level of price-band-v1.json

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|
| `schemaVersion` | version | no | 1 / 1 | Shape of this file. A change that moves or removes a field raises it; adding a field does not. |
| `datasetId` | identifier | no | 1 / 1 | Stable name for the dataset, so successive releases can be recognised as the same series. |
| `dataAsOf` | date (ISO 8601 date) | no | 1 / 1 | The day every price in the file was observed. Not the day the file was written. |
| `coverage` | block | no | 1 / 1 | What is priced and what is excluded, in the publisher’s own words: category, manufacturer, geography, price basis, exclusions. |
| `publisher` | block | no | 1 / 1 | Who publishes the file and how its own prices compare with the cheapest observation of the same product. |
| `sample` | block | no | 1 / 1 | Counts behind the release: products, observations, retailers tracked, retailers that contributed, exact and likely matches, archive depth. |
| `bands` | array of blocks | no | 1 / 1 | The pooled price distribution. One entry in this release, segment "all": low, p25, median, p75, high. |
| `confidence` | block | no | 1 / 1 | Release-level confidence, the fixed rule that decided it, and the reason codes behind it. Not the same scale as a product’s own tier. |
| `freshness` | block | no | 1 / 1 | Age of the observation in days and the status derived from it: fresh, aging or stale. |
| `skus` | array of products | no | 1 / 1 | One entry per priced product, each carrying the product-level fields documented separately. |
| `manifest` | block | no | 1 / 1 | Integrity record: source snapshot filename, its SHA-256, its byte and product counts, the generator, and when it ran. |

### Product level — every entry in skus

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|
| `id` | identifier | no | 839 / 839 | Slug for the product inside this dataset. Stable between releases; it is not a retailer’s identifier. |
| `title` | text | no | 839 / 839 | Product title as a seller states it, including the specification boilerplate. Where two sellers title one product differently, the longer title is kept. |
| `brand` | text | no | 839 / 839 | Brand field as the sellers state it, not normalised. Where two sellers spell one maker differently the majority spelling wins the row, and the disagreement is not rewritten away. |
| `family` | text | yes | 822 / 839 | Construction and installation method, read from the sellers’ own product type and title: glue-down-vinyl, loose-lay-vinyl, floating-vinyl, laminate or engineered-wood. Null where the two sellers describe it differently or neither says — treat null as unknown, never as a default family. |
| `mfrSku` | identifier | no | 839 / 839 | Manufacturer SKU. This is the key an exact match is confirmed against. |
| `boxSqft` | number (sq ft) | yes | 839 / 839 | Square feet in one sealed box. What billed area rounds up to, and the reason a small job costs more per square foot than a large one. |
| `z4Psf` | price (USD/sqft) | yes | 46 / 839 | The publisher’s own advertised price. Named here because the publisher is the one seller this dataset identifies. |
| `n` | count | no | 839 / 839 | Observations behind this product. 0 means unpriced: report it as unpriced rather than substituting the site-wide band. |
| `low` | price (USD/sqft) | yes | 839 / 839 | Cheapest observation of this product. Null when n is 0. |
| `high` | price (USD/sqft) | yes | 839 / 839 | Dearest observation of this product. Equal to low when n is 1 — one price, not a range. |
| `obs` | array of observations | no | 839 / 839 | The observations themselves, in no meaningful order. Empty when n is 0. |

### Observation level — every entry in skus[].obs

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|
| `psf` | price (USD/sqft) | no | 1704 / 1704 | Advertised price normalised to one square foot, materials only, before tax, shipping and any negotiated discount. |
| `match` | enum (strong | weak) | no | 1704 / 1704 | strong: the manufacturer SKU was confirmed on the listing. weak: the title matched but the SKU could not be confirmed, so it may be a different specification at the same name. |
| `minOrderSqft` | number (sq ft) | yes | 0 / 1704 | Minimum order the listing stated. Null means the listing stated none — not that none exists. |
| `boxSqftDerived` | flag | no | 1704 / 1704 | True when the listing did not publish a box size and the manufacturer’s figure was used to derive the per-square-foot price. |

### Series level — every row in history.json

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|
| `date` | date (ISO 8601 date) | no | 1 / 1 | The day the prices behind this row were observed. |
| `products` | count | no | 1 / 1 | Products the crawl matched that day. It grew during the archive, so two rows are not automatically comparable. |
| `observations` | count | no | 1 / 1 | Prices pooled into this row’s band. |
| `sources` | count | no | 1 / 1 | Retailers that contributed at least one price that day. The single largest move in this series is a change in this number, not a change in any price. |
| `low` | price (USD/sqft) | yes | 1 / 1 | Cheapest observation pooled across all products that day. |
| `p25` | price (USD/sqft) | yes | 1 / 1 | Quarter of the day’s observations sat at or below this. |
| `median` | price (USD/sqft) | yes | 1 / 1 | Middle observation of the day. The figure quoted on the rest of the site is this one, for the most recent date. |
| `p75` | price (USD/sqft) | yes | 1 / 1 | Three quarters of the day’s observations sat at or below this. |
| `high` | price (USD/sqft) | yes | 1 / 1 | Dearest observation pooled across all products that day. |

### Stability block — history.json, under stability

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|
| `dates` | count | no | 1 / 1 | Observation dates in the archive these figures were computed over. |
| `comparisons` | count | no | 1 / 1 | Times a price was compared with itself — the same product at the same retailer, one day apart. This is the denominator; the number of observations is not. |
| `held` | count | no | 1 / 1 | Comparisons where the price was identical to the day before. |
| `changed` | count | no | 1 / 1 | Comparisons where it was not. |
| `changedPct` | percentage (%) | yes | 0 / 1 | changed as a share of comparisons. Null when there is only one date, which is a different statement from a rate of zero. |
| `moveMinPct` | percentage (%) | yes | 0 / 1 | Smallest movement among the changes, in percent. Null when nothing changed. |
| `moveMedianPct` | percentage (%) | yes | 0 / 1 | Middle movement among the changes. Says how big a change is when one happens, which the rate alone does not. |
| `moveMaxPct` | percentage (%) | yes | 0 / 1 | Largest single movement observed in the archive. |
| `appeared` | count | no | 1 / 1 | Prices seen for the first time on a day. Catalogue churn, counted apart from movement so it cannot inflate the change rate. |
| `vanished` | count | no | 1 / 1 | Prices that stopped being listed. Also not a movement. |
| `spanFirst` | date (ISO 8601 date) | yes | 1 / 1 | First date in the archive. |
| `spanLast` | date (ISO 8601 date) | yes | 1 / 1 | Last date in the archive. |
| `spanPairs` | count | no | 1 / 1 | Product-and-retailer pairs present on both the first and the last date — the ones that can be compared end to end. |
| `spanIdentical` | count | no | 1 / 1 | How many of those carried the same price on both. A daily rate cannot answer this: a price that rises and falls back moves twice and ends where it started. |

### Panel block — history.json, under panel

Not in the current file: it compares the first archived observation date with the last, and the archive holds one date. It returns with the second.

| Field | Type | Null allowed | Filled | What it holds |
|---|---|---|---|---|


## What will break if you query this file

- **1704 observations, 156 distinct prices.** Counting observations counts catalogue coverage, not independent price checks. A retailer prices a whole product line at one figure and carries it across every colourway, so one decision produces many rows. Any confidence interval computed as though the rows were independent will be far too narrow.
- **minOrderSqft is null on 1704 of 1704 observations.** Null means the listing did not state a minimum, not that none applies. Treating null as "no minimum" reports prices as reachable for small jobs when they may not be.
- **0 of 1704 observations carry a derived box size.** The listing did not publish square feet per box, so the manufacturer’s figure was used to compute the price per square foot. Those prices are derived rather than printed, and they are the ones an audit should check first.

## Integrity

| Field | Value |
|---|---|
| Release | 1.1.0 · us-retail-lvp-observed-sqft |
| Observed | 2026-08-19 |
| Source snapshot | 2026-08-19.json |
| SHA-256 | 4a87967b0efb4dc8c0b795113383d1ac9bfea0a23ec4b1262f05f749a57b5b64 |
| Snapshot size | 733,577 bytes · 839 products |
| Generated by | @z4/veri-yayin@1.0.0 · 2026-11-10T16:51:11.577Z |

That digest is of the input snapshot, which is not itself published. It identifies the observation
run this release was built from; it is not a checksum of the file you downloaded, and hashing the
download will not reproduce it. A checksum of the published files is not published today.

## Stability

`schemaVersion` is raised when a field moves, is removed or changes meaning, and is not raised when
a field is added. Two things will not change without it: prices carry no retailer identity, and no
field will ever carry an installation or labor figure.

## What the data does not support

- Installed cost. No labor figure exists here and none should be derived from these numbers.
- The flooring market. 839 products from 8 manufacturers at 3 readable sellers; one maker supplies 39.7% of them.
- A single number for "flooring". The pooled band holds several constructions; quote a family band instead.
- Regional or metro pricing. Every observation is a US national online listing.
- Retailer comparison. The panel is named in full; prices are not attributed to its members.
- Forecasts. Every figure is an observation of one past day.
- Per-product certainty. 1 products rest on a single observation and 0 rest entirely on likely matches.

## Citation

    Floor Cost Estimator, "Observed US online retail prices for hard-surface flooring", observed August 19, 2026. Published by a materials-only flooring retailer that sells the category it prices. https://floorcostestimator.com/methodology

Licensed CC BY 4.0. Carry the conflict of interest with it: this site is operated by a flooring
retailer that sells what it prices, and on this release the figure that measures that conflict is withheld because 3 of 7 tracked sellers could not be read.
