{
  "schema": "chathome-open-data-release-v1",
  "version": "2026.10",
  "generatedAt": "2026-10-18T22:40:40.398Z",
  "publisher": {
    "name": "ChatHome",
    "url": "https://chathome.lu",
    "email": "hello@chathome.lu"
  },
  "filesBaseUrl": "https://chathome.lu/data/files",
  "datasets": [
    {
      "slug": "commune-asking-prices",
      "title": "Luxembourg housing asking prices by commune and month",
      "description": "Asking prices per square metre of living area for apartments and houses offered for sale or rent in each of Luxembourg's 100 communes and in the 24 official quarters of Luxembourg City. Values are the median and the 25th and 75th percentiles of the residential listings that were active on ChatHome when the statistics were computed; ChatHome collects these listings from estate-agency websites. The data has four segments (sale apartment, sale house, rent apartment, rent house), a current snapshot per commune and per quarter, and a monthly history in long format that grows with each release. A median or quartile is published only when at least 5 listings support it; otherwise the cell is empty, never zero. With few listings, a median or quartile can equal a single advert's asking price per m²; the adverts are public, so this reveals no private data. For reference, the commune snapshot also carries, where available, two official Observatoire de l'Habitat series from data.public.lu: the mean asking rent over a rolling 12-month window and the mean registered apartment sale price. These differ from the ChatHome figures in statistic (mean, not median), window, source and coverage, and the registered price is a transaction figure while every other price here is an asking price. Each commune row carries the official 4-digit commune code used by STATEC and data.public.lu.",
      "licence": {
        "id": "CC-BY-4.0",
        "name": "Creative Commons Attribution 4.0 International",
        "url": "https://creativecommons.org/licenses/by/4.0/"
      },
      "pageUrl": "https://chathome.lu/en/data/commune-asking-prices",
      "temporalCoverage": "2026-07/2026-10",
      "spatialCoverage": [
        {
          "name": "Luxembourg (100 communes)",
          "identifier": "LU"
        },
        {
          "name": "Luxembourg City (24 official quarters)"
        }
      ],
      "keywords": [
        "Luxembourg",
        "housing",
        "asking prices",
        "advertised rents",
        "price per square metre",
        "commune",
        "Luxembourg City quarters",
        "real estate",
        "open data"
      ],
      "measurementTechnique": "Median, 25th and 75th percentile of asking price per square metre of living area, computed over the residential listings active at computation time.",
      "methodology": [
        "A median, 25th or 75th percentile is published only when at least 5 listings support it; below that the value is empty, never zero. With few listings, a median or quartile can equal a single advert's asking price per m²; the adverts are public, so this reveals no private data.",
        "The official columns follow the Observatoire de l'Habitat's own disclosure rules: a mean asking rent needs at least 30 adverts, a mean registered apartment price at least 10 deeds.",
        "Listings: residential listings that are active and visible in the ChatHome catalogue at computation time. Disabled, sold, let and withdrawn listings are excluded.",
        "Apartments are apartment, flat, studio, penthouse and duplex listings; houses are house, townhouse, villa and bungalow listings. Living area must be 10 to 2,000 m². Sale prices outside EUR 500 to 40,000 per m² and rents outside EUR 3 to 120 per m² per month are discarded as implausible.",
        "A listing is assigned to a commune from the town stated on the listing; Luxembourg City quarters fold into the Luxembourg City commune in the commune tables.",
        "Statistics are rounded to whole euros by the commune statistics job, rents per m² per month included.",
        "commune_prices_monthly keeps one snapshot per month. A month with an archived release snapshot uses it for all four segments; any other month uses ChatHome's published monthly price index, which holds apartment sale and rent medians only and no quartiles. Missing months are absent, never interpolated.",
        "city_quarter_prices_latest counts a listing only when its precise location lies inside exactly one official Ville de Luxembourg quarter polygon.",
        "Commune codes are the official 4-digit codes from the national administrative boundaries; they equal the data.public.lu zones lu:commune:NNNN.",
        "Communes whose listing locations do not meet the publication-quality bar keep their row with asking_price_withheld = true and empty asking values."
      ],
      "limitations": [
        "Asking prices are what sellers and landlords advertise. They are not transaction prices and can differ from the price finally agreed.",
        "Coverage follows the listings ChatHome collects from estate-agency websites. Private sales and homes not advertised there are missing, so counts are not the size of the market.",
        "A change in a median between months mixes price movement with a change in the homes on offer; it is not a quality-adjusted price index.",
        "Small communes often have fewer than five comparable listings, so many cells are empty.",
        "The monthly history starts in July 2026 and has gaps; months without an archived snapshot carry apartment medians only.",
        "The figures are not comparable one-to-one with the Observatoire de l'Habitat or STATEC series, which use other statistics, windows and sources."
      ],
      "attribution": [
        "ChatHome (https://chathome.lu), licensed under CC BY 4.0.",
        "Luxembourg City quarter boundaries, names and ids: Ville de Luxembourg, CC BY 4.0 (data.public.lu).",
        "Official columns: Observatoire de l'Habitat, via data.public.lu (CC0 1.0; credited as a courtesy)."
      ],
      "sources": [
        {
          "name": "ChatHome listing catalogue: active residential listings collected from estate-agency websites",
          "url": "https://chathome.lu"
        },
        {
          "name": "Observatoire de l'Habitat: Loyers annoncés des logements par commune",
          "url": "https://data.public.lu/fr/datasets/loyers-annonces-des-logements-par-commune/",
          "licence": "CC0 1.0"
        },
        {
          "name": "Observatoire de l'Habitat: Prix de vente des appartements par commune",
          "url": "https://data.public.lu/fr/datasets/prix-de-vente-des-appartements-par-commune/",
          "licence": "CC0 1.0"
        },
        {
          "name": "Administration du cadastre et de la topographie / Geoportail Luxembourg: Limites administratives du Grand-Duché de Luxembourg (official commune codes)",
          "url": "https://data.public.lu/fr/datasets/limites-administratives-du-grand-duche-de-luxembourg/",
          "licence": "CC0 1.0"
        },
        {
          "name": "Ville de Luxembourg: official quarter boundaries, names and ids (open data)",
          "url": "https://maps.vdl.lu/arcgis/rest/services/OPENDATA/GEOJSON/FeatureServer/24/query?where=1%3D1&outFields=*&outSR=4326&f=geojson",
          "licence": "CC BY 4.0"
        }
      ],
      "updateCadence": "Released by hand, normally once a month, after the weekly commune statistics run. Each release states its date.",
      "catalogEntries": [],
      "doi": null,
      "totalRows": 858,
      "tables": [
        {
          "name": "commune_prices_latest",
          "title": "Commune asking-price snapshot",
          "description": "Latest sale and rental asking-price statistics per Luxembourg commune, computed from active, catalog-visible residential listings, next to labelled official reference values from the Observatoire de l'Habitat. One row per commune, including communes with no publishable sample, so absence is explicit rather than silent. Medians and quartiles are published only for cells with at least 5 listings.",
          "grain": "one row per Luxembourg commune, latest computation",
          "source": "ChatHome active-listing corpus (asking columns); official_* columns: Observatoire de l'Habitat — Ministère du Logement, via data.public.lu.",
          "rowCount": 100,
          "columns": [
            {
              "name": "commune_code",
              "type": "string",
              "description": "Official 4-digit Luxembourg commune code, e.g. 0304 for Luxembourg City; joins to commune_registry.commune_code."
            },
            {
              "name": "commune_slug",
              "type": "string",
              "description": "Stable ChatHome commune identifier; joins to commune_registry.commune_slug."
            },
            {
              "name": "commune_name_en",
              "type": "string",
              "description": "Commune name in English."
            },
            {
              "name": "canton",
              "type": "string",
              "description": "Luxembourg canton, as a lowercase key, e.g. esch."
            },
            {
              "name": "computed_at",
              "type": "string",
              "description": "ISO 8601 timestamp of the computation."
            },
            {
              "name": "active_listings_total",
              "type": "int",
              "description": "Active, catalog-visible residential listings observed in the commune. Null when asking statistics are withheld."
            },
            {
              "name": "sale_apartment_sample",
              "type": "int",
              "description": "Listings behind the sale-apartment statistics."
            },
            {
              "name": "sale_apartment_median_eur_m2",
              "type": "float",
              "description": "Median asking price per m² for apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_apartment_p25_eur_m2",
              "type": "float",
              "description": "25th percentile asking price per m², apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_apartment_p75_eur_m2",
              "type": "float",
              "description": "75th percentile asking price per m², apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_sample",
              "type": "int",
              "description": "Listings behind the sale-house statistics."
            },
            {
              "name": "sale_house_median_eur_m2",
              "type": "float",
              "description": "Median asking price per m² for houses for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_p25_eur_m2",
              "type": "float",
              "description": "25th percentile asking price per m², houses for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_p75_eur_m2",
              "type": "float",
              "description": "75th percentile asking price per m², houses for sale. Null below 5 listings."
            },
            {
              "name": "rent_apartment_sample",
              "type": "int",
              "description": "Listings behind the rental-apartment statistics."
            },
            {
              "name": "rent_apartment_median_eur_m2_month",
              "type": "float",
              "description": "Median asking rent per m² per month, apartments. Null below 5 listings."
            },
            {
              "name": "rent_apartment_p25_eur_m2_month",
              "type": "float",
              "description": "25th percentile asking rent per m² per month, apartments. Null below 5 listings."
            },
            {
              "name": "rent_apartment_p75_eur_m2_month",
              "type": "float",
              "description": "75th percentile asking rent per m² per month, apartments. Null below 5 listings."
            },
            {
              "name": "rent_house_sample",
              "type": "int",
              "description": "Listings behind the rental-house statistics."
            },
            {
              "name": "rent_house_median_eur_m2_month",
              "type": "float",
              "description": "Median asking rent per m² per month, houses. Null below 5 listings."
            },
            {
              "name": "rent_house_p25_eur_m2_month",
              "type": "float",
              "description": "25th percentile asking rent per m² per month, houses. Null below 5 listings."
            },
            {
              "name": "rent_house_p75_eur_m2_month",
              "type": "float",
              "description": "75th percentile asking rent per m² per month, houses. Null below 5 listings."
            },
            {
              "name": "official_quarter",
              "type": "string",
              "description": "Quarter in which the official rolling 12-month window ends, e.g. 2026-Q2. Every official_* value in the row covers this window; it is not a quarterly figure."
            },
            {
              "name": "official_buy_apartment_eur_m2",
              "type": "float",
              "description": "Observatoire de l'Habitat mean registered sale price per m² of existing apartments (deeds recorded by the Publicité Foncière) over the rolling 12-month window named by official_quarter. Published only where the commune has at least 10 deeds. A transaction price, not an asking price — do not substitute one for the other."
            },
            {
              "name": "official_rent_apartment_eur_m2_month",
              "type": "float",
              "description": "Observatoire de l'Habitat mean asking rent per m² per month for apartments, from rental adverts over the rolling 12-month window named by official_quarter. Published only where the commune has at least 30 adverts. An advertised rent, not a contracted rent."
            },
            {
              "name": "official_rent_house_eur_m2_month",
              "type": "float",
              "description": "Observatoire de l'Habitat mean asking rent per m² per month for houses, from rental adverts over the rolling 12-month window named by official_quarter. Published only where the commune has at least 30 adverts. An advertised rent, not a contracted rent."
            },
            {
              "name": "gross_apartment_yield_pct",
              "type": "float",
              "description": "Indicative gross yield: annualised median asking rent over median asking sale price, apartments. Null unless both medians are published."
            },
            {
              "name": "asking_price_withheld",
              "type": "bool",
              "description": "True when ChatHome deliberately withholds this commune's asking statistics for data-quality reasons; every asking column is then null. The official_* columns may still be populated."
            },
            {
              "name": "minimum_precise_sample",
              "type": "int",
              "description": "Listings a cell needs before its median and quartiles are published."
            },
            {
              "name": "sources",
              "type": "string",
              "description": "Semicolon-separated source labels for the row."
            }
          ],
          "files": {
            "csv": {
              "path": "commune-asking-prices/commune_prices_latest.csv",
              "bytes": 20380,
              "sha256": "1c2896cb96024250e61678fb174b6dde6c7253e4294464550a0270ad560f81ca",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "commune-asking-prices/commune_prices_latest.parquet",
              "bytes": 12022,
              "sha256": "6d5cbb8103f06ac7be05edcf8733db3a881582896315bc079341ff46206eb7fd",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        },
        {
          "name": "commune_prices_monthly",
          "title": "Monthly commune asking prices",
          "description": "Monthly history of asking-price statistics per Luxembourg commune and segment, in long format. A month comes from ChatHome's own release snapshot of the commune statistics when one exists (all four segments, with quartiles), otherwise from the immutable monthly ChatHome price-index issue (apartment sale and rent medians only). A month with neither is absent, never interpolated. Medians and quartiles are published only for cells with at least 5 listings.",
          "grain": "one row per month per commune per segment",
          "source": "ChatHome commune price statistics (release snapshots) and ChatHome monthly price-index issues, both computed from active, catalog-visible residential listings.",
          "rowCount": 634,
          "columns": [
            {
              "name": "month",
              "type": "string",
              "description": "Calendar month (UTC) of the computation, YYYY-MM."
            },
            {
              "name": "commune_code",
              "type": "string",
              "description": "Official 4-digit Luxembourg commune code; joins to commune_registry.commune_code."
            },
            {
              "name": "commune_slug",
              "type": "string",
              "description": "Stable ChatHome commune identifier; joins to commune_registry.commune_slug."
            },
            {
              "name": "segment",
              "type": "string",
              "description": "sale_apartment, sale_house, rent_apartment or rent_house."
            },
            {
              "name": "unit",
              "type": "string",
              "description": "eur_per_m2 for sale segments; eur_per_m2_per_month for rent segments."
            },
            {
              "name": "sample_size",
              "type": "int",
              "description": "Listings behind the cell."
            },
            {
              "name": "median",
              "type": "float",
              "description": "Median asking price (sale) or asking rent (rent) per m², in the row's unit. Null below 5 listings."
            },
            {
              "name": "p25",
              "type": "float",
              "description": "25th percentile, in the row's unit. Null below 5 listings, and for months from the price index, which stores no quartiles."
            },
            {
              "name": "p75",
              "type": "float",
              "description": "75th percentile, in the row's unit. Null below 5 listings, and for months from the price index, which stores no quartiles."
            },
            {
              "name": "source",
              "type": "string",
              "description": "release_snapshot (a ChatHome release's own snapshot of the commune statistics) or monthly_price_index (the monthly price-index issue)."
            },
            {
              "name": "methodology",
              "type": "string",
              "description": "Computation behind the row: commune-price-stats for release snapshots, price-index-<version> for price-index issues."
            },
            {
              "name": "computed_at",
              "type": "string",
              "description": "ISO 8601 timestamp of the commune statistics computation the row comes from."
            }
          ],
          "files": {
            "csv": {
              "path": "commune-asking-prices/commune_prices_monthly.csv",
              "bytes": 79180,
              "sha256": "87f3dd333790cae8dd15069ea92665782649a1de0c49b7517a281717eaaa6f09",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "commune-asking-prices/commune_prices_monthly.parquet",
              "bytes": 9009,
              "sha256": "ae53aa304833c8387d81236dc6b3e8423524e31e36b2532e6d786a24d0100ce0",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        },
        {
          "name": "city_quarter_prices_latest",
          "title": "Luxembourg City quarter asking-price snapshot",
          "description": "Latest sale and rental asking-price statistics for the 24 official quarters of Luxembourg City, computed from active, catalog-visible residential listings. A listing counts only when it has a precise point inside exactly one official quarter polygon, so the totals are lower than the commune's in commune_prices_latest. One row per official quarter, including quarters with no publishable sample. Medians and quartiles are published only for cells with at least 5 listings.",
          "grain": "one row per official Luxembourg City quarter, latest computation",
          "source": "ChatHome active-listing corpus; quarter names, identifiers and boundaries from the Ville de Luxembourg open-data district layer.",
          "rowCount": 24,
          "columns": [
            {
              "name": "quarter_slug",
              "type": "string",
              "description": "Stable ChatHome quarter identifier, e.g. belair."
            },
            {
              "name": "quarter_name",
              "type": "string",
              "description": "Official quarter name as in the Ville de Luxembourg district layer (FK_QUART_NAME, French)."
            },
            {
              "name": "vdl_object_id",
              "type": "int",
              "description": "Feature id of the quarter in the Ville de Luxembourg open-data district layer."
            },
            {
              "name": "commune_code",
              "type": "string",
              "description": "Official 4-digit code of the commune the quarter belongs to (0304, Luxembourg City)."
            },
            {
              "name": "computed_at",
              "type": "string",
              "description": "ISO 8601 timestamp of the computation. Null when the quarter has no computed statistics."
            },
            {
              "name": "active_listings_total",
              "type": "int",
              "description": "Active, catalog-visible residential listings with a precise point inside the quarter."
            },
            {
              "name": "sale_apartment_sample_size",
              "type": "int",
              "description": "Listings behind the statistics for apartments for sale."
            },
            {
              "name": "sale_apartment_median",
              "type": "float",
              "description": "Median asking price per m², apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_apartment_p25",
              "type": "float",
              "description": "25th percentile asking price per m², apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_apartment_p75",
              "type": "float",
              "description": "75th percentile asking price per m², apartments for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_sample_size",
              "type": "int",
              "description": "Listings behind the statistics for houses for sale."
            },
            {
              "name": "sale_house_median",
              "type": "float",
              "description": "Median asking price per m², houses for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_p25",
              "type": "float",
              "description": "25th percentile asking price per m², houses for sale. Null below 5 listings."
            },
            {
              "name": "sale_house_p75",
              "type": "float",
              "description": "75th percentile asking price per m², houses for sale. Null below 5 listings."
            },
            {
              "name": "rent_apartment_sample_size",
              "type": "int",
              "description": "Listings behind the statistics for apartments for rent."
            },
            {
              "name": "rent_apartment_median",
              "type": "float",
              "description": "Median asking rent per m² per month, apartments for rent. Null below 5 listings."
            },
            {
              "name": "rent_apartment_p25",
              "type": "float",
              "description": "25th percentile asking rent per m² per month, apartments for rent. Null below 5 listings."
            },
            {
              "name": "rent_apartment_p75",
              "type": "float",
              "description": "75th percentile asking rent per m² per month, apartments for rent. Null below 5 listings."
            },
            {
              "name": "rent_house_sample_size",
              "type": "int",
              "description": "Listings behind the statistics for houses for rent."
            },
            {
              "name": "rent_house_median",
              "type": "float",
              "description": "Median asking rent per m² per month, houses for rent. Null below 5 listings."
            },
            {
              "name": "rent_house_p25",
              "type": "float",
              "description": "25th percentile asking rent per m² per month, houses for rent. Null below 5 listings."
            },
            {
              "name": "rent_house_p75",
              "type": "float",
              "description": "75th percentile asking rent per m² per month, houses for rent. Null below 5 listings."
            }
          ],
          "files": {
            "csv": {
              "path": "commune-asking-prices/city_quarter_prices_latest.csv",
              "bytes": 2731,
              "sha256": "cc28f744e6c98532a7c0023b49184ac75bfcbd39981506a8f7c400750d3ed22b",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "commune-asking-prices/city_quarter_prices_latest.parquet",
              "bytes": 4669,
              "sha256": "907840391084ede9e2a86acb32223aecd50db7d0bf76c6b400539351bff70587",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        },
        {
          "name": "commune_registry",
          "title": "Luxembourg commune registry",
          "description": "The commune dimension every other table joins to: official commune code, ChatHome identifier, the identifiers used by the Grande Région geography and data.public.lu, names in the five ChatHome locales, and canton. Published so a reuser never has to guess how two tables line up.",
          "grain": "one row per Luxembourg commune",
          "source": "ChatHome commune registry; official commune codes from the Geoportail Luxembourg administrative limits, as used by data.public.lu.",
          "rowCount": 100,
          "columns": [
            {
              "name": "commune_code",
              "type": "string",
              "description": "Official 4-digit Luxembourg commune code, e.g. 0304."
            },
            {
              "name": "commune_slug",
              "type": "string",
              "description": "Stable ChatHome commune identifier."
            },
            {
              "name": "locality_id",
              "type": "string",
              "description": "Grande Région locality identifier, e.g. lu:lau:0304; the key used by the travel-times dataset."
            },
            {
              "name": "datapubliclu_zone",
              "type": "string",
              "description": "data.public.lu spatial zone identifier, e.g. lu:commune:0304."
            },
            {
              "name": "commune_name_en",
              "type": "string",
              "description": "Commune name in English."
            },
            {
              "name": "commune_name_fr",
              "type": "string",
              "description": "Commune name in French."
            },
            {
              "name": "commune_name_de",
              "type": "string",
              "description": "Commune name in German."
            },
            {
              "name": "commune_name_lu",
              "type": "string",
              "description": "Commune name in Luxembourgish."
            },
            {
              "name": "commune_name_pt",
              "type": "string",
              "description": "Commune name in Portuguese."
            },
            {
              "name": "canton",
              "type": "string",
              "description": "Luxembourg canton, as a lowercase key, e.g. esch."
            }
          ],
          "files": {
            "csv": {
              "path": "commune-asking-prices/commune_registry.csv",
              "bytes": 10558,
              "sha256": "9adedfe2a69e66c26b72680067b543def1cd01e846021572a090f9ba4e6090ca",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "commune-asking-prices/commune_registry.parquet",
              "bytes": 9398,
              "sha256": "cd0fead15c2ecf857a3eacceb4ee8e6a2a3245d0ee2f8d19c238f2de0b9e55c9",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        }
      ],
      "documents": [
        {
          "path": "commune-asking-prices/README.md",
          "bytes": 24126,
          "sha256": "c8dad5182077d2ad4bf4ec0f58ed6813e05ccecb24ea42c2dad58f9336aedf83",
          "mediaType": "text/markdown"
        },
        {
          "path": "commune-asking-prices/datapackage.json",
          "bytes": 24095,
          "sha256": "0509b5ad0cd484725c5ce7da56b053682ea565836380b9e65b296a17871794ae",
          "mediaType": "application/json"
        }
      ],
      "notes": [
        "No snapshot or price-index issue exists for 2026-09; that month is absent, not interpolated.",
        "In commune_prices_monthly, the months 2026-07 and 2026-08 come from the monthly price-index issues: apartment sale and rent medians only, without quartiles, for communes with active listings; house segments are absent for those months.",
        "In commune_prices_monthly, the month 2026-10 comes from ChatHome release snapshots of all four segments, with quartiles.",
        "Medians and quartiles are published only for cells with at least 5 listings; smaller cells keep their sample size and carry null statistics.",
        "Asking statistics for Steinsel are withheld for data-quality reasons: the commune_prices_latest row is kept with null asking values and asking_price_withheld = true, and commune_prices_monthly has no rows for it.",
        "Sale-apartment medians are published for 81 of 100 communes.",
        "Sale-house medians are published for 97 of 100 communes.",
        "Rent-apartment medians are published for 24 of 100 communes.",
        "Rent-house medians are published for 3 of 100 communes.",
        "official_buy_apartment_eur_m2 (official mean registered apartment price) is filled for 51 of 100 communes.",
        "official_rent_apartment_eur_m2_month (official mean asking rent, apartments) is empty for every commune in this release.",
        "official_rent_house_eur_m2_month (official mean asking rent, houses) is empty for every commune in this release.",
        "Luxembourg City quarter statistics were computed on 2026-10-01 and count 1,135 listings with a precise point inside exactly one quarter; the commune row in commune_prices_latest (computed on 2026-10-14) counts 2,371."
      ]
    },
    {
      "slug": "energy-class-asking-prices",
      "title": "Advertised homes and asking prices by energy class, Luxembourg and border regions",
      "description": "How the homes advertised for sale and for rent in Luxembourg and in the neighbouring areas of France, Belgium (Wallonia) and Germany are distributed across energy-performance classes, and what they ask per square metre in each class. Each country keeps its own certificate scheme and headline indicator: the Luxembourg CPE primary-energy class (A+ to I), the French DPE energy-consumption class (A to G), the Walloon PEB class (A++ to G) and the German Energieausweis class (A+ to H). Letters are never pooled or converted across schemes. A listing whose class is missing, on another axis or outside its scheme's scale counts as not stated, and not-stated rows are published so the gap stays visible. The source is a one-day snapshot of the public ChatHome catalogue of active listings, which ChatHome collects from estate-agency websites. Counts and shares need at least 5 listings, because smaller counts are unreliable; the adverts are public and this is not a confidentiality guarantee, as a hidden count can sometimes be worked out from the shares. Median, 25th and 75th percentile asking prices per m² and new-build shares need at least 10. A second table gives the class mix by Luxembourg canton. The shares describe advertised stock on the snapshot date. They are not the national certificate registers and not the housing stock.",
      "licence": {
        "id": "CC-BY-4.0",
        "name": "Creative Commons Attribution 4.0 International",
        "url": "https://creativecommons.org/licenses/by/4.0/"
      },
      "pageUrl": "https://chathome.lu/en/data/energy-class-asking-prices",
      "temporalCoverage": "2026-10-18",
      "spatialCoverage": [
        {
          "name": "Luxembourg",
          "identifier": "LU"
        },
        {
          "name": "France (areas near Luxembourg)",
          "identifier": "FR"
        },
        {
          "name": "Belgium (Wallonia, areas near Luxembourg)",
          "identifier": "BE"
        },
        {
          "name": "Germany (areas near Luxembourg)",
          "identifier": "DE"
        }
      ],
      "keywords": [
        "Luxembourg",
        "Greater Region",
        "energy performance certificate",
        "energy class",
        "CPE",
        "DPE",
        "PEB",
        "Energieausweis",
        "asking prices",
        "housing",
        "open data"
      ],
      "measurementTechnique": "Counts and shares of active advertised listings by stated energy-performance class, and median, 25th and 75th percentile asking price per square metre of living area within each class.",
      "methodology": [
        "Listing counts and shares are published when at least 5 listings support them; below that the cell shows no count or share and suppressed = true, because such small counts are unreliable. The underlying adverts are public, and this rule is not a confidentiality guarantee: a hidden count can sometimes be worked out from the shares of the other cells in its group.",
        "Median, 25th and 75th percentile prices need at least 10 listings inside the price bands; the new-build share needs at least 10 listings.",
        "Snapshot of the public ChatHome catalogue on the snapshot date: listings that are active and catalogue-eligible, restricted to records produced by the current listing pipeline.",
        "Headline class per country: Luxembourg CPE primary_energy, France DPE energy_consumption, Wallonia PEB overall_performance, Germany Energieausweis overall_performance. Any other axis, an empty class or a letter outside the scheme's scale counts as not_stated; nothing is coerced.",
        "class_rank orders classes within their own scheme only (0 = best); there is no crosswalk between countries.",
        "Apartments and studios form the apartment type and houses the house type. Rooms, colocations, multi-unit buildings and land are excluded.",
        "Price per m² is the asking price (or monthly rent) divided by living area. Listings with living area outside 10 to 2,000 m², sale prices outside EUR 500 to 40,000 per m² or rents outside EUR 3 to 120 per m² per month are left out of the price statistics but still count in the class shares.",
        "A listing counts as new build when it is marked new build, under construction or planned, or belongs to a multi-unit development.",
        "canton_energy_class_mix assigns Luxembourg listings to the canton of their commune through the official commune code."
      ],
      "limitations": [
        "Shares describe homes advertised on the snapshot date, not the housing stock and not the national certificate registers.",
        "Many adverts state no class; the not_stated rows show how many.",
        "Letters are not comparable across countries: each scheme has its own scale and its own thresholds.",
        "A price difference between classes mixes energy performance with age, size and location: new builds can make up much of the best classes, and each release's notes give the measured new-build share of Luxembourg class A.",
        "Coverage outside Luxembourg is limited to the areas near the border where ChatHome collects listings.",
        "Asking prices are not transaction prices."
      ],
      "attribution": [
        "ChatHome (https://chathome.lu), licensed under CC BY 4.0."
      ],
      "sources": [
        {
          "name": "ChatHome public listing catalogue: active residential listings collected from estate-agency websites",
          "url": "https://chathome.lu"
        },
        {
          "name": "Administration du cadastre et de la topographie / Geoportail Luxembourg: Limites administratives du Grand-Duché de Luxembourg (official commune codes)",
          "url": "https://data.public.lu/fr/datasets/limites-administratives-du-grand-duche-de-luxembourg/",
          "licence": "CC0 1.0"
        }
      ],
      "updateCadence": "Released by hand, normally once a month, after the weekly commune statistics run. Each release states its date.",
      "catalogEntries": [],
      "doi": null,
      "totalRows": 420,
      "tables": [
        {
          "name": "energy_class_asking_prices",
          "title": "Advertised homes and asking prices by energy class",
          "description": "Apartments (studios included) and houses advertised for sale or rent on the snapshot date, by country, tenure, property type and energy-performance class, with each class's share and its asking price per m². The class is the one each country's certificate states on its headline scale: Luxembourg CPE primary energy, French DPE energy consumption, Walloon PEB overall performance, German Energieausweis overall performance. Values on another indicator or off the scheme's scale count as not_stated. Letters are not comparable across countries. Rooms, colocations, multi-unit buildings and land are excluded. Price per m² is the asking price (or monthly base rent) divided by the living area, kept only for areas of 10–2,000 m², sale prices of 500–40,000 EUR/m² and rents of 3–120 EUR/m² a month; listings outside these bands still count in class shares, because their class is known, but not in price statistics. Asking prices are not transaction prices. Every class of the scale and not_stated is listed for each country, tenure and type with at least one listing, so absence is explicit.",
          "grain": "one row per country × tenure × property type × energy class (every class of the country's scale, plus not_stated)",
          "source": "ChatHome public listing catalog: active, publicly listed adverts with a V3 card body. Energy classes are the certificate facets read from each advert.",
          "rowCount": 156,
          "columns": [
            {
              "name": "snapshot_date",
              "type": "string",
              "description": "Date the catalog was read (YYYY-MM-DD, UTC)."
            },
            {
              "name": "country",
              "type": "string",
              "description": "ISO country of the home: LU, FR, BE or DE."
            },
            {
              "name": "energy_scheme",
              "type": "string",
              "description": "Certificate scheme: LU_CPE, FR_DPE, BE_WAL_PEB or DE_ENERGIEAUSWEIS."
            },
            {
              "name": "indicator",
              "type": "string",
              "description": "The scheme's headline indicator used for the class: primary_energy (LU), energy_consumption (FR), overall_performance (BE, DE)."
            },
            {
              "name": "tenure",
              "type": "string",
              "description": "sale or rent."
            },
            {
              "name": "property_type",
              "type": "string",
              "description": "apartment (studios included) or house."
            },
            {
              "name": "energy_class",
              "type": "string",
              "description": "Class label on the scheme's own scale, or not_stated when the advert states no class on the headline indicator, states one off the scale, or states two different ones."
            },
            {
              "name": "class_rank",
              "type": "int",
              "description": "Position on the scheme's own scale, 0 = best. Comparable only within one scheme. Null for not_stated."
            },
            {
              "name": "listings",
              "type": "int",
              "description": "Listings in this cell. Null when fewer than 5, because such small counts are unreliable (the row is then suppressed)."
            },
            {
              "name": "share_of_listings_pct",
              "type": "float",
              "description": "Share of this class among all listings of the same country, tenure and property type, not_stated included, in percent (1 decimal). Null when suppressed."
            },
            {
              "name": "share_of_stated_pct",
              "type": "float",
              "description": "Share of this class among the listings of the same country, tenure and property type that state a class, in percent (1 decimal). Null for not_stated and when suppressed."
            },
            {
              "name": "new_build_share_pct",
              "type": "float",
              "description": "Share of the cell's listings that are new builds, under construction, planned, or units of a multi-unit project, in percent (1 decimal). Null when fewer than 10 listings."
            },
            {
              "name": "price_listings",
              "type": "int",
              "description": "Listings of the cell with a price per m² inside the plausibility bands. Null when fewer than 10."
            },
            {
              "name": "unit",
              "type": "string",
              "description": "Unit of median, p25 and p75: eur_per_m2 (sale) or eur_per_m2_per_month (rent)."
            },
            {
              "name": "median",
              "type": "float",
              "description": "Median asking price per m² of the price listings. Sale rounded to whole euros, rent to 0.1 euro. Null unless price_listings is at least 10."
            },
            {
              "name": "p25",
              "type": "float",
              "description": "25th percentile of asking price per m² (linear interpolation). Null unless price_listings is at least 10."
            },
            {
              "name": "p75",
              "type": "float",
              "description": "75th percentile of asking price per m² (linear interpolation). Null unless price_listings is at least 10."
            },
            {
              "name": "suppressed",
              "type": "bool",
              "description": "True when the cell has fewer than 5 listings: its counts, shares and prices are then null, because such small counts are unreliable. The adverts are public and this is not a confidentiality guarantee: a hidden count can sometimes be worked out from the shares."
            }
          ],
          "files": {
            "csv": {
              "path": "energy-class-asking-prices/energy_class_asking_prices.csv",
              "bytes": 16486,
              "sha256": "c4ed99a3f8731f66cfb14545659180a7a4b56d26605bbb625a0d96a7c887cd43",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "energy-class-asking-prices/energy_class_asking_prices.parquet",
              "bytes": 5990,
              "sha256": "ff806a668d790ba800e2ce39cc550f42295ba6847098f5c26deb6ad9adcbbe4d",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        },
        {
          "name": "canton_energy_class_mix",
          "title": "Energy-class mix of advertised homes by Luxembourg canton",
          "description": "Share of each Luxembourg CPE primary-energy class among the apartments (studios included) and houses advertised for sale or rent on the snapshot date, per canton. The canton comes from the listing's official commune (STATEC locality id). Rooms, colocations, multi-unit buildings and land are excluded. Shares describe advertised stock, not the housing stock. Every class of the scale and not_stated is listed for each canton and tenure with at least one listing.",
          "grain": "one row per Luxembourg canton × tenure × energy class (every LU CPE class, plus not_stated)",
          "source": "ChatHome public listing catalog: active, publicly listed adverts with a V3 card body. Energy classes are the certificate facets read from each advert.",
          "rowCount": 264,
          "columns": [
            {
              "name": "snapshot_date",
              "type": "string",
              "description": "Date the catalog was read (YYYY-MM-DD, UTC)."
            },
            {
              "name": "canton",
              "type": "string",
              "description": "Canton key of the ChatHome commune registry, e.g. esch (joins commune_registry.canton)."
            },
            {
              "name": "canton_name",
              "type": "string",
              "description": "Official canton name, e.g. Esch-sur-Alzette."
            },
            {
              "name": "tenure",
              "type": "string",
              "description": "sale or rent."
            },
            {
              "name": "energy_class",
              "type": "string",
              "description": "LU CPE primary-energy class (A+ to I), or not_stated when the advert states none on that indicator, one off the scale, or two different ones."
            },
            {
              "name": "listings",
              "type": "int",
              "description": "Listings in this cell. Null when fewer than 5, because such small counts are unreliable (the row is then suppressed)."
            },
            {
              "name": "share_of_listings_pct",
              "type": "float",
              "description": "Share of this class among all listings of the canton and tenure, not_stated included, in percent (1 decimal). Null when suppressed."
            },
            {
              "name": "suppressed",
              "type": "bool",
              "description": "True when the cell has fewer than 5 listings: its count and share are then null, because such small counts are unreliable. The adverts are public and this is not a confidentiality guarantee: a hidden count can sometimes be worked out from the shares."
            }
          ],
          "files": {
            "csv": {
              "path": "energy-class-asking-prices/canton_energy_class_mix.csv",
              "bytes": 12564,
              "sha256": "33f6cf08fec67bf3a24c1c70eba116ed6ed2b784b958d157fcf7651d6cbe8e97",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "energy-class-asking-prices/canton_energy_class_mix.parquet",
              "bytes": 2681,
              "sha256": "71eb319ad5880f72c9b5c9d9b0c3369994f550daab3459d1a1cc4100fef6e3f7",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        }
      ],
      "documents": [
        {
          "path": "energy-class-asking-prices/README.md",
          "bytes": 15864,
          "sha256": "a3a121073e6992275425c0604209254f6c5bc3c5aea71295663b22bb9ade35d2",
          "mediaType": "text/markdown"
        },
        {
          "path": "energy-class-asking-prices/datapackage.json",
          "bytes": 12331,
          "sha256": "3e5c72171cb782822f0129cab6ab025da50befb8acb2060eae24145ebbab34af",
          "mediaType": "application/json"
        }
      ],
      "notes": [
        "Snapshot of 2026-10-18: 12,608 apartments and houses were classified. Listings stating a class on their country's headline scale: Luxembourg 6,149 of 9,888, France 1,002 of 1,704, Belgium 351 of 503, Germany 246 of 513.",
        "42.6% of the 2,868 Luxembourg class-A listings (LU CPE primary energy) are new builds, under construction, planned or part of a multi-unit project.",
        "Shares describe advertised stock, the homes listed on ChatHome on the snapshot date, not the housing stock.",
        "Energy-class letters are not comparable across countries: each country's certificate has its own scale and indicator and no crosswalk exists, so classes are never pooled or ranked across schemes.",
        "1,167 of the 12,608 classified listings have no price per m² inside the plausibility bands (price or area missing, or outside the bands); they count in class shares but not in price statistics.",
        "723 of the public listings have an older card body and were left out, because their energy scheme was assumed from the country rather than read from a certificate."
      ]
    },
    {
      "slug": "travel-times",
      "title": "Modelled travel times from Luxembourg and cross-border communes to six Luxembourg job hubs",
      "description": "Modelled door-to-door travel times from every Luxembourg commune and from the French and German communes where at least 100 residents work in Luxembourg (IGSS counts, at most 400 communes) to six Luxembourg destinations: Luxembourg station, Kirchberg, Cloche d'Or, the city centre, Belval and Findel airport. Each row gives one origin, destination and weekday scenario: driving off-peak and at the morning and evening peaks, public transport in the morning and evening peaks, walking and cycling. Road, walking and cycling times come from the open-source Valhalla router on OpenStreetMap; public-transport times come from the R5 router on official GTFS timetables, with the 80th percentile published next to the median. Peak driving times are modelled with a national congestion ratio, not measured traffic. A median over 120 minutes is reported as no journey found, never as zero.Every row states when its timetable or road graph stops being valid. Belgian communes are not included while the licence of their centroid source is unresolved. Because the times are derived from OpenStreetMap, the dataset is published under the ODbL.",
      "licence": {
        "id": "ODbL-1.0",
        "name": "Open Data Commons Open Database License 1.0",
        "url": "https://opendatacommons.org/licenses/odbl/1-0/"
      },
      "pageUrl": "https://chathome.lu/en/data/travel-times",
      "temporalCoverage": "2026-09-26/2026-12-07",
      "spatialCoverage": [
        {
          "name": "Luxembourg (100 communes)",
          "identifier": "LU"
        },
        {
          "name": "France (cross-border commuter communes)",
          "identifier": "FR"
        },
        {
          "name": "Germany (cross-border commuter communes)",
          "identifier": "DE"
        }
      ],
      "keywords": [
        "Luxembourg",
        "Greater Region",
        "travel time",
        "commuting",
        "cross-border workers",
        "frontaliers",
        "public transport",
        "GTFS",
        "OpenStreetMap",
        "accessibility",
        "open data"
      ],
      "measurementTechnique": "Door-to-door travel time computed with the Valhalla (road, walking, cycling) and R5 (public transport) routers from one reference point per commune to six fixed destinations, per weekday time window.",
      "methodology": [
        "No sample threshold applies: each value is a modelled time for one fixed reference point.",
        "A row whose median time is above the 120-minute cutoff has status no_journey_within_cutoff and no minutes. The public-transport 80th percentile is published with its median and can exceed 120 minutes.",
        "The 80th percentile is published for public transport only; for driving it equals the median in the source surface and is left empty.",
        "A release refuses any row whose valid_until date has passed.",
        "Origin: the centre of the H3 resolution-10 cell that contains the commune's official centroid, at most about 75 m from it. Destinations: six fixed hub points.",
        "Driving, walking and cycling: Valhalla on OpenStreetMap extracts. Driving samples three times inside the window and walking and cycling once; minutes are the nearest-rank median of the samples.",
        "Public transport: R5 on the official GTFS timetables of Luxembourg and the neighbouring networks, evaluated for every minute of the window. The median and 80th percentile include a per-ride reliability allowance calibrated on mobiliteit.lu journey data.",
        "Peak driving multiplies free-flow time over the first 10 km by a national congestion ratio calibrated on TomTom Traffic Index figures for Luxembourg. It is modelled, not measured traffic.",
        "Public transport: the median and 80th percentile are taken over the window minutes that have a journey within 120 minutes. When fewer than 70% of the window's minutes have one, or the median exceeds 120 minutes, the row is no_journey_within_cutoff. not_routed means the commune's reference cell is not in this routing surface.",
        "centroid_check compares the reference time with the median over other routed cells of the same commune and publishes only the resulting label.",
        "Reference set: all 100 Luxembourg communes plus the French and German communes with at least 100 residents working in Luxembourg, ordered by that count and capped at 400."
      ],
      "limitations": [
        "Times are modelled, not observed journeys; real trips vary with traffic, works, weather and service changes.",
        "Peak driving uses one national congestion ratio, not corridor-level traffic.",
        "Each commune is represented by one point near its centroid; places inside a large commune can have very different times.",
        "Public-transport times are valid only for the timetable period stated in valid_until.",
        "No journey found within 120 minutes is not the same as unreachable.",
        "Belgian communes are not included."
      ],
      "attribution": [
        "© OpenStreetMap contributors. Road, path and stop network from OpenStreetMap (Geofabrik extracts), available under the Open Database License 1.0: https://www.openstreetmap.org/copyright",
        "Luxembourg public-transport timetables: Administration des transports publics, CC BY 4.0, via data.public.lu.",
        "Source : Région Grand Est — réseau Fluo; Source : Région Grand Est (GTFS, Licence Ouverte 2.0, transport.data.gouv.fr).",
        "Datensatz der NVBW GmbH, dl-de/by-2-0 (https://www.govdata.de/dl-de/by-2-0), nvbw.de/open-data.",
        "Walloon bus timetables: OTW (TEC), CC0.",
        "Public-transport reliability calibrated on the mobiliteit.lu HAFAS API (Administration des transports publics, CC BY 4.0).",
        "Peak driving times are modelled: free-flow road time scaled by one national congestion ratio calibrated on TomTom Traffic Index city figures. No TomTom data is redistributed.",
        "Luxembourg commune limits and centroids: Administration du cadastre et de la topographie / Geoportail Luxembourg (CC0).",
        "Source : Insee — Code officiel géographique 2026; commune centres from API Géo (Licence Ouverte 2.0).",
        "© Statistisches Bundesamt (Destatis), 2026 — Gemeindeverzeichnis GV-ISys, dl-de/by-2-0 (https://www.govdata.de/dl-de/by-2-0); changes made: register suffixes dropped from names, centroids rounded to 5 decimals.",
        "Residents working in Luxembourg: IGSS, employment by commune of residence (CC0)."
      ],
      "sources": [
        {
          "name": "OpenStreetMap contributors, via Geofabrik extracts: road, path and stop network",
          "url": "https://www.openstreetmap.org/copyright",
          "licence": "ODbL 1.0"
        },
        {
          "name": "Administration des transports publics (Luxembourg): national public-transport timetables (GTFS), data.public.lu",
          "url": "https://data.public.lu/en/datasets/horaires-et-arrets-des-transport-publics-gtfs/",
          "licence": "CC BY 4.0"
        },
        {
          "name": "Région Grand Est: Fluo 57 interurban timetables (GTFS), transport.data.gouv.fr",
          "url": "https://transport.data.gouv.fr/datasets/offre-du-reseau-de-transport-interurbain-fluo-grand-est",
          "licence": "Licence Ouverte 2.0"
        },
        {
          "name": "Région Grand Est: aggregate regional timetables (GTFS), transport.data.gouv.fr",
          "url": "https://transport.data.gouv.fr/datasets/horaires-des-lignes-regulieres-et-scolaires-du-reseau-fluo-grand-est",
          "licence": "Licence Ouverte 2.0"
        },
        {
          "name": "NVBW GmbH: Baden-Württemberg, Rheinland-Pfalz and Saarland timetables (GTFS)",
          "url": "https://www.nvbw.de/open-data/fahrplandaten",
          "licence": "dl-de/by-2-0 (https://www.govdata.de/dl-de/by-2-0)"
        },
        {
          "name": "OTW (TEC): Walloon bus timetables (GTFS)",
          "url": "https://transportdata.be/dataset/tec-gtfs",
          "licence": "CC0 1.0"
        },
        {
          "name": "mobiliteit.lu HAFAS API (Administration des transports publics): public-transport reliability calibration only",
          "url": "https://data.public.lu/en/datasets/api-mobiliteit-lu/",
          "licence": "CC BY 4.0"
        },
        {
          "name": "TomTom Traffic Index city figures: calibration of one national peak congestion ratio; no TomTom data is redistributed",
          "url": "https://www.tomtom.com/traffic-index/"
        },
        {
          "name": "Administration du cadastre et de la topographie / Geoportail Luxembourg: administrative limits (Luxembourg codes, names, centroids)",
          "url": "https://data.public.lu/fr/datasets/limites-administratives-du-grand-duche-de-luxembourg/",
          "licence": "CC0 1.0"
        },
        {
          "name": "INSEE: Code officiel géographique 2026 (French codes, names), with commune centres from API Géo",
          "url": "https://www.insee.fr/fr/information/8740222",
          "licence": "Licence Ouverte 2.0"
        },
        {
          "name": "Statistisches Bundesamt (Destatis): Gemeindeverzeichnis GV-ISys (German codes, names, centroids)",
          "url": "https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/_inhalt.html",
          "licence": "dl-de/by-2-0 (https://www.govdata.de/dl-de/by-2-0)"
        },
        {
          "name": "IGSS: employment by commune of residence in Luxembourg and the neighbouring countries",
          "url": "https://data.public.lu/en/datasets/emploi-total-par-commune-de-residence-au-luxembourg-et-dans-les-pays-frontaliers/",
          "licence": "CC0 1.0"
        }
      ],
      "updateCadence": "Released when the routing surface is rebuilt; every row states its validity end.",
      "catalogEntries": [],
      "doi": null,
      "totalRows": 18767,
      "tables": [
        {
          "name": "commune_hub_travel_times",
          "title": "Commune-to-hub travel times",
          "description": "Routed weekday travel times between each reference commune (every Luxembourg commune, and the French and German communes with at least 100 residents working in Luxembourg) and six Luxembourg employment hubs, in eight scenarios: driving off-peak and at peak in both directions, public transport in the morning and evening peaks, walking and cycling. Road, walking and cycling times come from a self-hosted Valhalla router on OpenStreetMap; public-transport times from a self-hosted R5 router on official GTFS timetables. Where no journey within the cutoff was found the status says so and the minutes are empty, never zero.",
          "grain": "one row per reference commune × hub × scenario",
          "source": "ChatHome commute surface: self-hosted Valhalla (OpenStreetMap) and R5 (OpenStreetMap + official GTFS) routing.",
          "rowCount": 18384,
          "columns": [
            {
              "name": "locality_id",
              "type": "string",
              "description": "Grande Région locality id: lu:lau:<code>, fr:insee:<code> or de:ags:<code>."
            },
            {
              "name": "country",
              "type": "string",
              "description": "ISO 3166-1 alpha-2 country of the commune: LU, FR or DE."
            },
            {
              "name": "national_code",
              "type": "string",
              "description": "Official municipality code: STATEC 4-digit, INSEE 5-digit or Destatis 8-digit AGS."
            },
            {
              "name": "hub_id",
              "type": "string",
              "description": "Hub identifier, e.g. lux-gare or kirchberg."
            },
            {
              "name": "hub_name",
              "type": "string",
              "description": "Hub name in English."
            },
            {
              "name": "scenario_id",
              "type": "string",
              "description": "Scenario identifier, e.g. transit-weekday-am-to-target."
            },
            {
              "name": "mode",
              "type": "string",
              "description": "driving, transit, walking or cycling."
            },
            {
              "name": "direction",
              "type": "string",
              "description": "to_target (commune to hub) or from_target (hub to commune)."
            },
            {
              "name": "time_basis",
              "type": "string",
              "description": "depart_at, arrive_by, or static for time-invariant walking and cycling."
            },
            {
              "name": "window_from",
              "type": "string",
              "description": "Start of the weekday local-time window (Europe/Luxembourg, HH:MM); empty for static modes."
            },
            {
              "name": "window_to",
              "type": "string",
              "description": "End of the weekday local-time window (Europe/Luxembourg, HH:MM); empty for static modes."
            },
            {
              "name": "status",
              "type": "string",
              "description": "ok; no_journey_within_cutoff: no journey within cutoff_minutes was found (a longer journey, a point the network does not reach, or for public transport too few departures in the window; the surface does not tell these apart); not_routed: the commune's centroid cell is not in the routing surface."
            },
            {
              "name": "minutes_p50",
              "type": "float",
              "description": "Median journey time in minutes, 1 decimal; status ok only, so never above cutoff_minutes. Public transport: over the window minutes that have a journey within cutoff_minutes (at least 70% of them), including a per-ride reliability allowance calibrated on mobiliteit.lu observations."
            },
            {
              "name": "minutes_p80",
              "type": "float",
              "description": "80th-percentile journey time in minutes over the same window minutes as minutes_p50, with the per-ride allowance, 1 decimal; it can exceed cutoff_minutes. Public transport with status ok only, empty for driving, walking and cycling."
            },
            {
              "name": "distance_km",
              "type": "float",
              "description": "Median routed distance in kilometres, 1 decimal; status ok only, empty for public transport."
            },
            {
              "name": "cutoff_minutes",
              "type": "int",
              "description": "Longest journey the surface searched for."
            },
            {
              "name": "centroid_check",
              "type": "string",
              "description": "consistent or differs_over_10_min: minutes_p50 at the commune centroid compared with the median over at least five other routed cells of the same commune in the same scenario; not_checked when fewer exist or the centroid has no journey. A representativeness flag only."
            },
            {
              "name": "peak_traffic_model",
              "type": "string",
              "description": "Peak driving only: 'modelled national congestion ratio (<version>)'. Peak minutes are free-flow road time scaled by one national ratio calibrated on TomTom Traffic Index city figures, not measured traffic. Empty otherwise."
            },
            {
              "name": "provider",
              "type": "string",
              "description": "Routing engine: valhalla (road, walking, cycling) or r5 (public transport)."
            },
            {
              "name": "graph_version",
              "type": "string",
              "description": "OpenStreetMap network version the router used."
            },
            {
              "name": "timetable_version",
              "type": "string",
              "description": "GTFS timetable bundle version; public transport only."
            },
            {
              "name": "reliability_version",
              "type": "string",
              "description": "Version of the public-transport reliability calibration; public transport only."
            },
            {
              "name": "computed_at",
              "type": "string",
              "description": "When this hub and scenario were routed (ISO 8601, UTC)."
            },
            {
              "name": "valid_until",
              "type": "string",
              "description": "End of validity of the routing data behind this row (ISO 8601, UTC); for public transport, the end of the timetable."
            },
            {
              "name": "surface_version",
              "type": "string",
              "description": "Version of the routing surface the row was read from."
            }
          ],
          "files": {
            "csv": {
              "path": "travel-times/commune_hub_travel_times.csv",
              "bytes": 5527583,
              "sha256": "efe497772df68f291ba6586a28ca896128194509b0544ef62afe874aeefc3395",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "travel-times/commune_hub_travel_times.parquet",
              "bytes": 88534,
              "sha256": "3f9babf0b66f4622f8284daa54d8576ae3974b70bbf383535c8d05097010c7d3",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        },
        {
          "name": "travel_time_localities",
          "title": "Travel-time reference communes",
          "description": "The communes travel times are published for: official code, name and administrative area, the official centroid the routing starts from, and for French and German communes the number of residents working in Luxembourg (IGSS) and their straight-line distance to Luxembourg. Belgian communes are not included.",
          "grain": "one row per reference commune",
          "source": "Grande Région geography (Geoportail Luxembourg, INSEE COG and API Géo, Destatis GV-ISys) and IGSS employment by commune of residence.",
          "rowCount": 383,
          "columns": [
            {
              "name": "locality_id",
              "type": "string",
              "description": "Grande Région locality id: lu:lau:<code>, fr:insee:<code> or de:ags:<code>."
            },
            {
              "name": "country",
              "type": "string",
              "description": "ISO 3166-1 alpha-2 country: LU, FR or DE."
            },
            {
              "name": "national_code",
              "type": "string",
              "description": "Official municipality code: STATEC 4-digit, INSEE 5-digit or Destatis 8-digit AGS."
            },
            {
              "name": "national_authority",
              "type": "string",
              "description": "Code authority: STATEC_LAU, INSEE_COG or DESTATIS_AGS."
            },
            {
              "name": "name",
              "type": "string",
              "description": "Official commune name (French for Luxembourg and France); for Germany the plain register name without its administrative suffix such as ', Stadt' where the register gives one."
            },
            {
              "name": "admin_area",
              "type": "string",
              "description": "Luxembourg canton, French department or German Kreis name; empty where the source gives only a code instead of a name."
            },
            {
              "name": "centroid_lat",
              "type": "float",
              "description": "Official commune centroid latitude, WGS84, 5 decimals. Routing starts at the centre of the H3 resolution-10 cell that contains it."
            },
            {
              "name": "centroid_lng",
              "type": "float",
              "description": "Official commune centroid longitude, WGS84, 5 decimals."
            },
            {
              "name": "lu_resident_workers",
              "type": "int",
              "description": "Residents of this commune employed under Luxembourg social security (IGSS, rounded by IGSS to the nearest 10); empty for Luxembourg communes."
            },
            {
              "name": "igss_reference_date",
              "type": "string",
              "description": "Reference date of lu_resident_workers (YYYY-MM-DD); empty for Luxembourg communes."
            },
            {
              "name": "distance_to_luxembourg_km",
              "type": "float",
              "description": "Straight-line kilometres from the centroid to the nearest of five Luxembourg employment centres, as used to define the cross-border belt; empty for Luxembourg communes."
            },
            {
              "name": "routed",
              "type": "bool",
              "description": "True when the commune's centroid cell is in the routing surface; false rows have status not_routed in commune_hub_travel_times."
            }
          ],
          "files": {
            "csv": {
              "path": "travel-times/travel_time_localities.csv",
              "bytes": 34319,
              "sha256": "d587019fe3d94762b0f78da3ef66dec67cbf6a7ef073d69895e33cd014cfbffd",
              "mediaType": "text/csv"
            },
            "parquet": {
              "path": "travel-times/travel_time_localities.parquet",
              "bytes": 16785,
              "sha256": "effc2d36abc652b2e0d1cbbb5ebe869a324dd1127793f83f2bacf93d702afabf",
              "mediaType": "application/vnd.apache.parquet"
            }
          }
        }
      ],
      "documents": [
        {
          "path": "travel-times/README.md",
          "bytes": 19753,
          "sha256": "e932cb663e79bcbc560cee85ee2e787614d4ec648182b19c395cee39d6a3e238",
          "mediaType": "text/markdown"
        },
        {
          "path": "travel-times/datapackage.json",
          "bytes": 15908,
          "sha256": "1ac8596c3bc4516f674118aaf702014edbe40a3b916a8a181163528378c57df9",
          "mediaType": "application/json"
        },
        {
          "path": "travel-times/ATTRIBUTION.md",
          "bytes": 4649,
          "sha256": "750f8562e6a387dd14ac236871923a5957a3f11d8569e6a6268e1af850112913",
          "mediaType": "text/markdown"
        }
      ],
      "notes": [
        "Travel times are published for 383 reference communes: 100 in Luxembourg, 189 in France and 94 in Germany (every Luxembourg commune, and the cross-border communes with at least 100 residents working in Luxembourg according to IGSS on 2026-03-31, highest counts first, at most 400).",
        "Belgian communes are not published: their reference points are derived from Walloon boundary polygons whose reuse licence is unresolved.",
        "1 reference commune is not in the routing surface (fr:insee:54493); their rows have status not_routed.",
        "The 18384 rows cover 48 hub and scenario surfaces: 12513 ok, 5823 with no journey within 120 minutes found, 48 not routed.",
        "Routing starts at the centre of the H3 resolution-10 cell that contains each official centroid, at most 74 m from it.",
        "centroid_check: 3770 rows consistent, 672 differing by more than 10 minutes from the median of at least 5 other routed cells of the same commune, 13942 not checked.",
        "Peak driving times are modelled with a national congestion ratio (tomtom-luxembourg-2025-v1); they are not measured traffic.",
        "Rows are valid until: cycling 2027-09-26, driving 2027-09-26, transit 2026-12-07, walking 2027-09-26.",
        "The routing surface commute-v2-20260926224804988 expires on 2026-12-07, when its transit rows stop being valid; the expiring routing data (for public transport, the timetable) and the surface must be rebuilt by hand before then, or the next release refuses the surface."
      ]
    }
  ],
  "corrections": [
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "commune_rent_price_quarterly is withdrawn. Release 2026.08 labelled it as Observatoire de l'Habitat advertised rents, but it came from ChatHome's retired rental_price_index: it carried size brackets, medians and quartiles that the Observatoire does not publish, and 4,428 of its 9,470 rows had fewer than 5 observations."
    },
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "commune_sale_price_quarterly and commune_asking_price_annual are no longer published. They copied official Observatoire de l'Habitat series that could not be rebuilt reproducibly: the history is no longer stored and the stored commune codes were not the official ones. Use the originals on data.public.lu (CC0)."
    },
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "frontalier_belt_communes (2,321 French communes) and cross_border_source_status are withdrawn. travel_time_localities in the travel-times dataset covers only the French and German travel-time reference communes, with official codes, names and IGSS cross-border worker counts, and carries no population or postal codes."
    },
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "Release 2026.08 published 25th and 75th percentiles in commune_prices_latest for 57 cells (in 50 communes) with only 2 to 4 observations. Quartiles are now empty below 5 observations."
    },
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "commune_name_lu in commune_registry was empty in release 2026.08. It is now filled."
    },
    {
      "date": "2026-10-18",
      "release": "2026.10",
      "text": "Release 2026.08 said the data was regenerated monthly. No later release was produced before this one; releases are now run by hand and each one states its date."
    }
  ]
}
