pyfunda 3.0.0__tar.gz → 3.1.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.
Files changed (58) hide show
  1. pyfunda-3.1.0/PKG-INFO +157 -0
  2. pyfunda-3.1.0/README.md +132 -0
  3. pyfunda-3.1.0/docs/API.md +528 -0
  4. pyfunda-3.1.0/docs/ARCHITECTURE.md +87 -0
  5. {pyfunda-3.0.0 → pyfunda-3.1.0}/docs/DEVELOPMENT.md +4 -2
  6. pyfunda-3.1.0/docs/EXAMPLES.md +223 -0
  7. pyfunda-3.1.0/docs/README.md +71 -0
  8. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/__init__.py +4 -0
  9. pyfunda-3.1.0/funda/_autocomplete_parser.py +64 -0
  10. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_detail_parser.py +4 -9
  11. pyfunda-3.1.0/funda/autocomplete.py +47 -0
  12. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/constants.py +15 -0
  13. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/funda.py +58 -9
  14. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/listing.py +55 -24
  15. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/models.py +5 -0
  16. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/parsing.py +2 -0
  17. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/search.py +102 -83
  18. {pyfunda-3.0.0 → pyfunda-3.1.0}/pyproject.toml +1 -1
  19. pyfunda-3.1.0/tests/test_autocomplete.py +99 -0
  20. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_client.py +41 -1
  21. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_models.py +24 -1
  22. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_search.py +16 -2
  23. pyfunda-3.0.0/PKG-INFO +0 -250
  24. pyfunda-3.0.0/README.md +0 -225
  25. pyfunda-3.0.0/docs/API.md +0 -211
  26. {pyfunda-3.0.0 → pyfunda-3.1.0}/.dockerignore +0 -0
  27. {pyfunda-3.0.0 → pyfunda-3.1.0}/.github/FUNDING.yml +0 -0
  28. {pyfunda-3.0.0 → pyfunda-3.1.0}/.github/workflows/publish.yml +0 -0
  29. {pyfunda-3.0.0 → pyfunda-3.1.0}/.gitignore +0 -0
  30. {pyfunda-3.0.0 → pyfunda-3.1.0}/Dockerfile +0 -0
  31. {pyfunda-3.0.0 → pyfunda-3.1.0}/LICENSE +0 -0
  32. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/almere_age_rank.py +0 -0
  33. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/analysis.ipynb +0 -0
  34. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/batch_details.py +0 -0
  35. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/broker_due_diligence.py +0 -0
  36. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/enrichment_export.py +0 -0
  37. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/export_to_csv.py +0 -0
  38. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/full_api_walkthrough.py +0 -0
  39. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/neighborhood_market_snapshot.py +0 -0
  40. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/new_listings_alert.py +0 -0
  41. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/poll_new_listings.py +0 -0
  42. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/price_history.py +0 -0
  43. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/price_tracker.py +0 -0
  44. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/search_sold.py +0 -0
  45. {pyfunda-3.0.0 → pyfunda-3.1.0}/examples/similar_sales_comp.py +0 -0
  46. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_enrichment_parser.py +0 -0
  47. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_parallel.py +0 -0
  48. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_parse_helpers.py +0 -0
  49. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_price_history_parser.py +0 -0
  50. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_search_parser.py +0 -0
  51. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/_transport.py +0 -0
  52. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/exceptions.py +0 -0
  53. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/headers.py +0 -0
  54. {pyfunda-3.0.0 → pyfunda-3.1.0}/funda/py.typed +0 -0
  55. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/__init__.py +0 -0
  56. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_enrichment_parser.py +0 -0
  57. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_live.py +0 -0
  58. {pyfunda-3.0.0 → pyfunda-3.1.0}/tests/test_transport_parallel.py +0 -0
