pyfunda 2.3.0__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfunda
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Python API for Funda.nl real estate listings
5
5
  Project-URL: Homepage, https://github.com/0xMH/pyfunda
6
6
  Project-URL: Repository, https://github.com/0xMH/pyfunda
@@ -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()
@@ -315,6 +315,7 @@ class Funda:
315
315
  self,
316
316
  location: str | list[str] | None = None,
317
317
  offering_type: str = "buy",
318
+ availability: str | list[str] | None = None,
318
319
  price_min: int | None = None,
319
320
  price_max: int | None = None,
320
321
  area_min: int | None = None,
@@ -332,6 +333,8 @@ class Funda:
332
333
  Args:
333
334
  location: City/area name(s) or postcode to search in
334
335
  offering_type: "buy" or "rent"
336
+ availability: Filter by status - "available", "negotiations", "sold",
337
+ or a list combining them. Default: ["available", "negotiations"]
335
338
  price_min: Minimum price
336
339
  price_max: Maximum price
337
340
  area_min: Minimum living area in m²
@@ -350,6 +353,7 @@ class Funda:
350
353
 
351
354
  Example:
352
355
  >>> f.search_listing('amsterdam', price_max=500000)
356
+ >>> f.search_listing('amsterdam', availability='sold') # sold listings
353
357
  >>> f.search_listing('1012AB', radius_km=30, price_max=1250000, energy_label=['A', 'A+'])
354
358
  """
355
359
  import json
@@ -359,9 +363,19 @@ class Funda:
359
363
  if location:
360
364
  locations = [location] if isinstance(location, str) else list(location)
361
365
 
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
+
362
376
  # Build search params
363
377
  params: dict[str, Any] = {
364
- "availability": ["available", "negotiations"],
378
+ "availability": avail_list,
365
379
  "type": ["single"],
366
380
  "zoning": ["residential"],
367
381
  "object_type": object_type or ["house", "apartment"],
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pyfunda"
7
- version = "2.3.0"
7
+ version = "2.4.0"
8
8
  description = "Python API for Funda.nl real estate listings"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes