blocket-api 0.2.7__tar.gz → 0.2.9__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: blocket_api
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: A python API wrapper for blocket.se
5
5
  Author-email: dunderrrrrr <emil.bjorkroth@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -11,12 +11,13 @@ Requires-Dist: pre-commit>=3.7.1
11
11
  Requires-Dist: pytest>=8.2.2
12
12
  Requires-Dist: respx>=0.21.1
13
13
  Requires-Dist: mypy>=1.15.0
14
- Requires-Dist: ruff>=0.12.3
14
+ Requires-Dist: pydantic>=2.11.7
15
+ Requires-Dist: ruff>=0.13.0
15
16
  Dynamic: license-file
16
17
 
17
18
  # BlocketAPI
18
19
 
19
- [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) [![Python versions](https://img.shields.io/pypi/pyversions/blocket-api?style=for-the-badge)](https://pypi.org/project/blocket_api/) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
20
+ [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) ![Python](https://img.shields.io/badge/Python-3.10-blue?style=for-the-badge) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
20
21
 
21
22
  BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blocket.se](https://blocket.se/). This means you can either retrieve results from a specific saved search or list all listings/ads across all saved searches. The results from these queries are returned in a `json` format.
22
23
 
@@ -27,9 +28,9 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
27
28
  - List saved searches, called "Bevakningar".
28
29
  - Get listings from all or a specific saved search.
29
30
  - Search for anything on Blocket with filters for region and category.
30
- - Advanced search for vehicles with multiple filters.
31
- - Get vehicle information and price evaluation.
31
+ - Get vehicle information, price evaluation and search for vehicles with multiple filters.
32
32
  - Search for homes with filters for city, home type and more.
33
+ - Search for stores and get a specific store's listings.
33
34
 
34
35
  ## 🧑‍💻️ Install
35
36
 
@@ -50,23 +51,25 @@ pip install blocket-api
50
51
  ...
51
52
  ```
52
53
 
53
- Some calls require a bearerToken. However, some calls are public and don't require a token.
54
+ Some calls require a `bearerToken`. However, some calls are public and don't require a token.
54
55
 
55
56
  [Where token?](#-blocket-api-token)
56
57
 
57
58
 
58
- | Function | Token required | Description |
59
- |---|---|---|
60
- | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | List your saved searches (bevakningar) |
61
- | [`api.get_listings()`](#get_listingssearch_id-limit) | 🔐 Yes | List items related to a saved search |
62
- | [`api.custom_search()`](#custom_searchsearch_query-region-limit) | 👏 No | Search for everything on Blocket and filter by region |
63
- | [`api.motor_search()`](#motor_searchpage-make-fuel-chassi-price-modelyear-milage-gearbox) | 👏 No | Advanced search for car-listings. |
64
- | [`api.price_eval()`](#price_evalregistration_number) | 👏 No | Vehicle purchase valuation and details. |
65
- | [`api.home_search()`](#home_searchcity-type-order_by-ordering-offset) | 👏 No | Query home listings.
59
+ | Function | Token required | `as_objects` | Description |
60
+ |---|---|---|---|
61
+ | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | - | List your saved searches (bevakningar) |
62
+ | [`api.get_listings()`](#get_listings) | 🔐 Yes | - | List items related to a saved search |
63
+ | [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
64
+ | [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
65
+ | [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
66
+ | [`api.home_search()`](#home_search) | 👏 No | - | Query home listings.
67
+ | [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
68
+ | [`api.get_store_listings()`](#get_store_listings) | 👏 No | - | Get listings from a specific store.
66
69
 
67
70
  ## 🤓 Detailed usage
68
71
 
69
- ### saved_searches()
72
+ ### saved_searches
70
73
 
71
74
  Saved searches are your so called "Bevakningar" and can be found [here](https://www.blocket.se/sparade/bevakningar). Each saved search has and unique `id` which can be used as a parameter to `get_listings()`, see below.
72
75
 
@@ -85,7 +88,7 @@ Saved searches are your so called "Bevakningar" and can be found [here](https://
85
88
  ]
86
89
  ```
87
90
 
88
- ### get_listings(search_id, limit)
91
+ ### get_listings
89
92
  Returns all listings related to a saved search.
90
93
 
91
94
  Parameters:
@@ -119,7 +122,7 @@ Parameters:
119
122
  }
120
123
  ```
121
124
 
122
- ### custom_search(search_query, region, limit)
125
+ ### custom_search
123
126
  Make a custom search through out all of blocked. A region can be passed in as parameter for filtering.
124
127
 
125
128
  Parameters:
@@ -127,6 +130,7 @@ Parameters:
127
130
  - `region` (`enum`, optional) - Filter results on a region, default is all of Sweden.
128
131
  - `category` (`enum`, optional) - Filter for a specific category, ex. `Category.for_hemmet`.
129
132
  - `limit` (`int`, optional) - Limit number of results returned, max is 99.
133
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
130
134
 
131
135
  ```py
132
136
  >>> from blocket_api import Region
@@ -160,7 +164,7 @@ Parameters:
160
164
  }
161
165
  ```
162
166
 
163
- ### motor_search(page, make, fuel, chassi, price, modelYear, milage, gearbox)
167
+ ### motor_search
164
168
  To query listings related to a specific car, supply the following parameters:
165
169
 
166
170
  - `page` (`int`, required) - Results are split in pages, set page number here.
@@ -171,6 +175,8 @@ To query listings related to a specific car, supply the following parameters:
171
175
  - `modelYear` (`Optional[Tuple[int, int]]`) - Set model year range, ex. `(2000, 2020)`.
172
176
  - `milage` (`Optional[Tuple[int, int]]`) - Set milage range, ex. `(1000, 2000)`.
173
177
  - `gearbox` (`Optional[GEARBOX_OPTIONS]`) - Filter a specific gearbox, ex. `Automat`.
178
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
179
+
174
180
  ```py
175
181
  >>> api.motor_search(
176
182
  make=["Audi", "Ford"],
@@ -182,7 +188,7 @@ To query listings related to a specific car, supply the following parameters:
182
188
  ...
183
189
  ```
184
190
 
185
- ### price_eval(registration_number)
191
+ ### price_eval
186
192
  Query price evaluation for a specific vehicle by using cars registration number (ABC123). This returns company and private estimated prices, car information, and more. The api queries same endpoint as Blockets ["värdera bil"](https://www.blocket.se/tjanster/vardera-bil) service.
187
193
 
188
194
  - `registration_number` (`str`, required) - Registration number of the vehicle.
@@ -201,7 +207,7 @@ Query price evaluation for a specific vehicle by using cars registration number
201
207
  }
202
208
  ```
203
209
 
204
- ### home_search(city, type, order_by, ordering, offset)
210
+ ### home_search
205
211
  Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
206
212
 
207
213
  - `city` (`str`, required) - City name, ex. Stockholm.
@@ -221,6 +227,50 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
221
227
  ...
222
228
  ```
223
229
 
230
+ ### store_search
231
+ Search for a store in Blocket stores from [blocket.se/butiker](https://www.blocket.se/butiker).
232
+
233
+ - `search_query` (`str`, required) - The name of the store.
234
+ - `page` (`int`, optional) - The page number to return.
235
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
236
+
237
+
238
+ ```py
239
+ >>> api.store_search("Jannes Car and Kebab")
240
+ {
241
+ "data": [
242
+ {
243
+ "store_id": "1234",
244
+ "store_name": "Jannes Car and Kebab",
245
+ ...
246
+ },
247
+ ...
248
+ ]
249
+ }
250
+ ```
251
+
252
+ ### get_store_listings
253
+ Get listings from a specific store.
254
+
255
+ - `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
256
+ - `page` (`int`, optional) - The page number to return.
257
+
258
+ ```py
259
+ >>> api.get_store_listings(1234)
260
+ {
261
+ "data": [
262
+ {
263
+ "ad_id": "1234",
264
+ "price": 316000,
265
+ "body": "A very nice car.",
266
+ "images": [],
267
+ ...
268
+ },
269
+ ...
270
+ ]
271
+ }
272
+ ```
273
+
224
274
  ## 🔐 Blocket API token
225
275
 
226
276
  There are two ways to acquire your token:
@@ -1,6 +1,6 @@
1
1
  # BlocketAPI
2
2
 
3
- [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) [![Python versions](https://img.shields.io/pypi/pyversions/blocket-api?style=for-the-badge)](https://pypi.org/project/blocket_api/) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
3
+ [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) ![Python](https://img.shields.io/badge/Python-3.10-blue?style=for-the-badge) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
4
4
 
5
5
  BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blocket.se](https://blocket.se/). This means you can either retrieve results from a specific saved search or list all listings/ads across all saved searches. The results from these queries are returned in a `json` format.
6
6
 
@@ -11,9 +11,9 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
11
11
  - List saved searches, called "Bevakningar".
12
12
  - Get listings from all or a specific saved search.
13
13
  - Search for anything on Blocket with filters for region and category.
14
- - Advanced search for vehicles with multiple filters.
15
- - Get vehicle information and price evaluation.
14
+ - Get vehicle information, price evaluation and search for vehicles with multiple filters.
16
15
  - Search for homes with filters for city, home type and more.
16
+ - Search for stores and get a specific store's listings.
17
17
 
18
18
  ## 🧑‍💻️ Install
19
19
 
@@ -34,23 +34,25 @@ pip install blocket-api
34
34
  ...
35
35
  ```
36
36
 
37
- Some calls require a bearerToken. However, some calls are public and don't require a token.
37
+ Some calls require a `bearerToken`. However, some calls are public and don't require a token.
38
38
 
39
39
  [Where token?](#-blocket-api-token)
40
40
 
41
41
 
42
- | Function | Token required | Description |
43
- |---|---|---|
44
- | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | List your saved searches (bevakningar) |
45
- | [`api.get_listings()`](#get_listingssearch_id-limit) | 🔐 Yes | List items related to a saved search |
46
- | [`api.custom_search()`](#custom_searchsearch_query-region-limit) | 👏 No | Search for everything on Blocket and filter by region |
47
- | [`api.motor_search()`](#motor_searchpage-make-fuel-chassi-price-modelyear-milage-gearbox) | 👏 No | Advanced search for car-listings. |
48
- | [`api.price_eval()`](#price_evalregistration_number) | 👏 No | Vehicle purchase valuation and details. |
49
- | [`api.home_search()`](#home_searchcity-type-order_by-ordering-offset) | 👏 No | Query home listings.
42
+ | Function | Token required | `as_objects` | Description |
43
+ |---|---|---|---|
44
+ | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | - | List your saved searches (bevakningar) |
45
+ | [`api.get_listings()`](#get_listings) | 🔐 Yes | - | List items related to a saved search |
46
+ | [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
47
+ | [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
48
+ | [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
49
+ | [`api.home_search()`](#home_search) | 👏 No | - | Query home listings.
50
+ | [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
51
+ | [`api.get_store_listings()`](#get_store_listings) | 👏 No | - | Get listings from a specific store.
50
52
 
51
53
  ## 🤓 Detailed usage
52
54
 
53
- ### saved_searches()
55
+ ### saved_searches
54
56
 
55
57
  Saved searches are your so called "Bevakningar" and can be found [here](https://www.blocket.se/sparade/bevakningar). Each saved search has and unique `id` which can be used as a parameter to `get_listings()`, see below.
56
58
 
@@ -69,7 +71,7 @@ Saved searches are your so called "Bevakningar" and can be found [here](https://
69
71
  ]
70
72
  ```
71
73
 
72
- ### get_listings(search_id, limit)
74
+ ### get_listings
73
75
  Returns all listings related to a saved search.
74
76
 
75
77
  Parameters:
@@ -103,7 +105,7 @@ Parameters:
103
105
  }
104
106
  ```
105
107
 
106
- ### custom_search(search_query, region, limit)
108
+ ### custom_search
107
109
  Make a custom search through out all of blocked. A region can be passed in as parameter for filtering.
108
110
 
109
111
  Parameters:
@@ -111,6 +113,7 @@ Parameters:
111
113
  - `region` (`enum`, optional) - Filter results on a region, default is all of Sweden.
112
114
  - `category` (`enum`, optional) - Filter for a specific category, ex. `Category.for_hemmet`.
113
115
  - `limit` (`int`, optional) - Limit number of results returned, max is 99.
116
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
114
117
 
115
118
  ```py
116
119
  >>> from blocket_api import Region
@@ -144,7 +147,7 @@ Parameters:
144
147
  }
145
148
  ```
146
149
 
147
- ### motor_search(page, make, fuel, chassi, price, modelYear, milage, gearbox)
150
+ ### motor_search
148
151
  To query listings related to a specific car, supply the following parameters:
149
152
 
150
153
  - `page` (`int`, required) - Results are split in pages, set page number here.
@@ -155,6 +158,8 @@ To query listings related to a specific car, supply the following parameters:
155
158
  - `modelYear` (`Optional[Tuple[int, int]]`) - Set model year range, ex. `(2000, 2020)`.
156
159
  - `milage` (`Optional[Tuple[int, int]]`) - Set milage range, ex. `(1000, 2000)`.
157
160
  - `gearbox` (`Optional[GEARBOX_OPTIONS]`) - Filter a specific gearbox, ex. `Automat`.
161
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
162
+
158
163
  ```py
159
164
  >>> api.motor_search(
160
165
  make=["Audi", "Ford"],
@@ -166,7 +171,7 @@ To query listings related to a specific car, supply the following parameters:
166
171
  ...
167
172
  ```
168
173
 
169
- ### price_eval(registration_number)
174
+ ### price_eval
170
175
  Query price evaluation for a specific vehicle by using cars registration number (ABC123). This returns company and private estimated prices, car information, and more. The api queries same endpoint as Blockets ["värdera bil"](https://www.blocket.se/tjanster/vardera-bil) service.
171
176
 
172
177
  - `registration_number` (`str`, required) - Registration number of the vehicle.
@@ -185,7 +190,7 @@ Query price evaluation for a specific vehicle by using cars registration number
185
190
  }
186
191
  ```
187
192
 
188
- ### home_search(city, type, order_by, ordering, offset)
193
+ ### home_search
189
194
  Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
190
195
 
191
196
  - `city` (`str`, required) - City name, ex. Stockholm.
@@ -205,6 +210,50 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
205
210
  ...
206
211
  ```
207
212
 
213
+ ### store_search
214
+ Search for a store in Blocket stores from [blocket.se/butiker](https://www.blocket.se/butiker).
215
+
216
+ - `search_query` (`str`, required) - The name of the store.
217
+ - `page` (`int`, optional) - The page number to return.
218
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
219
+
220
+
221
+ ```py
222
+ >>> api.store_search("Jannes Car and Kebab")
223
+ {
224
+ "data": [
225
+ {
226
+ "store_id": "1234",
227
+ "store_name": "Jannes Car and Kebab",
228
+ ...
229
+ },
230
+ ...
231
+ ]
232
+ }
233
+ ```
234
+
235
+ ### get_store_listings
236
+ Get listings from a specific store.
237
+
238
+ - `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
239
+ - `page` (`int`, optional) - The page number to return.
240
+
241
+ ```py
242
+ >>> api.get_store_listings(1234)
243
+ {
244
+ "data": [
245
+ {
246
+ "ad_id": "1234",
247
+ "price": 316000,
248
+ "body": "A very nice car.",
249
+ "images": [],
250
+ ...
251
+ },
252
+ ...
253
+ ]
254
+ }
255
+ ```
256
+
208
257
  ## 🔐 Blocket API token
209
258
 
210
259
  There are two ways to acquire your token:
@@ -1,14 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Callable
4
- from functools import wraps
5
3
  import urllib
4
+ from collections.abc import Callable
6
5
  from dataclasses import dataclass
7
6
  from enum import Enum
8
- from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple
7
+ from functools import wraps
8
+ from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple, Union, overload
9
9
 
10
10
  import httpx
11
11
 
12
+ from blocket_api.models import (
13
+ CustomSearchResults,
14
+ MotorSearchResults,
15
+ StoreSearchResults,
16
+ )
12
17
  from blocket_api.qasa import HOME_SEARCH_ORDERING, HomeType, OrderBy, Qasa
13
18
 
14
19
  if TYPE_CHECKING:
@@ -232,6 +237,39 @@ class BlocketAPI:
232
237
  token=self.token,
233
238
  ).json()
234
239
 
240
+ @overload
241
+ def custom_search(
242
+ self,
243
+ search_query: str,
244
+ region: Region = Region.hela_sverige,
245
+ category: Category | None = None,
246
+ limit: int = 99,
247
+ *,
248
+ as_objects: Literal[True],
249
+ ) -> CustomSearchResults: ...
250
+
251
+ @overload
252
+ def custom_search(
253
+ self,
254
+ search_query: str,
255
+ region: Region = Region.hela_sverige,
256
+ category: Category | None = None,
257
+ limit: int = 99,
258
+ *,
259
+ as_objects: Literal[False] = False,
260
+ ) -> dict: ...
261
+
262
+ @overload
263
+ def custom_search(
264
+ self,
265
+ search_query: str,
266
+ region: Region = Region.hela_sverige,
267
+ category: Category | None = None,
268
+ limit: int = 99,
269
+ *,
270
+ as_objects: bool = False,
271
+ ) -> Union[dict, CustomSearchResults]: ...
272
+
235
273
  @public_token
236
274
  def custom_search(
237
275
  self,
@@ -239,10 +277,11 @@ class BlocketAPI:
239
277
  region: Region = Region.hela_sverige,
240
278
  category: Category | None = None,
241
279
  limit: int = 99,
242
- ) -> dict:
280
+ *,
281
+ as_objects: bool = False,
282
+ ) -> dict | CustomSearchResults:
243
283
  """
244
- Do a custom search through out all of Blocket.
245
- Supply a region for filtering. Default is all of Sweden.
284
+ Do a custom search throughout all of Blocket.
246
285
  """
247
286
  assert self.token
248
287
 
@@ -254,7 +293,53 @@ class BlocketAPI:
254
293
  if category:
255
294
  url += f"&cg={category.value}"
256
295
 
257
- return _make_request(url=url, token=self.token).json()
296
+ response = _make_request(url=url, token=self.token).json()
297
+ return CustomSearchResults.model_validate(response) if as_objects else response
298
+
299
+ @overload
300
+ def motor_search(
301
+ self,
302
+ page: int,
303
+ make: List[MAKE_OPTIONS],
304
+ fuel: Optional[List[FUEL_OPTIONS]] = None,
305
+ chassi: Optional[List[CHASSI_OPTIONS]] = None,
306
+ price: Optional[Tuple[int, int]] = None,
307
+ modelYear: Optional[Tuple[int, int]] = None,
308
+ milage: Optional[Tuple[int, int]] = None,
309
+ gearbox: Optional[GEARBOX_OPTIONS] = None,
310
+ *,
311
+ as_objects: Literal[True],
312
+ ) -> MotorSearchResults: ...
313
+
314
+ @overload
315
+ def motor_search(
316
+ self,
317
+ page: int,
318
+ make: List[MAKE_OPTIONS],
319
+ fuel: Optional[List[FUEL_OPTIONS]] = None,
320
+ chassi: Optional[List[CHASSI_OPTIONS]] = None,
321
+ price: Optional[Tuple[int, int]] = None,
322
+ modelYear: Optional[Tuple[int, int]] = None,
323
+ milage: Optional[Tuple[int, int]] = None,
324
+ gearbox: Optional[GEARBOX_OPTIONS] = None,
325
+ *,
326
+ as_objects: Literal[False] = False,
327
+ ) -> dict: ...
328
+
329
+ @overload
330
+ def motor_search(
331
+ self,
332
+ page: int,
333
+ make: List[MAKE_OPTIONS],
334
+ fuel: Optional[List[FUEL_OPTIONS]] = None,
335
+ chassi: Optional[List[CHASSI_OPTIONS]] = None,
336
+ price: Optional[Tuple[int, int]] = None,
337
+ modelYear: Optional[Tuple[int, int]] = None,
338
+ milage: Optional[Tuple[int, int]] = None,
339
+ gearbox: Optional[GEARBOX_OPTIONS] = None,
340
+ *,
341
+ as_objects: bool = False,
342
+ ) -> Union[dict, MotorSearchResults]: ...
258
343
 
259
344
  @public_token
260
345
  def motor_search(
@@ -267,7 +352,9 @@ class BlocketAPI:
267
352
  modelYear: Optional[Tuple[int, int]] = None,
268
353
  milage: Optional[Tuple[int, int]] = None,
269
354
  gearbox: Optional[GEARBOX_OPTIONS] = None,
270
- ) -> dict:
355
+ *,
356
+ as_objects: bool = False,
357
+ ) -> dict | MotorSearchResults:
271
358
  """
272
359
  Search specifically in the car section of Blocket
273
360
  with set optional parameters for filtering.
@@ -278,7 +365,8 @@ class BlocketAPI:
278
365
  set_params = {
279
366
  key: value
280
367
  for key, value in locals().items()
281
- if key not in ["self", "page", "range_params"] and value is not None
368
+ if key not in ["self", "page", "range_params", "as_objects"]
369
+ and value is not None
282
370
  }
283
371
 
284
372
  filters = []
@@ -298,7 +386,8 @@ class BlocketAPI:
298
386
  filters_str = "&".join([f"filter={f}" for f in filters])
299
387
  url = f"{motor_base_url}?{filters_str}&page={page}"
300
388
 
301
- return _make_request(url=f"{url}", token=self.token).json()
389
+ response = _make_request(url=f"{url}", token=self.token).json()
390
+ return MotorSearchResults.model_validate(response) if as_objects else response
302
391
 
303
392
  @public_token
304
393
  def price_eval(
@@ -334,3 +423,68 @@ class BlocketAPI:
334
423
  ordering=ordering,
335
424
  offset=offset,
336
425
  ).search()
426
+
427
+ @overload
428
+ def search_store(
429
+ self,
430
+ search_query: str,
431
+ page: int = 0,
432
+ *,
433
+ as_objects: Literal[True],
434
+ ) -> StoreSearchResults: ...
435
+
436
+ @overload
437
+ def search_store(
438
+ self,
439
+ search_query: str,
440
+ page: int = 0,
441
+ *,
442
+ as_objects: Literal[False] = False,
443
+ ) -> dict: ...
444
+
445
+ @overload
446
+ def search_store(
447
+ self,
448
+ page: int,
449
+ make: List[MAKE_OPTIONS],
450
+ fuel: Optional[List[FUEL_OPTIONS]] = None,
451
+ chassi: Optional[List[CHASSI_OPTIONS]] = None,
452
+ price: Optional[Tuple[int, int]] = None,
453
+ modelYear: Optional[Tuple[int, int]] = None,
454
+ milage: Optional[Tuple[int, int]] = None,
455
+ gearbox: Optional[GEARBOX_OPTIONS] = None,
456
+ *,
457
+ as_objects: bool = False,
458
+ ) -> Union[dict, StoreSearchResults]: ...
459
+
460
+ @public_token
461
+ def search_store(
462
+ self,
463
+ search_query: str,
464
+ page: int = 0,
465
+ *,
466
+ as_objects: bool = False,
467
+ ) -> dict | StoreSearchResults:
468
+ """
469
+ Searching through Blocket stores from https://www.blocket.se/butiker.
470
+ The store_id is used for get_store_listings().
471
+ """
472
+ url = f"{BASE_URL}/search_bff/v1/stores?q={search_query}&page={page}"
473
+ response = _make_request(url=f"{url}", token=self.token).json()
474
+ return StoreSearchResults.model_validate(response) if as_objects else response
475
+
476
+ @public_token
477
+ def get_store_listings(
478
+ self,
479
+ store_id: int,
480
+ page: int = 0,
481
+ ) -> dict:
482
+ """
483
+ Return all listings from a specific store from https://www.blocket.se/butik/<store>.
484
+ The store_id can be found by searching for the store with search_store().
485
+ """
486
+ url = (
487
+ f"{BASE_URL}/search_bff/v2/content?lim=60&page={page}&sort=rel&store_id={store_id}"
488
+ "&status=active&gl=3&include=extend_with_shipping"
489
+ )
490
+ return _make_request(url=f"{url}", token=self.token).json()
@@ -0,0 +1,151 @@
1
+ import datetime
2
+
3
+ from pydantic import BaseModel
4
+
5
+ ### CustomSearch ###
6
+
7
+
8
+ class CustomSearchPrice(BaseModel):
9
+ suffix: str
10
+ value: int
11
+
12
+
13
+ class CustomSearchLocation(BaseModel):
14
+ image_url: str
15
+ label: str
16
+ query_key: str | None = None
17
+
18
+
19
+ class CustomSearchImage(BaseModel):
20
+ height: int
21
+ type: str
22
+ url: str
23
+ width: int
24
+
25
+
26
+ class CustomSearchCategory(BaseModel):
27
+ id: str
28
+ name: str
29
+
30
+
31
+ class CustomSearchResult(BaseModel):
32
+ ad_id: str
33
+ ad_status: str
34
+ body: str
35
+ category: list[CustomSearchCategory]
36
+ images: list[CustomSearchImage] = []
37
+ list_id: str
38
+ list_time: datetime.datetime
39
+ map: CustomSearchLocation | None = None
40
+ map_url: str | None = None
41
+ price: CustomSearchPrice
42
+ share_url: str
43
+ state_id: str
44
+ subject: str
45
+ type: str
46
+ zipcode: str | None = None
47
+
48
+
49
+ class CustomSearchResults(BaseModel):
50
+ data: list[CustomSearchResult]
51
+ total_count: int
52
+
53
+
54
+ ### MotorSearch ###
55
+
56
+
57
+ class MotorSearchCarEquipment(BaseModel):
58
+ label: str
59
+
60
+
61
+ class MotorSearchCarLocation(BaseModel):
62
+ region: str
63
+ municipality: str
64
+ area: str
65
+
66
+
67
+ class MotorSearchCarImage(BaseModel):
68
+ height: int
69
+ width: int
70
+ image: str
71
+
72
+
73
+ class MotorSearchCar(BaseModel):
74
+ images: list[MotorSearchCarImage]
75
+ location: MotorSearchCarLocation
76
+ fuel: str
77
+ gearbox: str
78
+ regDate: int
79
+ mileage: int
80
+ equipment: list[MotorSearchCarEquipment] | None = None
81
+
82
+
83
+ class MotorSearchPrice(BaseModel):
84
+ amount: str
85
+ billingPeriod: str
86
+ oldPrice: str | None = None
87
+
88
+
89
+ class MotorSearchSeller(BaseModel):
90
+ type: str
91
+ name: str
92
+ id: str
93
+
94
+
95
+ class MotorSearchResult(BaseModel):
96
+ dealId: str
97
+ link: str
98
+ listTime: datetime.datetime
99
+ originalListTime: datetime.datetime
100
+ seller: MotorSearchSeller
101
+ heading: str
102
+ price: MotorSearchPrice
103
+ thumbnail: str
104
+ car: MotorSearchCar
105
+ description: str
106
+
107
+
108
+ class MotorSearchResults(BaseModel):
109
+ cars: list[MotorSearchResult]
110
+ hits: int
111
+ pages: int
112
+
113
+
114
+ ### StoreSearch ###
115
+
116
+
117
+ class StoreSearchLocation(BaseModel):
118
+ longitude: float
119
+ latitude: float
120
+
121
+
122
+ class StoreSearchImage(BaseModel):
123
+ height: int
124
+ width: int
125
+ url: str
126
+
127
+
128
+ class StoreSearchLogotype(BaseModel):
129
+ height: int
130
+ width: int
131
+ url: str
132
+
133
+
134
+ class StoreSearchResult(BaseModel):
135
+ address: str | None = None
136
+ id: str
137
+ logotype: StoreSearchLogotype
138
+ image: StoreSearchImage | None = None
139
+ description: str
140
+ external_store_url: str | None = None
141
+ location: StoreSearchLocation | None = None
142
+ name: str
143
+ title: str
144
+ ad_count: int
145
+ category: str
146
+
147
+
148
+ class StoreSearchResults(BaseModel):
149
+ data: list[StoreSearchResult]
150
+ total_count: int
151
+ total_page_count: int
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blocket_api
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: A python API wrapper for blocket.se
5
5
  Author-email: dunderrrrrr <emil.bjorkroth@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -11,12 +11,13 @@ Requires-Dist: pre-commit>=3.7.1
11
11
  Requires-Dist: pytest>=8.2.2
12
12
  Requires-Dist: respx>=0.21.1
13
13
  Requires-Dist: mypy>=1.15.0
14
- Requires-Dist: ruff>=0.12.3
14
+ Requires-Dist: pydantic>=2.11.7
15
+ Requires-Dist: ruff>=0.13.0
15
16
  Dynamic: license-file
16
17
 
17
18
  # BlocketAPI
18
19
 
19
- [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) [![Python versions](https://img.shields.io/pypi/pyversions/blocket-api?style=for-the-badge)](https://pypi.org/project/blocket_api/) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
20
+ [![PyPI version](https://img.shields.io/pypi/v/blocket_api?style=for-the-badge)](https://pypi.org/project/blocket_api/) [![License](https://img.shields.io/badge/license-WTFPL-green?style=for-the-badge)](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE) ![Python](https://img.shields.io/badge/Python-3.10-blue?style=for-the-badge) ![PyPI - Downloads](https://img.shields.io/pypi/dm/blocket_api?style=for-the-badge&color=%23dbce58)
20
21
 
21
22
  BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blocket.se](https://blocket.se/). This means you can either retrieve results from a specific saved search or list all listings/ads across all saved searches. The results from these queries are returned in a `json` format.
22
23
 
@@ -27,9 +28,9 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
27
28
  - List saved searches, called "Bevakningar".
28
29
  - Get listings from all or a specific saved search.
29
30
  - Search for anything on Blocket with filters for region and category.
30
- - Advanced search for vehicles with multiple filters.
31
- - Get vehicle information and price evaluation.
31
+ - Get vehicle information, price evaluation and search for vehicles with multiple filters.
32
32
  - Search for homes with filters for city, home type and more.
33
+ - Search for stores and get a specific store's listings.
33
34
 
34
35
  ## 🧑‍💻️ Install
35
36
 
@@ -50,23 +51,25 @@ pip install blocket-api
50
51
  ...
51
52
  ```
52
53
 
53
- Some calls require a bearerToken. However, some calls are public and don't require a token.
54
+ Some calls require a `bearerToken`. However, some calls are public and don't require a token.
54
55
 
55
56
  [Where token?](#-blocket-api-token)
56
57
 
57
58
 
58
- | Function | Token required | Description |
59
- |---|---|---|
60
- | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | List your saved searches (bevakningar) |
61
- | [`api.get_listings()`](#get_listingssearch_id-limit) | 🔐 Yes | List items related to a saved search |
62
- | [`api.custom_search()`](#custom_searchsearch_query-region-limit) | 👏 No | Search for everything on Blocket and filter by region |
63
- | [`api.motor_search()`](#motor_searchpage-make-fuel-chassi-price-modelyear-milage-gearbox) | 👏 No | Advanced search for car-listings. |
64
- | [`api.price_eval()`](#price_evalregistration_number) | 👏 No | Vehicle purchase valuation and details. |
65
- | [`api.home_search()`](#home_searchcity-type-order_by-ordering-offset) | 👏 No | Query home listings.
59
+ | Function | Token required | `as_objects` | Description |
60
+ |---|---|---|---|
61
+ | [`api.saved_searches()`](#saved_searches) | 🔐 Yes | - | List your saved searches (bevakningar) |
62
+ | [`api.get_listings()`](#get_listings) | 🔐 Yes | - | List items related to a saved search |
63
+ | [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
64
+ | [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
65
+ | [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
66
+ | [`api.home_search()`](#home_search) | 👏 No | - | Query home listings.
67
+ | [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
68
+ | [`api.get_store_listings()`](#get_store_listings) | 👏 No | - | Get listings from a specific store.
66
69
 
67
70
  ## 🤓 Detailed usage
68
71
 
69
- ### saved_searches()
72
+ ### saved_searches
70
73
 
71
74
  Saved searches are your so called "Bevakningar" and can be found [here](https://www.blocket.se/sparade/bevakningar). Each saved search has and unique `id` which can be used as a parameter to `get_listings()`, see below.
72
75
 
@@ -85,7 +88,7 @@ Saved searches are your so called "Bevakningar" and can be found [here](https://
85
88
  ]
86
89
  ```
87
90
 
88
- ### get_listings(search_id, limit)
91
+ ### get_listings
89
92
  Returns all listings related to a saved search.
90
93
 
91
94
  Parameters:
@@ -119,7 +122,7 @@ Parameters:
119
122
  }
120
123
  ```
121
124
 
122
- ### custom_search(search_query, region, limit)
125
+ ### custom_search
123
126
  Make a custom search through out all of blocked. A region can be passed in as parameter for filtering.
124
127
 
125
128
  Parameters:
@@ -127,6 +130,7 @@ Parameters:
127
130
  - `region` (`enum`, optional) - Filter results on a region, default is all of Sweden.
128
131
  - `category` (`enum`, optional) - Filter for a specific category, ex. `Category.for_hemmet`.
129
132
  - `limit` (`int`, optional) - Limit number of results returned, max is 99.
133
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
130
134
 
131
135
  ```py
132
136
  >>> from blocket_api import Region
@@ -160,7 +164,7 @@ Parameters:
160
164
  }
161
165
  ```
162
166
 
163
- ### motor_search(page, make, fuel, chassi, price, modelYear, milage, gearbox)
167
+ ### motor_search
164
168
  To query listings related to a specific car, supply the following parameters:
165
169
 
166
170
  - `page` (`int`, required) - Results are split in pages, set page number here.
@@ -171,6 +175,8 @@ To query listings related to a specific car, supply the following parameters:
171
175
  - `modelYear` (`Optional[Tuple[int, int]]`) - Set model year range, ex. `(2000, 2020)`.
172
176
  - `milage` (`Optional[Tuple[int, int]]`) - Set milage range, ex. `(1000, 2000)`.
173
177
  - `gearbox` (`Optional[GEARBOX_OPTIONS]`) - Filter a specific gearbox, ex. `Automat`.
178
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
179
+
174
180
  ```py
175
181
  >>> api.motor_search(
176
182
  make=["Audi", "Ford"],
@@ -182,7 +188,7 @@ To query listings related to a specific car, supply the following parameters:
182
188
  ...
183
189
  ```
184
190
 
185
- ### price_eval(registration_number)
191
+ ### price_eval
186
192
  Query price evaluation for a specific vehicle by using cars registration number (ABC123). This returns company and private estimated prices, car information, and more. The api queries same endpoint as Blockets ["värdera bil"](https://www.blocket.se/tjanster/vardera-bil) service.
187
193
 
188
194
  - `registration_number` (`str`, required) - Registration number of the vehicle.
@@ -201,7 +207,7 @@ Query price evaluation for a specific vehicle by using cars registration number
201
207
  }
202
208
  ```
203
209
 
204
- ### home_search(city, type, order_by, ordering, offset)
210
+ ### home_search
205
211
  Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
206
212
 
207
213
  - `city` (`str`, required) - City name, ex. Stockholm.
@@ -221,6 +227,50 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
221
227
  ...
222
228
  ```
223
229
 
230
+ ### store_search
231
+ Search for a store in Blocket stores from [blocket.se/butiker](https://www.blocket.se/butiker).
232
+
233
+ - `search_query` (`str`, required) - The name of the store.
234
+ - `page` (`int`, optional) - The page number to return.
235
+ - `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
236
+
237
+
238
+ ```py
239
+ >>> api.store_search("Jannes Car and Kebab")
240
+ {
241
+ "data": [
242
+ {
243
+ "store_id": "1234",
244
+ "store_name": "Jannes Car and Kebab",
245
+ ...
246
+ },
247
+ ...
248
+ ]
249
+ }
250
+ ```
251
+
252
+ ### get_store_listings
253
+ Get listings from a specific store.
254
+
255
+ - `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
256
+ - `page` (`int`, optional) - The page number to return.
257
+
258
+ ```py
259
+ >>> api.get_store_listings(1234)
260
+ {
261
+ "data": [
262
+ {
263
+ "ad_id": "1234",
264
+ "price": 316000,
265
+ "body": "A very nice car.",
266
+ "images": [],
267
+ ...
268
+ },
269
+ ...
270
+ ]
271
+ }
272
+ ```
273
+
224
274
  ## 🔐 Blocket API token
225
275
 
226
276
  There are two ways to acquire your token:
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  blocket_api/__init__.py
5
5
  blocket_api/blocket.py
6
+ blocket_api/models.py
6
7
  blocket_api/qasa.py
7
8
  blocket_api.egg-info/PKG-INFO
8
9
  blocket_api.egg-info/SOURCES.txt
@@ -3,4 +3,5 @@ pre-commit>=3.7.1
3
3
  pytest>=8.2.2
4
4
  respx>=0.21.1
5
5
  mypy>=1.15.0
6
- ruff>=0.12.3
6
+ pydantic>=2.11.7
7
+ ruff>=0.13.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "blocket_api"
3
- version = "0.2.7"
3
+ version = "0.2.9"
4
4
  description = "A python API wrapper for blocket.se"
5
5
  authors = [{name = "dunderrrrrr", email = "emil.bjorkroth@gmail.com"}]
6
6
  readme = "README.md"
@@ -11,7 +11,8 @@ dependencies = [
11
11
  "pytest>=8.2.2",
12
12
  "respx>=0.21.1",
13
13
  "mypy>=1.15.0",
14
- "ruff>=0.12.3",
14
+ "pydantic>=2.11.7",
15
+ "ruff>=0.13.0",
15
16
  ]
16
17
 
17
18
 
File without changes
File without changes