blocket-api 0.2.9__tar.gz → 0.3.1__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.
- {blocket_api-0.2.9 → blocket_api-0.3.1}/PKG-INFO +64 -7
- {blocket_api-0.2.9 → blocket_api-0.3.1}/README.md +62 -5
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api/blocket.py +94 -14
- blocket_api-0.3.1/blocket_api/models.py +592 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api.egg-info/PKG-INFO +64 -7
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api.egg-info/requires.txt +1 -1
- {blocket_api-0.2.9 → blocket_api-0.3.1}/pyproject.toml +2 -2
- blocket_api-0.2.9/blocket_api/models.py +0 -151
- {blocket_api-0.2.9 → blocket_api-0.3.1}/LICENSE +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api/__init__.py +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api/qasa.py +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api.egg-info/SOURCES.txt +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api.egg-info/dependency_links.txt +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/blocket_api.egg-info/top_level.txt +0 -0
- {blocket_api-0.2.9 → blocket_api-0.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blocket_api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -12,14 +12,14 @@ Requires-Dist: pytest>=8.2.2
|
|
|
12
12
|
Requires-Dist: respx>=0.21.1
|
|
13
13
|
Requires-Dist: mypy>=1.15.0
|
|
14
14
|
Requires-Dist: pydantic>=2.11.7
|
|
15
|
-
Requires-Dist: ruff>=0.13.
|
|
15
|
+
Requires-Dist: ruff>=0.13.1
|
|
16
16
|
Dynamic: license-file
|
|
17
17
|
|
|
18
18
|
# BlocketAPI
|
|
19
19
|
|
|
20
20
|
[](https://pypi.org/project/blocket_api/) [](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE)  
|
|
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
|
+
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 or as cleaned pydantic objects.
|
|
23
23
|
|
|
24
24
|
> Blocket is one of Sweden's largest online marketplaces. It was founded in 1996 and allows users to buy and sell a wide range of items, including cars, real estate, jobs, services, and second-hand goods. The platform is known for its extensive reach and user-friendly interface, making it a popular choice for Swedes looking to purchase or sell items quickly and efficiently.
|
|
25
25
|
|
|
@@ -31,15 +31,18 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
|
|
|
31
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
33
|
- Search for stores and get a specific store's listings.
|
|
34
|
+
- ...and more!
|
|
34
35
|
|
|
35
36
|
## 🧑💻️ Install
|
|
36
37
|
|
|
37
|
-
BlocketAPI
|
|
38
|
+
Install BlocketAPI via PyPI...
|
|
38
39
|
|
|
39
40
|
```sh
|
|
40
41
|
pip install blocket-api
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
45
|
+
|
|
43
46
|
## 💁♀️ Usage
|
|
44
47
|
|
|
45
48
|
```py
|
|
@@ -63,9 +66,12 @@ Some calls require a `bearerToken`. However, some calls are public and don't req
|
|
|
63
66
|
| [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
|
|
64
67
|
| [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
|
|
65
68
|
| [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
|
|
66
|
-
| [`api.home_search()`](#home_search) | 👏 No |
|
|
69
|
+
| [`api.home_search()`](#home_search) | 👏 No | Yes | Query home listings.
|
|
67
70
|
| [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
|
|
68
|
-
| [`api.get_store_listings()`](#get_store_listings) | 👏 No |
|
|
71
|
+
| [`api.get_store_listings()`](#get_store_listings) | 👏 No | Yes | Get listings from a specific store.
|
|
72
|
+
| [`api.get_ad_by_id()`](#get_ad_by_id) | 👏 No | Yes | Get ad data from id.
|
|
73
|
+
| [`api.get_user_by_id()`](#get_user_by_id) | 👏 No | Yes | Get user data from id.
|
|
74
|
+
|
|
69
75
|
|
|
70
76
|
## 🤓 Detailed usage
|
|
71
77
|
|
|
@@ -133,7 +139,7 @@ Parameters:
|
|
|
133
139
|
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
134
140
|
|
|
135
141
|
```py
|
|
136
|
-
>>> from blocket_api import Region
|
|
142
|
+
>>> from blocket_api.blocket import Region, Category
|
|
137
143
|
>>> api.custom_search("saab", Region.blekinge) # search for term "saab" in region of "Blekinge"
|
|
138
144
|
{
|
|
139
145
|
"data":[
|
|
@@ -215,6 +221,7 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
|
|
|
215
221
|
- `order_by` (`OrderBy`, optional) - Sorting order, ex. `OrderBy.price`.
|
|
216
222
|
- `ordering` (`HOME_SEARCH_ORDERING`, optional) - Sorting order, ex. `"descending"`.
|
|
217
223
|
- `offset` (`int`, optional) - Offset for results, ex. `60`.
|
|
224
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is False.
|
|
218
225
|
|
|
219
226
|
```py
|
|
220
227
|
>>> from blocket_api.qasa import HomeType, OrderBy
|
|
@@ -254,6 +261,8 @@ Get listings from a specific store.
|
|
|
254
261
|
|
|
255
262
|
- `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
|
|
256
263
|
- `page` (`int`, optional) - The page number to return.
|
|
264
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
265
|
+
|
|
257
266
|
|
|
258
267
|
```py
|
|
259
268
|
>>> api.get_store_listings(1234)
|
|
@@ -271,6 +280,53 @@ Get listings from a specific store.
|
|
|
271
280
|
}
|
|
272
281
|
```
|
|
273
282
|
|
|
283
|
+
### get_ad_by_id
|
|
284
|
+
Get ad data from a specific ad id.
|
|
285
|
+
|
|
286
|
+
- `ad_id` (`int`, required) - The ad id. Can be found by calling any other method or from ad url.
|
|
287
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
```py
|
|
291
|
+
>>> api.get_ad_by_id(1234)
|
|
292
|
+
{
|
|
293
|
+
"data": {
|
|
294
|
+
"ad_id": "1234",
|
|
295
|
+
"ad_status": "active",
|
|
296
|
+
"advertiser": {...},
|
|
297
|
+
"body": "A nice ad.",
|
|
298
|
+
"category": [
|
|
299
|
+
{"id": "4000", "name": "Personligt"},
|
|
300
|
+
{"id": "4080", "name": "Kläder & skor"},
|
|
301
|
+
],
|
|
302
|
+
...
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### get_user_by_id
|
|
308
|
+
Get user data from a user id. User id can be found in ads, searches and in blocket url `https://www.blocket.se/profil/<user_id>`.
|
|
309
|
+
|
|
310
|
+
- `user_id` (`int`, required) - The blocket user id.
|
|
311
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
```py
|
|
315
|
+
>>> api.get_user_by_id(1234)
|
|
316
|
+
{
|
|
317
|
+
'account': {
|
|
318
|
+
'blocket_account_id': '1234',
|
|
319
|
+
'created_at': '2020-10-10T15:45:06.407660',
|
|
320
|
+
'is_verified': True,
|
|
321
|
+
'name': 'Janne',
|
|
322
|
+
...
|
|
323
|
+
},
|
|
324
|
+
'active_ads': {[
|
|
325
|
+
...
|
|
326
|
+
]}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
274
330
|
## 🔐 Blocket API token
|
|
275
331
|
|
|
276
332
|
There are two ways to acquire your token:
|
|
@@ -295,5 +351,6 @@ My token has never expired or changed during this project. However, if your're m
|
|
|
295
351
|
|
|
296
352
|
## 📝 Notes
|
|
297
353
|
|
|
354
|
+
- REST API: https://blocket-api.se
|
|
298
355
|
- Source repo: https://github.com/dunderrrrrr/blocket_api
|
|
299
356
|
- PyPI: https://pypi.org/project/blocket-api/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/blocket_api/) [](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE)  
|
|
4
4
|
|
|
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.
|
|
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 or as cleaned pydantic objects.
|
|
6
6
|
|
|
7
7
|
> Blocket is one of Sweden's largest online marketplaces. It was founded in 1996 and allows users to buy and sell a wide range of items, including cars, real estate, jobs, services, and second-hand goods. The platform is known for its extensive reach and user-friendly interface, making it a popular choice for Swedes looking to purchase or sell items quickly and efficiently.
|
|
8
8
|
|
|
@@ -14,15 +14,18 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
|
|
|
14
14
|
- Get vehicle information, price evaluation and search for vehicles with multiple filters.
|
|
15
15
|
- Search for homes with filters for city, home type and more.
|
|
16
16
|
- Search for stores and get a specific store's listings.
|
|
17
|
+
- ...and more!
|
|
17
18
|
|
|
18
19
|
## 🧑💻️ Install
|
|
19
20
|
|
|
20
|
-
BlocketAPI
|
|
21
|
+
Install BlocketAPI via PyPI...
|
|
21
22
|
|
|
22
23
|
```sh
|
|
23
24
|
pip install blocket-api
|
|
24
25
|
```
|
|
25
26
|
|
|
27
|
+
or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
28
|
+
|
|
26
29
|
## 💁♀️ Usage
|
|
27
30
|
|
|
28
31
|
```py
|
|
@@ -46,9 +49,12 @@ Some calls require a `bearerToken`. However, some calls are public and don't req
|
|
|
46
49
|
| [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
|
|
47
50
|
| [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
|
|
48
51
|
| [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
|
|
49
|
-
| [`api.home_search()`](#home_search) | 👏 No |
|
|
52
|
+
| [`api.home_search()`](#home_search) | 👏 No | Yes | Query home listings.
|
|
50
53
|
| [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
|
|
51
|
-
| [`api.get_store_listings()`](#get_store_listings) | 👏 No |
|
|
54
|
+
| [`api.get_store_listings()`](#get_store_listings) | 👏 No | Yes | Get listings from a specific store.
|
|
55
|
+
| [`api.get_ad_by_id()`](#get_ad_by_id) | 👏 No | Yes | Get ad data from id.
|
|
56
|
+
| [`api.get_user_by_id()`](#get_user_by_id) | 👏 No | Yes | Get user data from id.
|
|
57
|
+
|
|
52
58
|
|
|
53
59
|
## 🤓 Detailed usage
|
|
54
60
|
|
|
@@ -116,7 +122,7 @@ Parameters:
|
|
|
116
122
|
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
117
123
|
|
|
118
124
|
```py
|
|
119
|
-
>>> from blocket_api import Region
|
|
125
|
+
>>> from blocket_api.blocket import Region, Category
|
|
120
126
|
>>> api.custom_search("saab", Region.blekinge) # search for term "saab" in region of "Blekinge"
|
|
121
127
|
{
|
|
122
128
|
"data":[
|
|
@@ -198,6 +204,7 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
|
|
|
198
204
|
- `order_by` (`OrderBy`, optional) - Sorting order, ex. `OrderBy.price`.
|
|
199
205
|
- `ordering` (`HOME_SEARCH_ORDERING`, optional) - Sorting order, ex. `"descending"`.
|
|
200
206
|
- `offset` (`int`, optional) - Offset for results, ex. `60`.
|
|
207
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is False.
|
|
201
208
|
|
|
202
209
|
```py
|
|
203
210
|
>>> from blocket_api.qasa import HomeType, OrderBy
|
|
@@ -237,6 +244,8 @@ Get listings from a specific store.
|
|
|
237
244
|
|
|
238
245
|
- `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
|
|
239
246
|
- `page` (`int`, optional) - The page number to return.
|
|
247
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
248
|
+
|
|
240
249
|
|
|
241
250
|
```py
|
|
242
251
|
>>> api.get_store_listings(1234)
|
|
@@ -254,6 +263,53 @@ Get listings from a specific store.
|
|
|
254
263
|
}
|
|
255
264
|
```
|
|
256
265
|
|
|
266
|
+
### get_ad_by_id
|
|
267
|
+
Get ad data from a specific ad id.
|
|
268
|
+
|
|
269
|
+
- `ad_id` (`int`, required) - The ad id. Can be found by calling any other method or from ad url.
|
|
270
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
```py
|
|
274
|
+
>>> api.get_ad_by_id(1234)
|
|
275
|
+
{
|
|
276
|
+
"data": {
|
|
277
|
+
"ad_id": "1234",
|
|
278
|
+
"ad_status": "active",
|
|
279
|
+
"advertiser": {...},
|
|
280
|
+
"body": "A nice ad.",
|
|
281
|
+
"category": [
|
|
282
|
+
{"id": "4000", "name": "Personligt"},
|
|
283
|
+
{"id": "4080", "name": "Kläder & skor"},
|
|
284
|
+
],
|
|
285
|
+
...
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### get_user_by_id
|
|
291
|
+
Get user data from a user id. User id can be found in ads, searches and in blocket url `https://www.blocket.se/profil/<user_id>`.
|
|
292
|
+
|
|
293
|
+
- `user_id` (`int`, required) - The blocket user id.
|
|
294
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
```py
|
|
298
|
+
>>> api.get_user_by_id(1234)
|
|
299
|
+
{
|
|
300
|
+
'account': {
|
|
301
|
+
'blocket_account_id': '1234',
|
|
302
|
+
'created_at': '2020-10-10T15:45:06.407660',
|
|
303
|
+
'is_verified': True,
|
|
304
|
+
'name': 'Janne',
|
|
305
|
+
...
|
|
306
|
+
},
|
|
307
|
+
'active_ads': {[
|
|
308
|
+
...
|
|
309
|
+
]}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
257
313
|
## 🔐 Blocket API token
|
|
258
314
|
|
|
259
315
|
There are two ways to acquire your token:
|
|
@@ -278,5 +334,6 @@ My token has never expired or changed during this project. However, if your're m
|
|
|
278
334
|
|
|
279
335
|
## 📝 Notes
|
|
280
336
|
|
|
337
|
+
- REST API: https://blocket-api.se
|
|
281
338
|
- Source repo: https://github.com/dunderrrrrr/blocket_api
|
|
282
339
|
- PyPI: https://pypi.org/project/blocket-api/
|
|
@@ -10,8 +10,12 @@ from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple, Union, ov
|
|
|
10
10
|
import httpx
|
|
11
11
|
|
|
12
12
|
from blocket_api.models import (
|
|
13
|
+
AdByIdResults,
|
|
14
|
+
BlocketUser,
|
|
13
15
|
CustomSearchResults,
|
|
16
|
+
HomeSearchResults,
|
|
14
17
|
MotorSearchResults,
|
|
18
|
+
StoreListings,
|
|
15
19
|
StoreSearchResults,
|
|
16
20
|
)
|
|
17
21
|
from blocket_api.qasa import HOME_SEARCH_ORDERING, HomeType, OrderBy, Qasa
|
|
@@ -403,6 +407,7 @@ class BlocketAPI:
|
|
|
403
407
|
url = f"{BYTBIL_URL}/blocket-basedata-api/v3/vehicle-data/{registration_number}"
|
|
404
408
|
return _make_request(url=f"{url}", token=None).json()
|
|
405
409
|
|
|
410
|
+
@overload
|
|
406
411
|
def home_search(
|
|
407
412
|
self,
|
|
408
413
|
city: str,
|
|
@@ -410,36 +415,54 @@ class BlocketAPI:
|
|
|
410
415
|
order_by: OrderBy = OrderBy.published_at,
|
|
411
416
|
ordering: HOME_SEARCH_ORDERING = "descending",
|
|
412
417
|
offset: int = 0,
|
|
413
|
-
|
|
418
|
+
*,
|
|
419
|
+
as_objects: Literal[True],
|
|
420
|
+
) -> HomeSearchResults: ...
|
|
421
|
+
|
|
422
|
+
@overload
|
|
423
|
+
def home_search(
|
|
424
|
+
self,
|
|
425
|
+
city: str,
|
|
426
|
+
type: HomeType,
|
|
427
|
+
order_by: OrderBy = OrderBy.published_at,
|
|
428
|
+
ordering: HOME_SEARCH_ORDERING = "descending",
|
|
429
|
+
offset: int = 0,
|
|
430
|
+
*,
|
|
431
|
+
as_objects: Literal[False] = False,
|
|
432
|
+
) -> dict: ...
|
|
433
|
+
|
|
434
|
+
def home_search(
|
|
435
|
+
self,
|
|
436
|
+
city: str,
|
|
437
|
+
type: HomeType,
|
|
438
|
+
order_by: OrderBy = OrderBy.published_at,
|
|
439
|
+
ordering: HOME_SEARCH_ORDERING = "descending",
|
|
440
|
+
offset: int = 0,
|
|
441
|
+
*,
|
|
442
|
+
as_objects: bool = False,
|
|
443
|
+
) -> dict | HomeSearchResults:
|
|
414
444
|
"""
|
|
415
445
|
This returns all available home listings available at
|
|
416
446
|
https://bostad.blocket.se/. Specify offset to get next page. Each page contains
|
|
417
447
|
60 items, which is max items returned per api query.
|
|
418
448
|
"""
|
|
419
|
-
|
|
449
|
+
response = Qasa(
|
|
420
450
|
city=city,
|
|
421
451
|
home_type=type,
|
|
422
452
|
order_by=order_by,
|
|
423
453
|
ordering=ordering,
|
|
424
454
|
offset=offset,
|
|
425
455
|
).search()
|
|
456
|
+
return HomeSearchResults.model_validate(response) if as_objects else response
|
|
426
457
|
|
|
427
458
|
@overload
|
|
428
459
|
def search_store(
|
|
429
|
-
self,
|
|
430
|
-
search_query: str,
|
|
431
|
-
page: int = 0,
|
|
432
|
-
*,
|
|
433
|
-
as_objects: Literal[True],
|
|
460
|
+
self, search_query: str, page: int = 0, *, as_objects: Literal[True]
|
|
434
461
|
) -> StoreSearchResults: ...
|
|
435
462
|
|
|
436
463
|
@overload
|
|
437
464
|
def search_store(
|
|
438
|
-
self,
|
|
439
|
-
search_query: str,
|
|
440
|
-
page: int = 0,
|
|
441
|
-
*,
|
|
442
|
-
as_objects: Literal[False] = False,
|
|
465
|
+
self, search_query: str, page: int = 0, *, as_objects: Literal[False] = False
|
|
443
466
|
) -> dict: ...
|
|
444
467
|
|
|
445
468
|
@overload
|
|
@@ -473,12 +496,24 @@ class BlocketAPI:
|
|
|
473
496
|
response = _make_request(url=f"{url}", token=self.token).json()
|
|
474
497
|
return StoreSearchResults.model_validate(response) if as_objects else response
|
|
475
498
|
|
|
499
|
+
@overload
|
|
500
|
+
def get_store_listings(
|
|
501
|
+
self, store_id: int, page: int = 0, *, as_objects: Literal[True]
|
|
502
|
+
) -> StoreListings: ...
|
|
503
|
+
|
|
504
|
+
@overload
|
|
505
|
+
def get_store_listings(
|
|
506
|
+
self, store_id: int, page: int = 0, *, as_objects: Literal[False] = False
|
|
507
|
+
) -> dict: ...
|
|
508
|
+
|
|
476
509
|
@public_token
|
|
477
510
|
def get_store_listings(
|
|
478
511
|
self,
|
|
479
512
|
store_id: int,
|
|
480
513
|
page: int = 0,
|
|
481
|
-
|
|
514
|
+
*,
|
|
515
|
+
as_objects: bool = False,
|
|
516
|
+
) -> dict | StoreListings:
|
|
482
517
|
"""
|
|
483
518
|
Return all listings from a specific store from https://www.blocket.se/butik/<store>.
|
|
484
519
|
The store_id can be found by searching for the store with search_store().
|
|
@@ -487,4 +522,49 @@ class BlocketAPI:
|
|
|
487
522
|
f"{BASE_URL}/search_bff/v2/content?lim=60&page={page}&sort=rel&store_id={store_id}"
|
|
488
523
|
"&status=active&gl=3&include=extend_with_shipping"
|
|
489
524
|
)
|
|
490
|
-
|
|
525
|
+
response = _make_request(url=f"{url}", token=self.token).json()
|
|
526
|
+
return StoreListings.model_validate(response) if as_objects else response
|
|
527
|
+
|
|
528
|
+
@overload
|
|
529
|
+
def get_ad_by_id(
|
|
530
|
+
self, ad_id: int, *, as_objects: Literal[True]
|
|
531
|
+
) -> AdByIdResults: ...
|
|
532
|
+
|
|
533
|
+
@overload
|
|
534
|
+
def get_ad_by_id(
|
|
535
|
+
self, ad_id: int, *, as_objects: Literal[False] = False
|
|
536
|
+
) -> dict: ...
|
|
537
|
+
|
|
538
|
+
@public_token
|
|
539
|
+
def get_ad_by_id(
|
|
540
|
+
self, ad_id: int, *, as_objects: bool = False
|
|
541
|
+
) -> dict | AdByIdResults:
|
|
542
|
+
"""
|
|
543
|
+
Return an ad with it's content. The ad_id can be found by searching or
|
|
544
|
+
from the blocket ad url.
|
|
545
|
+
"""
|
|
546
|
+
url = f"{BASE_URL}/search_bff/v2/content/{ad_id}"
|
|
547
|
+
response = _make_request(url=f"{url}", token=self.token).json()
|
|
548
|
+
return AdByIdResults.model_validate(response) if as_objects else response
|
|
549
|
+
|
|
550
|
+
@overload
|
|
551
|
+
def get_user_by_id(
|
|
552
|
+
self, user_id: int, *, as_objects: Literal[True]
|
|
553
|
+
) -> BlocketUser: ...
|
|
554
|
+
|
|
555
|
+
@overload
|
|
556
|
+
def get_user_by_id(
|
|
557
|
+
self, user_id: int, *, as_objects: Literal[False] = False
|
|
558
|
+
) -> dict: ...
|
|
559
|
+
|
|
560
|
+
@public_token
|
|
561
|
+
def get_user_by_id(
|
|
562
|
+
self, user_id: int, *, as_objects: bool = False
|
|
563
|
+
) -> dict | BlocketUser:
|
|
564
|
+
"""
|
|
565
|
+
Returns a user's profile data. Contains account information, ads and more.
|
|
566
|
+
Blocket url is https://www.blocket.se/profil/<user_id>.
|
|
567
|
+
"""
|
|
568
|
+
url = f"{BASE_URL}/profile-be/v1/public-profiles/{user_id}"
|
|
569
|
+
response = _make_request(url=f"{url}", token=self.token).json()
|
|
570
|
+
return BlocketUser.model_validate(response) if as_objects else response
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
### CustomSearch ###
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CustomSearchPrice(BaseModel):
|
|
10
|
+
suffix: str
|
|
11
|
+
value: int
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CustomSearchLocation(BaseModel):
|
|
15
|
+
image_url: str
|
|
16
|
+
label: str
|
|
17
|
+
query_key: str | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CustomSearchImage(BaseModel):
|
|
21
|
+
height: int
|
|
22
|
+
type: str
|
|
23
|
+
url: str
|
|
24
|
+
width: int
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CustomSearchCategory(BaseModel):
|
|
28
|
+
id: str
|
|
29
|
+
name: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CustomSearchResult(BaseModel):
|
|
33
|
+
ad_id: str
|
|
34
|
+
ad_status: str
|
|
35
|
+
body: str
|
|
36
|
+
category: list[CustomSearchCategory]
|
|
37
|
+
images: list[CustomSearchImage] = []
|
|
38
|
+
list_id: str
|
|
39
|
+
list_time: datetime.datetime
|
|
40
|
+
map: CustomSearchLocation | None = None
|
|
41
|
+
map_url: str | None = None
|
|
42
|
+
price: CustomSearchPrice
|
|
43
|
+
share_url: str
|
|
44
|
+
state_id: str
|
|
45
|
+
subject: str
|
|
46
|
+
type: str
|
|
47
|
+
zipcode: str | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CustomSearchResults(BaseModel):
|
|
51
|
+
data: list[CustomSearchResult]
|
|
52
|
+
total_count: int
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### MotorSearch ###
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class MotorSearchCarEquipment(BaseModel):
|
|
59
|
+
label: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MotorSearchCarLocation(BaseModel):
|
|
63
|
+
region: str
|
|
64
|
+
municipality: str
|
|
65
|
+
area: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MotorSearchCarImage(BaseModel):
|
|
69
|
+
height: int
|
|
70
|
+
width: int
|
|
71
|
+
image: str
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class MotorSearchCar(BaseModel):
|
|
75
|
+
images: list[MotorSearchCarImage]
|
|
76
|
+
location: MotorSearchCarLocation
|
|
77
|
+
fuel: str
|
|
78
|
+
gearbox: str
|
|
79
|
+
regDate: int
|
|
80
|
+
mileage: int
|
|
81
|
+
equipment: list[MotorSearchCarEquipment] | None = None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class MotorSearchPrice(BaseModel):
|
|
85
|
+
amount: str
|
|
86
|
+
billingPeriod: str
|
|
87
|
+
oldPrice: str | None = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class MotorSearchSeller(BaseModel):
|
|
91
|
+
type: str
|
|
92
|
+
name: str
|
|
93
|
+
id: str
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MotorSearchResult(BaseModel):
|
|
97
|
+
dealId: str
|
|
98
|
+
link: str
|
|
99
|
+
listTime: datetime.datetime
|
|
100
|
+
originalListTime: datetime.datetime
|
|
101
|
+
seller: MotorSearchSeller
|
|
102
|
+
heading: str
|
|
103
|
+
price: MotorSearchPrice
|
|
104
|
+
thumbnail: str
|
|
105
|
+
car: MotorSearchCar
|
|
106
|
+
description: str
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class MotorSearchResults(BaseModel):
|
|
110
|
+
cars: list[MotorSearchResult]
|
|
111
|
+
hits: int
|
|
112
|
+
pages: int
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
### StoreSearch ###
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class StoreSearchLocation(BaseModel):
|
|
119
|
+
longitude: float
|
|
120
|
+
latitude: float
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class StoreSearchImage(BaseModel):
|
|
124
|
+
height: int
|
|
125
|
+
width: int
|
|
126
|
+
url: str
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class StoreSearchLogotype(BaseModel):
|
|
130
|
+
height: int
|
|
131
|
+
width: int
|
|
132
|
+
url: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class StoreSearchResult(BaseModel):
|
|
136
|
+
address: str | None = None
|
|
137
|
+
id: str
|
|
138
|
+
logotype: StoreSearchLogotype
|
|
139
|
+
image: StoreSearchImage | None = None
|
|
140
|
+
description: str
|
|
141
|
+
external_store_url: str | None = None
|
|
142
|
+
location: StoreSearchLocation | None = None
|
|
143
|
+
name: str
|
|
144
|
+
title: str
|
|
145
|
+
ad_count: int
|
|
146
|
+
category: str
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class StoreSearchResults(BaseModel):
|
|
150
|
+
data: list[StoreSearchResult]
|
|
151
|
+
total_count: int
|
|
152
|
+
total_page_count: int
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
### HomeSearch ###
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class HomeSearchGeoPoint(BaseModel):
|
|
159
|
+
lat: float
|
|
160
|
+
lon: float
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class HomeSearchLocation(BaseModel):
|
|
164
|
+
id: int
|
|
165
|
+
locality: str
|
|
166
|
+
countryCode: str
|
|
167
|
+
streetNumber: str | None = None
|
|
168
|
+
point: HomeSearchGeoPoint
|
|
169
|
+
route: str
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class HomeSearchUpload(BaseModel):
|
|
173
|
+
id: int
|
|
174
|
+
order: int | None = None
|
|
175
|
+
type: str
|
|
176
|
+
url: str
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class HomeSearchResult(BaseModel):
|
|
180
|
+
bedroomCount: int | None = None
|
|
181
|
+
blockListing: bool
|
|
182
|
+
rentalLengthSeconds: float | None = None
|
|
183
|
+
householdSize: int
|
|
184
|
+
corporateHome: bool
|
|
185
|
+
description: str
|
|
186
|
+
endDate: str | None = None
|
|
187
|
+
firstHand: bool
|
|
188
|
+
furnished: bool
|
|
189
|
+
homeType: str
|
|
190
|
+
id: str
|
|
191
|
+
instantSign: bool
|
|
192
|
+
market: str
|
|
193
|
+
lastBumpedAt: str | None = None
|
|
194
|
+
monthlyCost: int
|
|
195
|
+
petsAllowed: bool
|
|
196
|
+
platform: str
|
|
197
|
+
publishedAt: str
|
|
198
|
+
publishedOrBumpedAt: str
|
|
199
|
+
earlyAccessEndsAt: str | None = None
|
|
200
|
+
rent: int
|
|
201
|
+
currency: str
|
|
202
|
+
roomCount: int
|
|
203
|
+
seniorHome: bool
|
|
204
|
+
shared: bool
|
|
205
|
+
shortcutHome: bool
|
|
206
|
+
smokingAllowed: bool
|
|
207
|
+
sortingScore: float
|
|
208
|
+
squareMeters: int
|
|
209
|
+
startDate: str
|
|
210
|
+
studentHome: bool
|
|
211
|
+
tenantBaseFee: int
|
|
212
|
+
title: str | None = None
|
|
213
|
+
wheelchairAccessible: bool
|
|
214
|
+
location: HomeSearchLocation
|
|
215
|
+
displayStreetNumber: bool
|
|
216
|
+
uploads: list[HomeSearchUpload]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class Documents(BaseModel):
|
|
220
|
+
hasNextPage: bool
|
|
221
|
+
hasPreviousPage: bool
|
|
222
|
+
nodes: list[HomeSearchResult]
|
|
223
|
+
pagesCount: int
|
|
224
|
+
totalCount: int
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class HomeIndexSearch(BaseModel):
|
|
228
|
+
documents: Documents
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class DataModel(BaseModel):
|
|
232
|
+
homeIndexSearch: HomeIndexSearch
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class HomeSearchResults(BaseModel):
|
|
236
|
+
data: DataModel
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
### StoreListings ###
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class StoreListingContactMethods(BaseModel):
|
|
243
|
+
phone: bool
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class StoreListingAdvertiser(BaseModel):
|
|
247
|
+
contact_methods: StoreListingContactMethods
|
|
248
|
+
name: str
|
|
249
|
+
store_name: str | None = None
|
|
250
|
+
type: str
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class StoreListingAttribute(BaseModel):
|
|
254
|
+
header: str
|
|
255
|
+
id: str
|
|
256
|
+
items: list[str]
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class StoreListingCategory(BaseModel):
|
|
260
|
+
id: str
|
|
261
|
+
name: str
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class StoreListingImage(BaseModel):
|
|
265
|
+
height: int
|
|
266
|
+
type: str
|
|
267
|
+
url: str
|
|
268
|
+
width: int
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class StorListingLocationItem(BaseModel):
|
|
272
|
+
id: str
|
|
273
|
+
name: str
|
|
274
|
+
query_key: str
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class StoreListingParameter(BaseModel):
|
|
278
|
+
id: str
|
|
279
|
+
label: str
|
|
280
|
+
value: str
|
|
281
|
+
suffix: str | None = None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class ParameterGroup(BaseModel):
|
|
285
|
+
label: str
|
|
286
|
+
parameters: list[StoreListingParameter]
|
|
287
|
+
type: str
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class StoreListingPartnerInfo(BaseModel):
|
|
291
|
+
external_id: str
|
|
292
|
+
name: str
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class StoreListingPrice(BaseModel):
|
|
296
|
+
suffix: str
|
|
297
|
+
value: int
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class StoreListing(BaseModel):
|
|
301
|
+
ad_id: str
|
|
302
|
+
ad_status: str
|
|
303
|
+
advertiser: StoreListingAdvertiser
|
|
304
|
+
attributes: list[StoreListingAttribute]
|
|
305
|
+
body: str
|
|
306
|
+
category: list[StoreListingCategory]
|
|
307
|
+
images: list[StoreListingImage]
|
|
308
|
+
license_plate: str | None = None
|
|
309
|
+
list_id: str
|
|
310
|
+
list_time: str
|
|
311
|
+
location: list[StorListingLocationItem]
|
|
312
|
+
map_url: str | None = None
|
|
313
|
+
parameter_groups: list[ParameterGroup]
|
|
314
|
+
partner_info: StoreListingPartnerInfo
|
|
315
|
+
price: StoreListingPrice
|
|
316
|
+
share_url: str
|
|
317
|
+
state_id: str
|
|
318
|
+
subject: str
|
|
319
|
+
type: str
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class StoreListings(BaseModel):
|
|
323
|
+
data: list[StoreListing]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
### GetAdById ###
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class AdByIdReviews(BaseModel):
|
|
330
|
+
overall_score: int
|
|
331
|
+
reviews_received_count: int
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class AdByIdPublicProfile(BaseModel):
|
|
335
|
+
account_created_at: str
|
|
336
|
+
badges: list[Any]
|
|
337
|
+
blocket_account_id: str
|
|
338
|
+
display_name: str
|
|
339
|
+
number_of_active_ads: int
|
|
340
|
+
reviews: AdByIdReviews
|
|
341
|
+
verified: bool
|
|
342
|
+
verified_label: str
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class AdByIdContactMethods(BaseModel):
|
|
346
|
+
mc: bool
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class AdByIdAdvertiser(BaseModel):
|
|
350
|
+
account_id: str
|
|
351
|
+
contact_methods: AdByIdContactMethods
|
|
352
|
+
name: str
|
|
353
|
+
public_profile: AdByIdPublicProfile
|
|
354
|
+
type: str
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
class AdByIdCategory(BaseModel):
|
|
358
|
+
id: str
|
|
359
|
+
name: str
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class AdByIdImage(BaseModel):
|
|
363
|
+
height: int
|
|
364
|
+
type: str
|
|
365
|
+
url: str
|
|
366
|
+
width: int
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class AdByIdLocation(BaseModel):
|
|
370
|
+
id: str
|
|
371
|
+
name: str
|
|
372
|
+
query_key: str
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class AdByIdMap(BaseModel):
|
|
376
|
+
image_url: str
|
|
377
|
+
label: str
|
|
378
|
+
type: str
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class AdByIdParameter(BaseModel):
|
|
382
|
+
id: str
|
|
383
|
+
label: str
|
|
384
|
+
value: str
|
|
385
|
+
suffix: str | None = None
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class AdByIdParameterGroup(BaseModel):
|
|
389
|
+
label: str
|
|
390
|
+
parameters: list[AdByIdParameter]
|
|
391
|
+
type: str
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
class AdByIdPrice(BaseModel):
|
|
395
|
+
suffix: str
|
|
396
|
+
value: int
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class AdByIdFeatures(BaseModel):
|
|
400
|
+
is_part_of_merge: bool
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class AdByIdUrls(BaseModel):
|
|
404
|
+
seller_activate: str
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
class AdByIdBlocketPackage(BaseModel):
|
|
408
|
+
activated_providers: list[Any]
|
|
409
|
+
ad_id: str
|
|
410
|
+
blocket_payment_backwards_compatability: Any
|
|
411
|
+
buyers_protection_fee: Any
|
|
412
|
+
eligible_providers: list[Any]
|
|
413
|
+
fail_code: Any
|
|
414
|
+
failure: Any
|
|
415
|
+
feature_swish: bool
|
|
416
|
+
features: AdByIdFeatures
|
|
417
|
+
is_in_dispute: bool
|
|
418
|
+
partner: str
|
|
419
|
+
payment_methods: list[Any]
|
|
420
|
+
payment_request_entrance: Any
|
|
421
|
+
payment_url: Any
|
|
422
|
+
service_data: Any
|
|
423
|
+
shipping_buy_now_entrance: Any
|
|
424
|
+
state: str
|
|
425
|
+
transaction_dates: Any
|
|
426
|
+
urls: AdByIdUrls
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class AdByIdGamApiKeywords(BaseModel):
|
|
430
|
+
adid: list[str]
|
|
431
|
+
adtitle: list[str]
|
|
432
|
+
adtype: list[str]
|
|
433
|
+
appmode: list[str]
|
|
434
|
+
blocket_section: list[str]
|
|
435
|
+
companyad: list[str]
|
|
436
|
+
country: list[str]
|
|
437
|
+
country_code: list[str]
|
|
438
|
+
county: list[str]
|
|
439
|
+
id: list[str]
|
|
440
|
+
itemimage: list[str]
|
|
441
|
+
itemsecuredthumb: list[str]
|
|
442
|
+
itemthumb: list[str]
|
|
443
|
+
livingarea: list[str] | None = None
|
|
444
|
+
lkf: list[str]
|
|
445
|
+
municipality: list[str]
|
|
446
|
+
nmp_vertical: list[str]
|
|
447
|
+
page: list[str]
|
|
448
|
+
price: list[str]
|
|
449
|
+
price_range_2: list[str] | None = None
|
|
450
|
+
publisher: list[str]
|
|
451
|
+
services: list[Any]
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class AdByIdClientKeywords(BaseModel):
|
|
455
|
+
cmp_advertising: str
|
|
456
|
+
logged_in: str
|
|
457
|
+
page_gen: str
|
|
458
|
+
ppid_1: str
|
|
459
|
+
ppid_2: str
|
|
460
|
+
screen_height: str
|
|
461
|
+
screen_width: str
|
|
462
|
+
site_mode: str
|
|
463
|
+
supply_type: str
|
|
464
|
+
viewport_height: str
|
|
465
|
+
viewport_width: str
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class AdByIdGamContext(BaseModel):
|
|
469
|
+
api_keywords: AdByIdGamApiKeywords
|
|
470
|
+
client_keywords: AdByIdClientKeywords
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class AdByIdContext(BaseModel):
|
|
474
|
+
gam: AdByIdGamContext
|
|
475
|
+
network_id: str | None = None
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class AdByIdInventory(BaseModel):
|
|
479
|
+
context: AdByIdContext
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
class AdByIdGamKeywords(BaseModel):
|
|
483
|
+
adformat: list[str]
|
|
484
|
+
adsize: list[str]
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class AdByIdGamMediaType(BaseModel):
|
|
488
|
+
height: int | None = None
|
|
489
|
+
type: str
|
|
490
|
+
width: int | None = None
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
class AdByIdGamPlacement(BaseModel):
|
|
494
|
+
keywords: AdByIdGamKeywords
|
|
495
|
+
media_types: list[AdByIdGamMediaType]
|
|
496
|
+
path: str
|
|
497
|
+
target_id: str
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
class AdByIdPlacement(BaseModel):
|
|
501
|
+
gam: AdByIdGamPlacement | None = None
|
|
502
|
+
type: str
|
|
503
|
+
vendor: str
|
|
504
|
+
viewport: str
|
|
505
|
+
safe_purchase: dict[str, Any] | None = None
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class AdByIdData(BaseModel):
|
|
509
|
+
ad_id: str
|
|
510
|
+
ad_status: str
|
|
511
|
+
advertiser: AdByIdAdvertiser
|
|
512
|
+
body: str
|
|
513
|
+
category: list[AdByIdCategory]
|
|
514
|
+
images: list[AdByIdImage]
|
|
515
|
+
latitude: float | None = None
|
|
516
|
+
list_id: str
|
|
517
|
+
list_time: str
|
|
518
|
+
location: list[AdByIdLocation]
|
|
519
|
+
longitude: float | None = None
|
|
520
|
+
map: AdByIdMap
|
|
521
|
+
map_url: str
|
|
522
|
+
parameter_groups: list[AdByIdParameterGroup]
|
|
523
|
+
price: AdByIdPrice
|
|
524
|
+
saved: bool
|
|
525
|
+
share_url: str
|
|
526
|
+
state_id: str
|
|
527
|
+
subject: str
|
|
528
|
+
type: str
|
|
529
|
+
zipcode: str
|
|
530
|
+
blocket_package: AdByIdBlocketPackage
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class AdByIdResults(BaseModel):
|
|
534
|
+
data: AdByIdData
|
|
535
|
+
inventory: AdByIdInventory
|
|
536
|
+
placements: list[AdByIdPlacement] | None = None
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
### GetUserById ###
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class BlocketUserAccount(BaseModel):
|
|
543
|
+
blocket_account_id: str
|
|
544
|
+
created_at: datetime.datetime
|
|
545
|
+
is_verified: bool
|
|
546
|
+
name: str
|
|
547
|
+
on_blocket_since: str
|
|
548
|
+
verified_with: str
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
class BlocketUserAd(BaseModel):
|
|
552
|
+
ad_id: str
|
|
553
|
+
ad_url: str
|
|
554
|
+
image_url: str
|
|
555
|
+
price: str
|
|
556
|
+
region: str
|
|
557
|
+
subject: str
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
class BlocketUserActiveAds(BaseModel):
|
|
561
|
+
ads: list[BlocketUserAd]
|
|
562
|
+
label_primary: str
|
|
563
|
+
label_secondary: str
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
class BlocketUserBadge(BaseModel):
|
|
567
|
+
description: str
|
|
568
|
+
icon: str
|
|
569
|
+
icon_blocket_ui: str
|
|
570
|
+
id: str
|
|
571
|
+
label: str
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class BlocketUserCO2Savings(BaseModel):
|
|
575
|
+
date_range: str
|
|
576
|
+
equivalent: str
|
|
577
|
+
text: str
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class BlocketUserProfileInfo(BaseModel):
|
|
581
|
+
description: str | None = None
|
|
582
|
+
has_reviews: bool
|
|
583
|
+
name: str
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
class BlocketUser(BaseModel):
|
|
587
|
+
account: BlocketUserAccount
|
|
588
|
+
active_ads: BlocketUserActiveAds
|
|
589
|
+
badges: list[BlocketUserBadge]
|
|
590
|
+
co2_savings: BlocketUserCO2Savings
|
|
591
|
+
profile_info: BlocketUserProfileInfo
|
|
592
|
+
review_scores: dict | None = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blocket_api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -12,14 +12,14 @@ Requires-Dist: pytest>=8.2.2
|
|
|
12
12
|
Requires-Dist: respx>=0.21.1
|
|
13
13
|
Requires-Dist: mypy>=1.15.0
|
|
14
14
|
Requires-Dist: pydantic>=2.11.7
|
|
15
|
-
Requires-Dist: ruff>=0.13.
|
|
15
|
+
Requires-Dist: ruff>=0.13.1
|
|
16
16
|
Dynamic: license-file
|
|
17
17
|
|
|
18
18
|
# BlocketAPI
|
|
19
19
|
|
|
20
20
|
[](https://pypi.org/project/blocket_api/) [](https://github.com/dunderrrrrr/blocket_api/blob/main/LICENSE)  
|
|
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
|
+
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 or as cleaned pydantic objects.
|
|
23
23
|
|
|
24
24
|
> Blocket is one of Sweden's largest online marketplaces. It was founded in 1996 and allows users to buy and sell a wide range of items, including cars, real estate, jobs, services, and second-hand goods. The platform is known for its extensive reach and user-friendly interface, making it a popular choice for Swedes looking to purchase or sell items quickly and efficiently.
|
|
25
25
|
|
|
@@ -31,15 +31,18 @@ BlocketAPI allows users to query saved searches, known as "Bevakningar", on [blo
|
|
|
31
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
33
|
- Search for stores and get a specific store's listings.
|
|
34
|
+
- ...and more!
|
|
34
35
|
|
|
35
36
|
## 🧑💻️ Install
|
|
36
37
|
|
|
37
|
-
BlocketAPI
|
|
38
|
+
Install BlocketAPI via PyPI...
|
|
38
39
|
|
|
39
40
|
```sh
|
|
40
41
|
pip install blocket-api
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
45
|
+
|
|
43
46
|
## 💁♀️ Usage
|
|
44
47
|
|
|
45
48
|
```py
|
|
@@ -63,9 +66,12 @@ Some calls require a `bearerToken`. However, some calls are public and don't req
|
|
|
63
66
|
| [`api.custom_search()`](#custom_search) | 👏 No | Yes | Search for everything on Blocket and filter by region |
|
|
64
67
|
| [`api.motor_search()`](#motor_search) | 👏 No | Yes | Advanced search for car-listings. |
|
|
65
68
|
| [`api.price_eval()`](#price_eval) | 👏 No | - | Vehicle purchase valuation and details. |
|
|
66
|
-
| [`api.home_search()`](#home_search) | 👏 No |
|
|
69
|
+
| [`api.home_search()`](#home_search) | 👏 No | Yes | Query home listings.
|
|
67
70
|
| [`api.store_search()`](#store_search) | 👏 No | Yes | Search for a store.
|
|
68
|
-
| [`api.get_store_listings()`](#get_store_listings) | 👏 No |
|
|
71
|
+
| [`api.get_store_listings()`](#get_store_listings) | 👏 No | Yes | Get listings from a specific store.
|
|
72
|
+
| [`api.get_ad_by_id()`](#get_ad_by_id) | 👏 No | Yes | Get ad data from id.
|
|
73
|
+
| [`api.get_user_by_id()`](#get_user_by_id) | 👏 No | Yes | Get user data from id.
|
|
74
|
+
|
|
69
75
|
|
|
70
76
|
## 🤓 Detailed usage
|
|
71
77
|
|
|
@@ -133,7 +139,7 @@ Parameters:
|
|
|
133
139
|
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
134
140
|
|
|
135
141
|
```py
|
|
136
|
-
>>> from blocket_api import Region
|
|
142
|
+
>>> from blocket_api.blocket import Region, Category
|
|
137
143
|
>>> api.custom_search("saab", Region.blekinge) # search for term "saab" in region of "Blekinge"
|
|
138
144
|
{
|
|
139
145
|
"data":[
|
|
@@ -215,6 +221,7 @@ Query home listings from [bostad.blocket.se](https://bostad.blocket.se/).
|
|
|
215
221
|
- `order_by` (`OrderBy`, optional) - Sorting order, ex. `OrderBy.price`.
|
|
216
222
|
- `ordering` (`HOME_SEARCH_ORDERING`, optional) - Sorting order, ex. `"descending"`.
|
|
217
223
|
- `offset` (`int`, optional) - Offset for results, ex. `60`.
|
|
224
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is False.
|
|
218
225
|
|
|
219
226
|
```py
|
|
220
227
|
>>> from blocket_api.qasa import HomeType, OrderBy
|
|
@@ -254,6 +261,8 @@ Get listings from a specific store.
|
|
|
254
261
|
|
|
255
262
|
- `store_id` (`int`, required) - The store id. Can be obtained by calling `store_search()`.
|
|
256
263
|
- `page` (`int`, optional) - The page number to return.
|
|
264
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
265
|
+
|
|
257
266
|
|
|
258
267
|
```py
|
|
259
268
|
>>> api.get_store_listings(1234)
|
|
@@ -271,6 +280,53 @@ Get listings from a specific store.
|
|
|
271
280
|
}
|
|
272
281
|
```
|
|
273
282
|
|
|
283
|
+
### get_ad_by_id
|
|
284
|
+
Get ad data from a specific ad id.
|
|
285
|
+
|
|
286
|
+
- `ad_id` (`int`, required) - The ad id. Can be found by calling any other method or from ad url.
|
|
287
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
```py
|
|
291
|
+
>>> api.get_ad_by_id(1234)
|
|
292
|
+
{
|
|
293
|
+
"data": {
|
|
294
|
+
"ad_id": "1234",
|
|
295
|
+
"ad_status": "active",
|
|
296
|
+
"advertiser": {...},
|
|
297
|
+
"body": "A nice ad.",
|
|
298
|
+
"category": [
|
|
299
|
+
{"id": "4000", "name": "Personligt"},
|
|
300
|
+
{"id": "4080", "name": "Kläder & skor"},
|
|
301
|
+
],
|
|
302
|
+
...
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### get_user_by_id
|
|
308
|
+
Get user data from a user id. User id can be found in ads, searches and in blocket url `https://www.blocket.se/profil/<user_id>`.
|
|
309
|
+
|
|
310
|
+
- `user_id` (`int`, required) - The blocket user id.
|
|
311
|
+
- `as_objects` (`bool`, optional) - Return results as pydantic models, default is `False`.
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
```py
|
|
315
|
+
>>> api.get_user_by_id(1234)
|
|
316
|
+
{
|
|
317
|
+
'account': {
|
|
318
|
+
'blocket_account_id': '1234',
|
|
319
|
+
'created_at': '2020-10-10T15:45:06.407660',
|
|
320
|
+
'is_verified': True,
|
|
321
|
+
'name': 'Janne',
|
|
322
|
+
...
|
|
323
|
+
},
|
|
324
|
+
'active_ads': {[
|
|
325
|
+
...
|
|
326
|
+
]}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
274
330
|
## 🔐 Blocket API token
|
|
275
331
|
|
|
276
332
|
There are two ways to acquire your token:
|
|
@@ -295,5 +351,6 @@ My token has never expired or changed during this project. However, if your're m
|
|
|
295
351
|
|
|
296
352
|
## 📝 Notes
|
|
297
353
|
|
|
354
|
+
- REST API: https://blocket-api.se
|
|
298
355
|
- Source repo: https://github.com/dunderrrrrr/blocket_api
|
|
299
356
|
- PyPI: https://pypi.org/project/blocket-api/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "blocket_api"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.1"
|
|
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"
|
|
@@ -12,7 +12,7 @@ dependencies = [
|
|
|
12
12
|
"respx>=0.21.1",
|
|
13
13
|
"mypy>=1.15.0",
|
|
14
14
|
"pydantic>=2.11.7",
|
|
15
|
-
"ruff>=0.13.
|
|
15
|
+
"ruff>=0.13.1",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
|
|
@@ -1,151 +0,0 @@
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|