htag-sdk 1.9.2__tar.gz → 1.9.3__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. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/PKG-INFO +1 -1
  2. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/pyproject.toml +1 -1
  3. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/__init__.py +6 -0
  4. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/markets/models.py +1 -0
  5. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/property/__init__.py +6 -0
  6. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/property/async_client.py +21 -0
  7. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/property/client.py +29 -0
  8. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/property/models.py +40 -0
  9. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/reference/models.py +1 -0
  10. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_domains.py +34 -0
  11. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/.gitignore +0 -0
  12. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/README.md +0 -0
  13. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/_async_client.py +0 -0
  14. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/_base.py +0 -0
  15. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/_client.py +0 -0
  16. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/_exceptions.py +0 -0
  17. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/_types.py +0 -0
  18. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/address/__init__.py +0 -0
  19. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/address/async_client.py +0 -0
  20. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/address/client.py +0 -0
  21. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/address/models.py +0 -0
  22. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/agents/__init__.py +0 -0
  23. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/agents/async_client.py +0 -0
  24. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/agents/client.py +0 -0
  25. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/agents/models.py +0 -0
  26. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/demographics/__init__.py +0 -0
  27. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/demographics/async_client.py +0 -0
  28. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/demographics/client.py +0 -0
  29. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/demographics/models.py +0 -0
  30. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/economics/__init__.py +0 -0
  31. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/economics/async_client.py +0 -0
  32. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/economics/client.py +0 -0
  33. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/economics/models.py +0 -0
  34. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/intent_hub/__init__.py +0 -0
  35. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/intent_hub/async_client.py +0 -0
  36. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/intent_hub/client.py +0 -0
  37. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/intent_hub/models.py +0 -0
  38. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/internal/__init__.py +0 -0
  39. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/internal/async_client.py +0 -0
  40. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/internal/client.py +0 -0
  41. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/markets/__init__.py +0 -0
  42. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/markets/async_client.py +0 -0
  43. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/markets/client.py +0 -0
  44. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/py.typed +0 -0
  45. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/reference/__init__.py +0 -0
  46. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/reference/async_client.py +0 -0
  47. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/reference/client.py +0 -0
  48. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/spatial/__init__.py +0 -0
  49. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/spatial/async_client.py +0 -0
  50. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/spatial/client.py +0 -0
  51. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/src/htag_sdk/spatial/models.py +0 -0
  52. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/__init__.py +0 -0
  53. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_base.py +0 -0
  54. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_client.py +0 -0
  55. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_exceptions.py +0 -0
  56. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_models.py +0 -0
  57. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/tests/test_spatial_viewport.py +0 -0
  58. {htag_sdk-1.9.2 → htag_sdk-1.9.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htag-sdk
3
- Version: 1.9.2
3
+ Version: 1.9.3
4
4
  Summary: Official Python SDK for the HtAG Location Intelligence API — address search, property data, and market analytics for Australia
5
5
  Project-URL: Homepage, https://developer.htagai.com
6
6
  Project-URL: Documentation, https://developer.htagai.com
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "htag-sdk"
7
- version = "1.9.2"
7
+ version = "1.9.3"
8
8
  description = "Official Python SDK for the HtAG Location Intelligence API — address search, property data, and market analytics for Australia"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -62,8 +62,11 @@ from htag_sdk.property.models import (
62
62
  PropertyAvmResponse,
63
63
  PropertyEstimatesRecord,
64
64
  PropertyEstimatesResponse,
65
+ PropertyHistoryResponse,
65
66
  PropertyMarketRecord,
66
67
  PropertyMarketResponse,
68
+ PropertyRentedHistoryRecord,
69
+ PropertySoldHistoryRecord,
67
70
  PropertySummaryRecord,
68
71
  PropertySummaryResponse,
69
72
  RentedPropertiesResponse,
@@ -233,8 +236,11 @@ __all__ = [
233
236
  "PropertyAvmResponse",
234
237
  "PropertyEstimatesRecord",
235
238
  "PropertyEstimatesResponse",
239
+ "PropertyHistoryResponse",
236
240
  "PropertyMarketRecord",
237
241
  "PropertyMarketResponse",
242
+ "PropertyRentedHistoryRecord",
243
+ "PropertySoldHistoryRecord",
238
244
  "PropertySummaryRecord",
239
245
  "PropertySummaryResponse",
240
246
  "RentedPropertiesResponse",
@@ -722,6 +722,7 @@ class MarketFundamentalsRecord(BaseModel):
722
722
  uh_ratio: Optional[float] = None
723
723
  uhv_ratio: Optional[float] = None
724
724
  years_to_own: Optional[float] = None
725
+ non_res_ba_per_capita_value: Optional[float] = None
725
726
 
726
727
 
727
728
  class MarketCycleRecord(BaseModel):
@@ -7,8 +7,11 @@ from htag_sdk.property.models import (
7
7
  PropertyAvmResponse,
8
8
  PropertyEstimatesRecord,
9
9
  PropertyEstimatesResponse,
10
+ PropertyHistoryResponse,
10
11
  PropertyMarketRecord,
11
12
  PropertyMarketResponse,
13
+ PropertyRentedHistoryRecord,
14
+ PropertySoldHistoryRecord,
12
15
  PropertySummaryRecord,
13
16
  PropertySummaryResponse,
14
17
  SoldPropertiesResponse,
@@ -22,8 +25,11 @@ __all__ = [
22
25
  "PropertyAvmResponse",
23
26
  "PropertyEstimatesRecord",
24
27
  "PropertyEstimatesResponse",
28
+ "PropertyHistoryResponse",
25
29
  "PropertyMarketRecord",
26
30
  "PropertyMarketResponse",
31
+ "PropertyRentedHistoryRecord",
32
+ "PropertySoldHistoryRecord",
27
33
  "PropertySummaryRecord",
28
34
  "PropertySummaryResponse",
29
35
  "SoldPropertiesResponse",
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, List, Optional, Union
6
6
 
7
7
  from htag_sdk.property.models import (
8
8
  PropertyEstimatesResponse,
9
+ PropertyHistoryResponse,
9
10
  PropertyMarketResponse,
10
11
  PropertySummaryResponse,
11
12
  RentedPropertiesResponse,
@@ -230,6 +231,26 @@ class AsyncPropertyClient:
230
231
  data = await self._client._request("GET", "/property/market", params=params)
231
232
  return PropertyMarketResponse.model_validate(data)
232
233
 
234
+ async def history(
235
+ self,
236
+ *,
237
+ address_key: str,
238
+ sold: bool = True,
239
+ rented: bool = True,
240
+ ) -> PropertyHistoryResponse:
241
+ """Async: retrieve the full sale and/or rental history for an address.
242
+
243
+ See :meth:`htag_sdk.property.client.PropertyClient.history` for full
244
+ documentation.
245
+ """
246
+ params = {
247
+ "address_key": address_key,
248
+ "sold": sold,
249
+ "rented": rented,
250
+ }
251
+ data = await self._client._request("GET", "/property/history", params=params)
252
+ return PropertyHistoryResponse.model_validate(data)
253
+
233
254
  async def structural_adjustment(
234
255
  self,
235
256
  *,
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, List, Optional, Union
6
6
 
7
7
  from htag_sdk.property.models import (
8
8
  PropertyEstimatesResponse,
9
+ PropertyHistoryResponse,
9
10
  PropertyMarketResponse,
10
11
  PropertySummaryResponse,
11
12
  RentedPropertiesResponse,
@@ -257,6 +258,34 @@ class PropertyClient:
257
258
  data = self._client._request("GET", "/property/market", params=params)
258
259
  return PropertyMarketResponse.model_validate(data)
259
260
 
261
+ def history(
262
+ self,
263
+ *,
264
+ address_key: str,
265
+ sold: bool = True,
266
+ rented: bool = True,
267
+ ) -> PropertyHistoryResponse:
268
+ """Retrieve the full sale and/or rental history for an address.
269
+
270
+ Args:
271
+ address_key: Canonical HTAG address key.
272
+ sold: Include sale history (default ``True``).
273
+ rented: Include rental history (default ``True``). At least one of
274
+ ``sold`` or ``rented`` must be ``True``.
275
+
276
+ Returns:
277
+ A :class:`PropertyHistoryResponse` with sale and rental timelines,
278
+ the canonical top-level ``property_type``, and a billable ``total``
279
+ row count.
280
+ """
281
+ params = {
282
+ "address_key": address_key,
283
+ "sold": sold,
284
+ "rented": rented,
285
+ }
286
+ data = self._client._request("GET", "/property/history", params=params)
287
+ return PropertyHistoryResponse.model_validate(data)
288
+
260
289
  def structural_adjustment(
261
290
  self,
262
291
  *,
@@ -137,6 +137,46 @@ class PropertyMarketResponse(BaseModel):
137
137
  total: int
138
138
 
139
139
 
140
+ class PropertySoldHistoryRecord(BaseModel):
141
+ """A single sale entry in the property history timeline."""
142
+
143
+ model_config = ConfigDict(populate_by_name=True)
144
+
145
+ sale_date: Optional[str] = None
146
+ sale_price: Optional[int] = None
147
+ bedrooms: Optional[int] = None
148
+ bathrooms: Optional[int] = None
149
+ car_spaces: Optional[int] = None
150
+ land_area: Optional[float] = None
151
+ floor_area: Optional[float] = None
152
+
153
+
154
+ class PropertyRentedHistoryRecord(BaseModel):
155
+ """A single rental entry in the property history timeline."""
156
+
157
+ model_config = ConfigDict(populate_by_name=True)
158
+
159
+ list_date: Optional[str] = None
160
+ rent: Optional[float] = None
161
+ bedrooms: Optional[int] = None
162
+ bathrooms: Optional[int] = None
163
+ car_spaces: Optional[int] = None
164
+ land_area: Optional[float] = None
165
+ floor_area: Optional[float] = None
166
+
167
+
168
+ class PropertyHistoryResponse(BaseModel):
169
+ """Response from the property history endpoint."""
170
+
171
+ model_config = ConfigDict(populate_by_name=True)
172
+
173
+ address_key: str
174
+ property_type: Optional[str] = None
175
+ total: int = 0
176
+ sales: Optional[List[PropertySoldHistoryRecord]] = None
177
+ rentals: Optional[List[PropertyRentedHistoryRecord]] = None
178
+
179
+
140
180
  class PropertyAvmRecord(BaseModel):
141
181
  """A single AVM valuation result."""
142
182
 
@@ -193,6 +193,7 @@ class AddressKeyRecord(BaseModel):
193
193
  parking: Optional[int] = None
194
194
  lot_size: Optional[float] = None
195
195
  property_group: Optional[str] = None
196
+ own_status: str = 'owner occupied'
196
197
 
197
198
 
198
199
  class AddressKeyEnumerationResponse(BaseModel):
@@ -215,6 +215,40 @@ class TestPropertyClient:
215
215
  assert result.results[0].address_key == "100102HICKOXSTREETTRARALGONVIC3844"
216
216
  client.close()
217
217
 
218
+ def test_property_history_sends_correct_params(self):
219
+ client = HtAgApi(api_key="sk-test", environment="prod")
220
+ mock_resp = _mock_response(
221
+ 200,
222
+ {
223
+ "address_key": "AK_HIST_1",
224
+ "property_type": "unit",
225
+ "total": 2,
226
+ "sales": [{"sale_date": "2025-10-13", "sale_price": 750000}],
227
+ "rentals": [{"list_date": "2024-05-04", "rent": 550.0}],
228
+ },
229
+ )
230
+
231
+ with patch.object(client._http, "request", return_value=mock_resp) as mock_req:
232
+ result = client.property.history(
233
+ address_key="AK_HIST_1",
234
+ sold=True,
235
+ rented=False,
236
+ )
237
+
238
+ call_kwargs = mock_req.call_args
239
+ assert call_kwargs[0][0] == "GET"
240
+ assert "/property/history" in call_kwargs[0][1]
241
+ params = call_kwargs[1]["params"]
242
+ assert params["address_key"] == "AK_HIST_1"
243
+ assert params["sold"] == "true"
244
+ assert params["rented"] == "false"
245
+ assert result.address_key == "AK_HIST_1"
246
+ assert result.property_type == "unit"
247
+ assert result.total == 2
248
+ assert result.sales[0].sale_price == 750000
249
+ assert result.rentals[0].rent == 550.0
250
+ client.close()
251
+
218
252
  def test_rented_search_property_type_list_param(self):
219
253
  client = HtAgApi(api_key="sk-test", environment="prod")
220
254
  mock_resp = _mock_response(200, {"results": [], "total": 0})
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