pyfunda-3.1.0/PKG-INFO ADDED
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyfunda
3
+ Version: 3.1.0
4
+ Summary: Python API for Funda.nl real estate listings
5
+ Project-URL: Homepage, https://github.com/0xMH/pyfunda
6
+ Project-URL: Repository, https://github.com/0xMH/pyfunda
7
+ Project-URL: Issues, https://github.com/0xMH/pyfunda/issues
8
+ Author: 0xMH
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Keywords: api,funda,housing,netherlands,real-estate,scraper
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: curl-cffi>=0.14.0
22
+ Requires-Dist: tls-client>=1.0.1
23
+ Requires-Dist: typing-extensions>=4.0.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # pyfunda
27
+
28
+ [![PyPI version](https://img.shields.io/pypi/v/pyfunda)](https://pypi.org/project/pyfunda/)
29
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyfunda)](https://pypi.org/project/pyfunda/)
30
+ [![License](https://img.shields.io/pypi/l/pyfunda)](https://github.com/0xMH/pyfunda/blob/main/LICENSE)
31
+
32
+ The only working real Python API for Funda ([funda.nl](https://www.funda.nl)) -- the Netherlands' largest real estate platform.
33
+
34
+ > If you find this useful, consider giving it a star -- it helps others discover the project.
35
+
36
+ [![Star History Chart](https://api.star-history.com/svg?repos=0xMH/pyfunda&type=Date)](https://star-history.com/#0xMH/pyfunda&Date)
37
+
38
+ ## Why I'm open-sourcing this?
39
+
40
+ After pyfunda, I got messages asking why I'd give this away when aggregators will just take it and sell it. They're right, every week there's a new "revolutionary AI-powered housing finder" charging EUR40/month or a EUR250 "success fee." They all pull from the same one or two sources and wrap it in a fancy UI completely built with AI.
41
+
42
+ That's exactly why I'm open-sourcing it.
43
+
44
+ These services are selling air to people who are looking for any kind of hope. The data is public. The APIs aren't hard to figure out. You shouldn't have to pay someone to refresh a webpage for you. Funda could kill this entire market overnight by offering a public API. They don't, so here we are.
45
+
46
+ Here's the code, do it yourself. Send my library link to any AI service you use and ask it to build whatever tool you think will make your life easier while searching for your next home.
47
+
48
+ With pyfunda, I've already done all the heavy lifting for you.
49
+
50
+ ## Why pyfunda?
51
+
52
+ **Because it simply works.**
53
+
54
+ Funda has no public developer API. If you want Dutch real estate data programmatically, your options are limited:
55
+
56
+ | Library | Approach | Limitations |
57
+ |---------|----------|-------------|
58
+ | [whchien/funda-scraper](https://github.com/whchien/funda-scraper) | HTML scraping | Listing dates blocked since Q4 2023 (requires login). Breaks when Funda changes frontend. |
59
+ | [khpeek/funda-scraper](https://github.com/khpeek/funda-scraper) | Scrapy | Last updated 2016. No longer maintained. |
60
+ | [joostboon/Funda-Scraper](https://github.com/joostboon/Funda-Scraper) | Selenium | Requires manual CAPTCHA solving. Slow browser automation. |
61
+ | **Official API** | Broker API | Only available to registered brokers. Not accessible to regular developers. |
62
+
63
+ **pyfunda takes a different approach:** it uses Funda's app-facing JSON APIs instead of scraping browser HTML.
64
+
65
+ - Pure Python, no browser or Selenium needed
66
+ - No manual CAPTCHA solving
67
+ - Typed Python objects for listings, prices, media, brokers, coordinates, and history
68
+ - Search, listing detail, enrichment, broker, price-history, polling, and parallel batch workflows
69
+ - Raw Funda payloads are still available when you need fields that pyfunda does not model yet
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ pip install pyfunda
75
+ ```
76
+
77
+ For local development:
78
+
79
+ ```bash
80
+ uv sync
81
+ uv run python -m unittest discover -s tests
82
+ ```
83
+
84
+ ## Quick Start
85
+
86
+ ```python
87
+ from funda import Funda
88
+
89
+ with Funda() as client:
90
+ # Get a listing by global id, tiny id, or Funda URL
91
+ listing = client.listing(43117443)
92
+ print(listing.title, listing.city, listing.price.amount)
93
+
94
+ # Search listings
95
+ results = client.search("amsterdam", max_price=500000)
96
+ for item in results:
97
+ print(item.title, item.price.amount, item.url)
98
+ ```
99
+
100
+ ## How It Works
101
+
102
+ This library uses Funda's undocumented app-facing APIs, which provide clean JSON responses unlike the website that embeds data in Nuxt.js/JavaScript bundles.
103
+
104
+ ### Discovery Process
105
+
106
+ The original API was reverse engineered by intercepting and analyzing HTTPS traffic from the official Funda Android app:
107
+
108
+ 1. Configured an Android device to route traffic through an intercepting proxy
109
+ 2. Used the Funda app normally - browsing listings, searching, opening shared URLs
110
+ 3. Identified the `*.funda.io` API infrastructure separate from the `www.funda.nl` website
111
+ 4. Analyzed request/response patterns to understand the query format and available filters
112
+ 5. Discovered how the app resolves URL-based IDs (`tinyId`) to internal IDs (`globalId`)
113
+
114
+ ### API Architecture
115
+
116
+ The app-facing APIs live across several `*.funda.io` services:
117
+
118
+ | Endpoint | Method | Purpose |
119
+ |----------|--------|---------|
120
+ | `listing-detail-page.funda.io/api/v4/listing/object/nl/{globalId}` | GET | Fetch listing by internal ID |
121
+ | `listing-detail-page.funda.io/api/v4/listing/object/nl/tinyId/{tinyId}` | GET | Fetch listing by URL ID |
122
+ | `listing-search-wonen.funda.io/_msearch/template` | POST | Search listings |
123
+ | `listing-search-wonen.funda.io/geo-wonen-alias-prod/_search/template` | POST | Autocomplete location/search-box text |
124
+ | `listing-detail-summary.funda.io/api/v1/listing/nl/{globalId}` | GET | Fetch lightweight listing summary |
125
+ | `contacts-flows-bff.funda.io/.../contact-block` | GET | Fetch realtor contact block |
126
+ | `contacts-bff.funda.io/.../contact-form` | GET | Fetch contact-form availability |
127
+ | `local-listings.funda.io/api/v1/similarlistings` | GET | Fetch similar and recently sold listing IDs |
128
+ | `marketinsights.funda.io/v2/localinsights/preview/...` | GET | Fetch neighbourhood market insights |
129
+ | `brokerpresentation-office-pages-bff.funda.io/.../office-page/...` | GET | Fetch broker profile and listings |
130
+ | `reviews-office-pages-bff.funda.io/.../reviews/nl` | GET | Fetch broker review aggregates |
131
+ | `api.walterliving.com/hunter/lookup` | POST | Fetch price history data |
132
+
133
+ The request transport, headers, retry profiles, and TLS fingerprint rotation are internal implementation details. Normal users only construct `Funda()` and call the public methods below.
134
+
135
+ ## Documentation
136
+
137
+ - [Start here](docs/README.md)
138
+ - [API reference](docs/API.md)
139
+ - [Architecture notes](docs/ARCHITECTURE.md)
140
+ - [Examples](docs/EXAMPLES.md)
141
+ - [Development and testing](docs/DEVELOPMENT.md)
142
+
143
+ ## Disclaimer
144
+
145
+ This is an unofficial library and is not affiliated with, authorized, maintained, sponsored, or endorsed by Funda or any of its affiliates. Use at your own risk.
146
+
147
+ This library only accesses publicly available listing data through Funda's undocumented internal APIs. Using this library may violate Funda's Terms of Service. The authors are not responsible for any consequences of using this software.
148
+
149
+ This project is intended for personal use, research, and educational purposes only.
150
+
151
+ - The APIs are undocumented and may change or break at any time without notice.
152
+ - Please use this library responsibly and avoid excessive requests that could burden Funda's infrastructure.
153
+ - Data may be subject to copyright and usage restrictions. Ensure your use complies with applicable laws.
154
+
155
+ ## License
156
+
157
+ AGPL-3.0
@@ -0,0 +1,132 @@
1
+ # pyfunda
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/pyfunda)](https://pypi.org/project/pyfunda/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyfunda)](https://pypi.org/project/pyfunda/)
5
+ [![License](https://img.shields.io/pypi/l/pyfunda)](https://github.com/0xMH/pyfunda/blob/main/LICENSE)
6
+
7
+ The only working real Python API for Funda ([funda.nl](https://www.funda.nl)) -- the Netherlands' largest real estate platform.
8
+
9
+ > If you find this useful, consider giving it a star -- it helps others discover the project.
10
+
11
+ [![Star History Chart](https://api.star-history.com/svg?repos=0xMH/pyfunda&type=Date)](https://star-history.com/#0xMH/pyfunda&Date)
12
+
13
+ ## Why I'm open-sourcing this?
14
+
15
+ After pyfunda, I got messages asking why I'd give this away when aggregators will just take it and sell it. They're right, every week there's a new "revolutionary AI-powered housing finder" charging EUR40/month or a EUR250 "success fee." They all pull from the same one or two sources and wrap it in a fancy UI completely built with AI.
16
+
17
+ That's exactly why I'm open-sourcing it.
18
+
19
+ These services are selling air to people who are looking for any kind of hope. The data is public. The APIs aren't hard to figure out. You shouldn't have to pay someone to refresh a webpage for you. Funda could kill this entire market overnight by offering a public API. They don't, so here we are.
20
+
21
+ Here's the code, do it yourself. Send my library link to any AI service you use and ask it to build whatever tool you think will make your life easier while searching for your next home.
22
+
23
+ With pyfunda, I've already done all the heavy lifting for you.
24
+
25
+ ## Why pyfunda?
26
+
27
+ **Because it simply works.**
28
+
29
+ Funda has no public developer API. If you want Dutch real estate data programmatically, your options are limited:
30
+
31
+ | Library | Approach | Limitations |
32
+ |---------|----------|-------------|
33
+ | [whchien/funda-scraper](https://github.com/whchien/funda-scraper) | HTML scraping | Listing dates blocked since Q4 2023 (requires login). Breaks when Funda changes frontend. |
34
+ | [khpeek/funda-scraper](https://github.com/khpeek/funda-scraper) | Scrapy | Last updated 2016. No longer maintained. |
35
+ | [joostboon/Funda-Scraper](https://github.com/joostboon/Funda-Scraper) | Selenium | Requires manual CAPTCHA solving. Slow browser automation. |
36
+ | **Official API** | Broker API | Only available to registered brokers. Not accessible to regular developers. |
37
+
38
+ **pyfunda takes a different approach:** it uses Funda's app-facing JSON APIs instead of scraping browser HTML.
39
+
40
+ - Pure Python, no browser or Selenium needed
41
+ - No manual CAPTCHA solving
42
+ - Typed Python objects for listings, prices, media, brokers, coordinates, and history
43
+ - Search, listing detail, enrichment, broker, price-history, polling, and parallel batch workflows
44
+ - Raw Funda payloads are still available when you need fields that pyfunda does not model yet
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install pyfunda
50
+ ```
51
+
52
+ For local development:
53
+
54
+ ```bash
55
+ uv sync
56
+ uv run python -m unittest discover -s tests
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ```python
62
+ from funda import Funda
63
+
64
+ with Funda() as client:
65
+ # Get a listing by global id, tiny id, or Funda URL
66
+ listing = client.listing(43117443)
67
+ print(listing.title, listing.city, listing.price.amount)
68
+
69
+ # Search listings
70
+ results = client.search("amsterdam", max_price=500000)
71
+ for item in results:
72
+ print(item.title, item.price.amount, item.url)
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ This library uses Funda's undocumented app-facing APIs, which provide clean JSON responses unlike the website that embeds data in Nuxt.js/JavaScript bundles.
78
+
79
+ ### Discovery Process
80
+
81
+ The original API was reverse engineered by intercepting and analyzing HTTPS traffic from the official Funda Android app:
82
+
83
+ 1. Configured an Android device to route traffic through an intercepting proxy
84
+ 2. Used the Funda app normally - browsing listings, searching, opening shared URLs
85
+ 3. Identified the `*.funda.io` API infrastructure separate from the `www.funda.nl` website
86
+ 4. Analyzed request/response patterns to understand the query format and available filters
87
+ 5. Discovered how the app resolves URL-based IDs (`tinyId`) to internal IDs (`globalId`)
88
+
89
+ ### API Architecture
90
+
91
+ The app-facing APIs live across several `*.funda.io` services:
92
+
93
+ | Endpoint | Method | Purpose |
94
+ |----------|--------|---------|
95
+ | `listing-detail-page.funda.io/api/v4/listing/object/nl/{globalId}` | GET | Fetch listing by internal ID |
96
+ | `listing-detail-page.funda.io/api/v4/listing/object/nl/tinyId/{tinyId}` | GET | Fetch listing by URL ID |
97
+ | `listing-search-wonen.funda.io/_msearch/template` | POST | Search listings |
98
+ | `listing-search-wonen.funda.io/geo-wonen-alias-prod/_search/template` | POST | Autocomplete location/search-box text |
99
+ | `listing-detail-summary.funda.io/api/v1/listing/nl/{globalId}` | GET | Fetch lightweight listing summary |
100
+ | `contacts-flows-bff.funda.io/.../contact-block` | GET | Fetch realtor contact block |
101
+ | `contacts-bff.funda.io/.../contact-form` | GET | Fetch contact-form availability |
102
+ | `local-listings.funda.io/api/v1/similarlistings` | GET | Fetch similar and recently sold listing IDs |
103
+ | `marketinsights.funda.io/v2/localinsights/preview/...` | GET | Fetch neighbourhood market insights |
104
+ | `brokerpresentation-office-pages-bff.funda.io/.../office-page/...` | GET | Fetch broker profile and listings |
105
+ | `reviews-office-pages-bff.funda.io/.../reviews/nl` | GET | Fetch broker review aggregates |
106
+ | `api.walterliving.com/hunter/lookup` | POST | Fetch price history data |
107
+
108
+ The request transport, headers, retry profiles, and TLS fingerprint rotation are internal implementation details. Normal users only construct `Funda()` and call the public methods below.
109
+
110
+ ## Documentation
111
+
112
+ - [Start here](docs/README.md)
113
+ - [API reference](docs/API.md)
114
+ - [Architecture notes](docs/ARCHITECTURE.md)
115
+ - [Examples](docs/EXAMPLES.md)
116
+ - [Development and testing](docs/DEVELOPMENT.md)
117
+
118
+ ## Disclaimer
119
+
120
+ This is an unofficial library and is not affiliated with, authorized, maintained, sponsored, or endorsed by Funda or any of its affiliates. Use at your own risk.
121
+
122
+ This library only accesses publicly available listing data through Funda's undocumented internal APIs. Using this library may violate Funda's Terms of Service. The authors are not responsible for any consequences of using this software.
123
+
124
+ This project is intended for personal use, research, and educational purposes only.
125
+
126
+ - The APIs are undocumented and may change or break at any time without notice.
127
+ - Please use this library responsibly and avoid excessive requests that could burden Funda's infrastructure.
128
+ - Data may be subject to copyright and usage restrictions. Ensure your use complies with applicable laws.
129
+
130
+ ## License
131
+
132
+ AGPL-3.0