barcelona-rent.fyiDevelopers

Endpoints

Three of them. All GET, all JSON, all requiring a key.

GET/api/data/latest.json

Current quarter for every district and neighbourhood — 83 areas, a few kilobytes. This is the one to poll if you want a number on a dashboard.

GET/api/data/latest.json
json
{
  "name": "Barcelona rent and sale prices … — current quarter",
  "publisher": {"name": "...", "url": "..."},
  "source": {"rent": "...", "sale": "..."},
  "license": "https://www.barcelona-rent.fyi/data/licence",
  "periods": {
    "neighbourhoodRent": "2026-Q1", "neighbourhoodSale": "2026-Q1",
    "districtRent": "2026-Q1",      "districtSale": "2026-Q1"
  },
  "units": {
    "rentEurPerSqmMonth": "euros per square metre per month",
    "salePriceEurPerSqm": "euros per square metre of built area",
    "grossYieldPct": "annual rent as a percentage of purchase price"
  },
  "areas": [
    {
      "type": "district",
      "code": 1,
      "name": "Ciutat Vella",
      "url": "https://www.barcelona-rent.fyi/district/ciutat-vella",
      "rentEurPerSqmMonth": 18.41,
      "salePriceEurPerSqm": 4647.32,
      "grossYieldPct": 4.75
    }
  ]
}
FieldTypeDescription
areas[].typestringEither "district" or "neighbourhood". 10 of the first, 73 of the second.
areas[].codeintegerThe official Barcelona statistical code. District 1–10; neighbourhood 1–73. Codes are stable across quarters, names are the thing that changes.
areas[].rentEurPerSqmMonthnumber | nullNull where the quarter has too few registered contracts to publish.
areas[].salePriceEurPerSqmnumber | nullSame, for registered sales.
areas[].grossYieldPctnumber | nullOmitted rather than guessed when rent and sale describe different quarters — check `periods` before comparing across the two.
GET/api/data/history.json

Every observation behind the site, back to 2000 for district rent. Around a megabyte. Fetch it once and keep it — it changes quarterly.

GET/api/data/history.json

About a megabyte — the response below is truncated for display.

Observations are positional tuples rather than objects: repeating six key names across nine thousand rows would triple the payload for nothing. The columns key tells you the order.

json
{
  "columns": {
    "neighbourhoodSeries": ["neighbourhoodCode", "year", "quarter", "value"],
    "districtSeries":      ["districtCode", "year", "quarter", "value"],
    "citySeries":          ["year", "quarter", "value"]
  },
  "coverage": {
    "neighbourhoodRent": {"from": "2014-Q1", "to": "2026-Q1", "quarters": 49,  "rows": 3347},
    "districtRent":      {"from": "2000-Q1", "to": "2026-Q1", "quarters": 105, "rows": 1050},
    "cityRent":          {"from": "2000-Q1", "to": "2026-Q1", "quarters": 105, "rows": 105}
  },
  "neighbourhoods": [{"code": 1, "name": "el Raval", "districtCode": 1, "url": "..."}],
  "districts":      [{"code": 1, "name": "Ciutat Vella", "url": "..."}],
  "series": {
    "neighbourhoodRent": [[71, 2014, 1, 9.54]],
    "districtRent":      [[1, 2000, 1, 5]],
    "cityRent":          [[2000, 1, 5.73]]
  }
}
FieldTypeDescription
series.neighbourhoodRentarray[code, year, quarter, €/m²/month]. 2014 Q1 onward — before that the Generalitat published districts only.
series.districtRentarray[code, year, quarter, €/m²/month]. 2000 Q1 onward, the longest series here.
series.cityRentarray[year, quarter, €/m²/month]. Barcelona as a whole — the row the Generalitat publishes, which is not the mean of the districts.
series.neighbourhoodSalearray[code, year, quarter, €/m² built]. 2013 Q4 onward.
series.districtSalearray[code, year, quarter, €/m² built]. 2013 Q4 onward.
series.citySalearray[year, quarter, €/m² built]. 2013 Q4 onward.
GET/api/keys/status

What your key is and what it has spent today. Does not count against your quota.

GET/api/keys/status
json
{"tier":"free","daily_limit":500,"used_today":1,"remaining":499,"linked_to_account":false}
POST/api/keys

Issues a key. Takes an optional JSON body with label. Three keys per address per day; the fourth gets a 429.

What there isn’t

No query parameters, no pagination, no filtering by area or date. Two endpoints that each return everything is the whole surface, because the whole dataset fits in a megabyte — filtering it is a line of code on your side and a cache miss on ours. If that stops being true, this page changes before the endpoints do.

NextRate limits