Skip to content

Commit b4adf4f

Browse files
authored
Add availability parameter to search_listing() (#5)
Allow searching for sold listings by adding an availability parameter: - "available" - active listings - "negotiations" - under negotiation - "sold" - sold/rented listings Default remains ["available", "negotiations"] to match current behavior. Example: f.search_listing('amsterdam', availability='sold') Bump version to 2.4.0
1 parent dbc653c commit b4adf4f

3 files changed

Lines changed: 106 additions & 2 deletions

File tree

examples/search_sold.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python3
2+
"""Search for sold listings on Funda.
3+
4+
Find recently sold properties in a given location to analyze
5+
market prices and trends.
6+
7+
Usage:
8+
uv run examples/search_sold.py amsterdam
9+
uv run examples/search_sold.py rotterdam --max-price 500000
10+
uv run examples/search_sold.py utrecht --pages 3
11+
"""
12+
13+
import argparse
14+
15+
from funda import Funda
16+
17+
18+
def main():
19+
parser = argparse.ArgumentParser(description="Search for sold listings")
20+
parser.add_argument("location", help="City or area to search in")
21+
parser.add_argument("--min-price", type=int, help="Minimum price")
22+
parser.add_argument("--max-price", type=int, help="Maximum price")
23+
parser.add_argument("--pages", type=int, default=1, help="Number of pages (15 results each)")
24+
args = parser.parse_args()
25+
26+
with Funda() as f:
27+
print(f"Searching for sold listings in {args.location}...")
28+
print()
29+
30+
all_listings = []
31+
for page in range(args.pages):
32+
results = f.search_listing(
33+
location=args.location,
34+
availability="sold",
35+
price_min=args.min_price,
36+
price_max=args.max_price,
37+
sort="newest",
38+
page=page,
39+
)
40+
all_listings.extend(results)
41+
42+
if len(results) < 15:
43+
break
44+
45+
if not all_listings:
46+
print("No sold listings found.")
47+
return
48+
49+
print(f"Found {len(all_listings)} sold listings:")
50+
print("-" * 70)
51+
print(f"{'Address':<35} {'City':<15} {'Price':>12} {'m²':>6}")
52+
print("-" * 70)
53+
54+
total_price = 0
55+
total_area = 0
56+
count_with_area = 0
57+
58+
for listing in all_listings:
59+
title = listing["title"][:34]
60+
city = (listing["city"] or "")[:14]
61+
price = listing["price"]
62+
area = listing["living_area"]
63+
64+
price_str = f"€{price:,}" if price else "N/A"
65+
area_str = str(area) if area else "-"
66+
67+
print(f"{title:<35} {city:<15} {price_str:>12} {area_str:>6}")
68+
69+
if price:
70+
total_price += price
71+
if area:
72+
total_area += area
73+
count_with_area += 1
74+
75+
print("-" * 70)
76+
77+
# Summary statistics
78+
if all_listings:
79+
avg_price = total_price // len(all_listings)
80+
print(f"\nAverage sold price: €{avg_price:,}")
81+
82+
if count_with_area > 0:
83+
avg_area = total_area // count_with_area
84+
avg_price_m2 = total_price // total_area
85+
print(f"Average living area: {avg_area} m²")
86+
print(f"Average price per m²: €{avg_price_m2:,}")
87+
88+
89+
if __name__ == "__main__":
90+
main()

funda/funda.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ def search_listing(
315315
self,
316316
location: str | list[str] | None = None,
317317
offering_type: str = "buy",
318+
availability: str | list[str] | None = None,
318319
price_min: int | None = None,
319320
price_max: int | None = None,
320321
area_min: int | None = None,
@@ -332,6 +333,8 @@ def search_listing(
332333
Args:
333334
location: City/area name(s) or postcode to search in
334335
offering_type: "buy" or "rent"
336+
availability: Filter by status - "available", "negotiations", "sold",
337+
or a list combining them. Default: ["available", "negotiations"]
335338
price_min: Minimum price
336339
price_max: Maximum price
337340
area_min: Minimum living area in m²
@@ -350,6 +353,7 @@ def search_listing(
350353
351354
Example:
352355
>>> f.search_listing('amsterdam', price_max=500000)
356+
>>> f.search_listing('amsterdam', availability='sold') # sold listings
353357
>>> f.search_listing('1012AB', radius_km=30, price_max=1250000, energy_label=['A', 'A+'])
354358
"""
355359
import json
@@ -359,9 +363,19 @@ def search_listing(
359363
if location:
360364
locations = [location] if isinstance(location, str) else list(location)
361365

366+
# Normalize availability - map user-friendly "sold" to API's "unavailable"
367+
if availability is None:
368+
avail_list = ["available", "negotiations"]
369+
elif isinstance(availability, str):
370+
avail_list = [availability]
371+
else:
372+
avail_list = list(availability)
373+
# Map "sold" to "unavailable" (API terminology)
374+
avail_list = ["unavailable" if v == "sold" else v for v in avail_list]
375+
362376
# Build search params
363377
params: dict[str, Any] = {
364-
"availability": ["available", "negotiations"],
378+
"availability": avail_list,
365379
"type": ["single"],
366380
"zoning": ["residential"],
367381
"object_type": object_type or ["house", "apartment"],

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "pyfunda"
7-
version = "2.3.0"
7+
version = "2.4.0"
88
description = "Python API for Funda.nl real estate listings"
99
readme = "README.md"
1010
license = "AGPL-3.0-or-later"

0 commit comments

Comments
 (0)