blocket-toolkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build sdist and wheel
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
21
+ with:
22
+ enable-cache: false
23
+
24
+ - name: Build distributions
25
+ run: uv build
26
+
27
+ - name: Check metadata and README rendering
28
+ run: uvx twine check dist/*
29
+
30
+ - name: Upload distributions
31
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+ if-no-files-found: error
36
+
37
+ publish:
38
+ name: Publish to PyPI
39
+ needs: build
40
+ runs-on: ubuntu-latest
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/p/blocket-toolkit
44
+ permissions:
45
+ id-token: write
46
+ contents: read
47
+ steps:
48
+ - name: Download distributions
49
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+
54
+ - name: Install uv
55
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
56
+ with:
57
+ enable-cache: false
58
+
59
+ - name: Generate PEP 740 attestations
60
+ uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6
61
+
62
+ - name: Publish to PyPI
63
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .eggs/
8
+
9
+ # Environments
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Tooling
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # OS
22
+ .DS_Store
23
+
24
+ # Output
25
+ *.jsonl
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Hidefjäll
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.5
2
+ Name: blocket-toolkit
3
+ Version: 0.1.0
4
+ Summary: Search all of Blocket.se from the terminal: general items, cars, boats and motorcycles.
5
+ Project-URL: Homepage, https://github.com/dojje/blocket-toolkit
6
+ Project-URL: Repository, https://github.com/dojje/blocket-toolkit
7
+ Project-URL: Issues, https://github.com/dojje/blocket-toolkit/issues
8
+ Author: Daniel Hidefjäll
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: blocket,boats,cars,cli,marketplace,second-hand,sweden
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: blocket-api>=0.5.2
24
+ Requires-Dist: httpx>=0.28.1
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.6; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # blocket-toolkit
31
+
32
+ Search **all of Blocket.se** from the terminal: general items (*torget*), cars, boats
33
+ and motorcycles, with clean JSON output that is easy to pipe into other tools or hand
34
+ to an LLM.
35
+
36
+ Built on top of the [`blocket-api`](https://pypi.org/project/blocket_api/) Python
37
+ package.
38
+
39
+ ## Features
40
+
41
+ - **Everything on Blocket**: torget, cars, boats, motorcycles and full ad details.
42
+ - **Full filter surface**: category + subcategory, all 21 regions, price, year, milage,
43
+ horsepower, colour, gearbox, wheel drive, length, engine volume, brand/model.
44
+ - **AI-friendly output**: compacted, flat JSON by default; `jsonl` and text `table` when
45
+ you want them.
46
+ - **Self-documenting enums**: every category, subcategory, region, brand and model can
47
+ be listed or resolved by name *or* by Blocket's internal id.
48
+ - **Pagination**: `--page`, `-n/--limit`, or `--all`.
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ uv tool install blocket-toolkit # or: pip install blocket-toolkit
54
+ ```
55
+
56
+ From a checkout:
57
+
58
+ ```bash
59
+ git clone https://github.com/dojje/blocket-toolkit
60
+ cd blocket-toolkit
61
+ uv venv && uv pip install -e '.[dev]'
62
+ ```
63
+
64
+ ## Quick start
65
+
66
+ ```bash
67
+ blocket-toolkit search "kindle" --price-max 800 --sort price_asc
68
+
69
+ blocket-toolkit search "grafikkort" -c elektronik_och_vitvaror --sub-category datorer -n 5
70
+
71
+ blocket-toolkit cars --model volvo --year-min 2018 --mileage-max 12000 --sort mileage_asc
72
+
73
+ blocket-toolkit boats --type segelbat_motorseglare --length-min 30
74
+
75
+ blocket-toolkit mc --model yamaha --engine-min 600 --price-max 80000
76
+
77
+ blocket-toolkit ad 20851738 --type recommerce
78
+ ```
79
+
80
+ Every command accepts `-o json|jsonl|table` (default `json`).
81
+
82
+ ## Commands
83
+
84
+ | Command | What it does |
85
+ |---|---|
86
+ | `search <query>` | General items on torget |
87
+ | `cars [query]` | Used cars |
88
+ | `boats [query]` | Used boats |
89
+ | `mc [query]` | Used motorcycles |
90
+ | `ad <ad_id> --type ...` | Full details for one listing |
91
+ | `categories` | All 11 top-level categories |
92
+ | `subcategories [-c CATEGORY]` | All subcategories, optionally for one category |
93
+ | `locations` | All 21 Swedish regions |
94
+ | `car-options` | Car brands, colours, gearboxes, wheel drives, sort orders |
95
+ | `boat-options` | Boat types and sort orders |
96
+ | `mc-options` | MC brands, types and sort orders |
97
+
98
+ Run `blocket-toolkit <command> --help` for the full filter list.
99
+
100
+ ### Common flags
101
+
102
+ | Flag | Applies to | Meaning |
103
+ |---|---|---|
104
+ | `-c, --category` | `search` | Category name or id (e.g. `elektronik_och_vitvaror` or `0.93`) |
105
+ | `--sub-category` | `search` | Subcategory name or id (mutually exclusive with `--category`) |
106
+ | `-l, --location` | all searches | Region(s), repeatable or comma-separated (`-l stockholm,skane`) |
107
+ | `--sort` | all searches | Sort order; valid values depend on the command |
108
+ | `--price-min/--price-max` | `search`, `cars`, `boats`, `mc` | Price range in SEK |
109
+ | `--newer-than HOURS` | `search` | Only ads published within the last N hours |
110
+ | `--page`, `-n/--limit`, `--all` | all searches | Pagination |
111
+ | `-o, --output` | everything | `json` (default), `jsonl`, `table` |
112
+ | `--raw` | searches | Return Blocket's raw objects instead of compacted records |
113
+
114
+ > `--price-min/--price-max` and `--newer-than` are applied **client-side** for torget,
115
+ > because Blocket's general search endpoint has no server-side price filter. For cars,
116
+ > boats and motorcycles the price range is sent to the API.
117
+
118
+ ## Output shape
119
+
120
+ Search commands return:
121
+
122
+ ```json
123
+ {
124
+ "page": 1,
125
+ "last_page": 20,
126
+ "total": 1187,
127
+ "count": 40,
128
+ "items": [
129
+ {
130
+ "id": 20851738,
131
+ "heading": "Kindle Paperwhite 11th gen",
132
+ "price": 750,
133
+ "location": "Stockholm",
134
+ "url": "https://www.blocket.se/annons/20851738",
135
+ "image_url": "https://...",
136
+ "timestamp": 1700000000000,
137
+ "published_at": "2023-11-14T22:13:20+00:00"
138
+ }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ `ad` returns the full payload for a single listing.
144
+
145
+ ## Finding valid filter values
146
+
147
+ ```bash
148
+ blocket-toolkit categories -o table
149
+ blocket-toolkit subcategories -c elektronik_och_vitvaror -o table
150
+ blocket-toolkit locations -o table
151
+ blocket-toolkit car-options -o table
152
+ ```
153
+
154
+ Values are resolved case-insensitively and accept spaces or dashes, so
155
+ `ELEKTRONIK_OCH_VITVAROR`, `elektronik och vitvaror` and `0.93` are all valid.
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ uv venv && uv pip install -e '.[dev]'
161
+ pytest # unit tests (no network)
162
+ pytest -m live # smoke tests against the real Blocket API
163
+ ruff check .
164
+ ```
165
+
166
+ ## Notes and limitations
167
+
168
+ - Blocket has no official public API; this uses the same internal endpoints as the
169
+ website. Be gentle: don't hammer it with `--all --max-pages 200`.
170
+ - Torget's search endpoint does not accept a result count, so `-n/--limit` and
171
+ `--price-*` are applied after fetching.
172
+ - `ad` for `car`/`boat`/`mc` scrapes the mobility page and returns somewhat less
173
+ structured data than `recommerce`.
174
+
175
+ ## Credits
176
+
177
+ - [`blocket-api`](https://github.com/dunderrrrrr/blocket_api) by dunderrrrrr, the
178
+ underlying API wrapper.
179
+ - Blocket.se, obviously, for the data.
180
+
181
+ ## License
182
+
183
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,154 @@
1
+ # blocket-toolkit
2
+
3
+ Search **all of Blocket.se** from the terminal: general items (*torget*), cars, boats
4
+ and motorcycles, with clean JSON output that is easy to pipe into other tools or hand
5
+ to an LLM.
6
+
7
+ Built on top of the [`blocket-api`](https://pypi.org/project/blocket_api/) Python
8
+ package.
9
+
10
+ ## Features
11
+
12
+ - **Everything on Blocket**: torget, cars, boats, motorcycles and full ad details.
13
+ - **Full filter surface**: category + subcategory, all 21 regions, price, year, milage,
14
+ horsepower, colour, gearbox, wheel drive, length, engine volume, brand/model.
15
+ - **AI-friendly output**: compacted, flat JSON by default; `jsonl` and text `table` when
16
+ you want them.
17
+ - **Self-documenting enums**: every category, subcategory, region, brand and model can
18
+ be listed or resolved by name *or* by Blocket's internal id.
19
+ - **Pagination**: `--page`, `-n/--limit`, or `--all`.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ uv tool install blocket-toolkit # or: pip install blocket-toolkit
25
+ ```
26
+
27
+ From a checkout:
28
+
29
+ ```bash
30
+ git clone https://github.com/dojje/blocket-toolkit
31
+ cd blocket-toolkit
32
+ uv venv && uv pip install -e '.[dev]'
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ ```bash
38
+ blocket-toolkit search "kindle" --price-max 800 --sort price_asc
39
+
40
+ blocket-toolkit search "grafikkort" -c elektronik_och_vitvaror --sub-category datorer -n 5
41
+
42
+ blocket-toolkit cars --model volvo --year-min 2018 --mileage-max 12000 --sort mileage_asc
43
+
44
+ blocket-toolkit boats --type segelbat_motorseglare --length-min 30
45
+
46
+ blocket-toolkit mc --model yamaha --engine-min 600 --price-max 80000
47
+
48
+ blocket-toolkit ad 20851738 --type recommerce
49
+ ```
50
+
51
+ Every command accepts `-o json|jsonl|table` (default `json`).
52
+
53
+ ## Commands
54
+
55
+ | Command | What it does |
56
+ |---|---|
57
+ | `search <query>` | General items on torget |
58
+ | `cars [query]` | Used cars |
59
+ | `boats [query]` | Used boats |
60
+ | `mc [query]` | Used motorcycles |
61
+ | `ad <ad_id> --type ...` | Full details for one listing |
62
+ | `categories` | All 11 top-level categories |
63
+ | `subcategories [-c CATEGORY]` | All subcategories, optionally for one category |
64
+ | `locations` | All 21 Swedish regions |
65
+ | `car-options` | Car brands, colours, gearboxes, wheel drives, sort orders |
66
+ | `boat-options` | Boat types and sort orders |
67
+ | `mc-options` | MC brands, types and sort orders |
68
+
69
+ Run `blocket-toolkit <command> --help` for the full filter list.
70
+
71
+ ### Common flags
72
+
73
+ | Flag | Applies to | Meaning |
74
+ |---|---|---|
75
+ | `-c, --category` | `search` | Category name or id (e.g. `elektronik_och_vitvaror` or `0.93`) |
76
+ | `--sub-category` | `search` | Subcategory name or id (mutually exclusive with `--category`) |
77
+ | `-l, --location` | all searches | Region(s), repeatable or comma-separated (`-l stockholm,skane`) |
78
+ | `--sort` | all searches | Sort order; valid values depend on the command |
79
+ | `--price-min/--price-max` | `search`, `cars`, `boats`, `mc` | Price range in SEK |
80
+ | `--newer-than HOURS` | `search` | Only ads published within the last N hours |
81
+ | `--page`, `-n/--limit`, `--all` | all searches | Pagination |
82
+ | `-o, --output` | everything | `json` (default), `jsonl`, `table` |
83
+ | `--raw` | searches | Return Blocket's raw objects instead of compacted records |
84
+
85
+ > `--price-min/--price-max` and `--newer-than` are applied **client-side** for torget,
86
+ > because Blocket's general search endpoint has no server-side price filter. For cars,
87
+ > boats and motorcycles the price range is sent to the API.
88
+
89
+ ## Output shape
90
+
91
+ Search commands return:
92
+
93
+ ```json
94
+ {
95
+ "page": 1,
96
+ "last_page": 20,
97
+ "total": 1187,
98
+ "count": 40,
99
+ "items": [
100
+ {
101
+ "id": 20851738,
102
+ "heading": "Kindle Paperwhite 11th gen",
103
+ "price": 750,
104
+ "location": "Stockholm",
105
+ "url": "https://www.blocket.se/annons/20851738",
106
+ "image_url": "https://...",
107
+ "timestamp": 1700000000000,
108
+ "published_at": "2023-11-14T22:13:20+00:00"
109
+ }
110
+ ]
111
+ }
112
+ ```
113
+
114
+ `ad` returns the full payload for a single listing.
115
+
116
+ ## Finding valid filter values
117
+
118
+ ```bash
119
+ blocket-toolkit categories -o table
120
+ blocket-toolkit subcategories -c elektronik_och_vitvaror -o table
121
+ blocket-toolkit locations -o table
122
+ blocket-toolkit car-options -o table
123
+ ```
124
+
125
+ Values are resolved case-insensitively and accept spaces or dashes, so
126
+ `ELEKTRONIK_OCH_VITVAROR`, `elektronik och vitvaror` and `0.93` are all valid.
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ uv venv && uv pip install -e '.[dev]'
132
+ pytest # unit tests (no network)
133
+ pytest -m live # smoke tests against the real Blocket API
134
+ ruff check .
135
+ ```
136
+
137
+ ## Notes and limitations
138
+
139
+ - Blocket has no official public API; this uses the same internal endpoints as the
140
+ website. Be gentle: don't hammer it with `--all --max-pages 200`.
141
+ - Torget's search endpoint does not accept a result count, so `-n/--limit` and
142
+ `--price-*` are applied after fetching.
143
+ - `ad` for `car`/`boat`/`mc` scrapes the mobility page and returns somewhat less
144
+ structured data than `recommerce`.
145
+
146
+ ## Credits
147
+
148
+ - [`blocket-api`](https://github.com/dunderrrrrr/blocket_api) by dunderrrrrr, the
149
+ underlying API wrapper.
150
+ - Blocket.se, obviously, for the data.
151
+
152
+ ## License
153
+
154
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "blocket-toolkit"
7
+ version = "0.1.0"
8
+ description = "Search all of Blocket.se from the terminal: general items, cars, boats and motorcycles."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Daniel Hidefjäll" }]
14
+ keywords = ["blocket", "cli", "marketplace", "sweden", "second-hand", "cars", "boats"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Utilities",
26
+ ]
27
+ dependencies = ["blocket-api>=0.5.2", "httpx>=0.28.1"]
28
+
29
+ [project.optional-dependencies]
30
+ dev = ["pytest>=8.0", "ruff>=0.6"]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/dojje/blocket-toolkit"
34
+ Repository = "https://github.com/dojje/blocket-toolkit"
35
+ Issues = "https://github.com/dojje/blocket-toolkit/issues"
36
+
37
+ [project.scripts]
38
+ blocket-toolkit = "blocket_toolkit.cli:main"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/blocket_toolkit"]
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+ markers = ["live: tests that hit the real Blocket API (deselected by default)"]
46
+ addopts = "-m 'not live'"
47
+
48
+ [tool.ruff]
49
+ line-length = 100
50
+ target-version = "py310"
51
+
52
+ [tool.ruff.lint]
53
+ select = ["E", "F", "I", "UP", "B"]