trawl-api 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,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .env
10
+ *.csv
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ This project follows [Semantic Versioning](https://semver.org).
4
+
5
+ ## 0.1.0 (2026-10-06)
6
+
7
+ - First release: `Trawl` and `AsyncTrawl` clients with `ebay.sold`, `ebay.item` and
8
+ `ebay.categories`, typed responses, typed errors and automatic retries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 trawl
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,273 @@
1
+ Metadata-Version: 2.5
2
+ Name: trawl-api
3
+ Version: 0.1.0
4
+ Summary: Python eBay scraper API: sold listings, prices and item details from eBay as JSON
5
+ Project-URL: Homepage, https://trawl.dev
6
+ Project-URL: Documentation, https://trawl.dev/docs
7
+ Project-URL: Pricing, https://trawl.dev/pricing
8
+ Project-URL: Repository, https://github.com/trawl-inc/trawl-python
9
+ Project-URL: Changelog, https://github.com/trawl-inc/trawl-python/blob/main/CHANGELOG.md
10
+ Author: trawl
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: ebay,ebay api,ebay price data,ebay scraper,ebay sold listings,scraper,sold prices,trawl
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Internet :: WWW/HTTP
24
+ Classifier: Topic :: Office/Business :: Financial
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: httpx<1,>=0.25
29
+ Requires-Dist: pydantic<3,>=2.5
30
+ Description-Content-Type: text/markdown
31
+
32
+ # trawl-api: Python eBay scraper API for sold listings and prices
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/trawl-api)](https://pypi.org/project/trawl-api/)
35
+ [![Python](https://img.shields.io/pypi/pyversions/trawl-api)](https://pypi.org/project/trawl-api/)
36
+
37
+ The official Python client for [trawl](https://trawl.dev), a data API for eBay. Search 300+
38
+ million completed eBay sales on the US and UK marketplaces and get each one back as typed
39
+ Python objects: final price, sale date, condition, shipping, seller, item specifics, images
40
+ and description. One API key, no proxies, no HTML parsing, no browser to run.
41
+
42
+ ```bash
43
+ pip install trawl-api
44
+ ```
45
+
46
+ Requires Python 3.10 or newer. [Create a free account](https://trawl.dev/signup) to get an
47
+ API key; the free plan needs no card.
48
+
49
+ ## Quickstart
50
+
51
+ ```python
52
+ from trawl_api import Trawl
53
+
54
+ client = Trawl() # reads the TRAWL_API_KEY environment variable
55
+
56
+ sold = client.ebay.sold("iphone 15 pro 256gb", condition="used")
57
+
58
+ for listing in sold.results[:5]:
59
+ print(listing.date_sold.date(), listing.sale_price, listing.title)
60
+
61
+ print(f"{sold.count} results, {sold.credits_charged} credit(s) charged")
62
+ ```
63
+
64
+ The key can also be passed directly: `Trawl(api_key="sk_live_...")`. Keep it on your server
65
+ and out of source control.
66
+
67
+ ## What you can ask
68
+
69
+ | Method | Answers | Costs |
70
+ |---|---|---|
71
+ | `client.ebay.sold(query, ...)` | Sold listings matching a query, newest first, up to 2,000 in one response | 1 credit per page of 100 results (2 with `details=True`); free when nothing matches |
72
+ | `client.ebay.item(item_id)` | One sold listing in full: specifics, seller, every image, description, recorded sales | 1 credit |
73
+ | `client.ebay.categories(query)` | eBay categories by name or id, for the `category` filter | 1 credit; free when nothing matches |
74
+
75
+ The full parameter and field reference is at [trawl.dev/docs](https://trawl.dev/docs).
76
+
77
+ ## Tutorials
78
+
79
+ Each of these is a complete script in
80
+ [`examples/`](https://github.com/trawl-inc/trawl-python/tree/main/examples).
81
+
82
+ ### Get the sold prices of a product
83
+
84
+ ```python
85
+ from trawl_api import Trawl
86
+
87
+ client = Trawl()
88
+ sold = client.ebay.sold(
89
+ "iphone 15 pro 256gb",
90
+ condition="used",
91
+ exclude=["case", "cracked"], # words that must not be in the title
92
+ )
93
+
94
+ for listing in sold.results:
95
+ print(
96
+ f"{listing.date_sold:%Y-%m-%d} {listing.currency}{listing.sale_price:.2f} {listing.title}"
97
+ )
98
+ ```
99
+
100
+ ### Average sold price over the last 90 days
101
+
102
+ `max_pages` sets how many pages of 100 results come back in the one response, and is also the
103
+ most credits the call can cost.
104
+
105
+ ```python
106
+ from datetime import date, timedelta
107
+ from statistics import mean, median
108
+
109
+ from trawl_api import Trawl
110
+
111
+ client = Trawl()
112
+ sold = client.ebay.sold(
113
+ "nintendo switch oled",
114
+ condition="used",
115
+ date_from=date.today() - timedelta(days=90),
116
+ max_pages=5,
117
+ )
118
+
119
+ prices = [listing.sale_price for listing in sold.results]
120
+ print(
121
+ f"{len(prices)} sales, average {mean(prices):.2f}, median {median(prices):.2f} {sold.currency}"
122
+ )
123
+ ```
124
+
125
+ ### Filter by price, marketplace and item specifics
126
+
127
+ ```python
128
+ sold = client.ebay.sold(
129
+ "charizard",
130
+ site="EBAY_GB", # ebay.co.uk; prices are then in GBP
131
+ min_price=100,
132
+ max_price=2000,
133
+ attr={"Set": "Base Set", "Grade": ["9", "10"]}, # Grade 9 or 10, from Base Set
134
+ )
135
+ ```
136
+
137
+ ### Export sold listings to CSV or pandas
138
+
139
+ ```python
140
+ import csv
141
+
142
+ from trawl_api import Trawl
143
+
144
+ COLUMNS = ["date_sold", "title", "sale_price", "currency", "condition", "item_id", "item_link"]
145
+
146
+ client = Trawl()
147
+ sold = client.ebay.sold("charizard base set holo", max_pages=10)
148
+
149
+ with open("sold.csv", "w", newline="", encoding="utf-8") as file:
150
+ writer = csv.DictWriter(file, fieldnames=COLUMNS)
151
+ writer.writeheader()
152
+ for listing in sold.results:
153
+ writer.writerow(listing.model_dump(mode="json", include=set(COLUMNS)))
154
+ ```
155
+
156
+ For pandas: `pd.DataFrame(listing.model_dump() for listing in sold.results)`.
157
+
158
+ ### Get one listing's full details
159
+
160
+ ```python
161
+ from trawl_api import NotFoundError, Trawl
162
+
163
+ client = Trawl()
164
+
165
+ try:
166
+ item = client.ebay.item("256637082114")
167
+ except NotFoundError:
168
+ # Details arrive a few minutes after a sale. A 404 is never billed.
169
+ raise SystemExit("Details are not available yet.")
170
+
171
+ print(item.title, item.sale_price, item.currency)
172
+ print(item.specifics["Brand"]) # item specifics as a dict
173
+ print(item.seller.username, item.seller.feedback_percent)
174
+ print(len(item.images), "images")
175
+ ```
176
+
177
+ To get the details of every result of a search in one call, pass `details=True` to `sold()`
178
+ and read `listing.details`.
179
+
180
+ ### Find a category and search inside it
181
+
182
+ ```python
183
+ found = client.ebay.categories("trading card singles", site="EBAY_US")
184
+ for category in found.categories:
185
+ print(category.category_id, category.name, category.group)
186
+
187
+ sold = client.ebay.sold("charizard", category=found.categories[0].category_id)
188
+ ```
189
+
190
+ Category ids differ per marketplace, so look them up with the same `site` you search with.
191
+
192
+ ## Credits
193
+
194
+ Every answer says what it cost, and how much of the month's allowance is left:
195
+
196
+ ```python
197
+ sold = client.ebay.sold("rolex submariner", max_pages=3)
198
+
199
+ sold.credits_charged # 3 when three pages came back, 0 when nothing matched
200
+ sold.rate_limit.remaining # credits left in this billing window
201
+ sold.rate_limit.reset # when the allowance resets
202
+ ```
203
+
204
+ Errors are never billed.
205
+
206
+ ## Errors
207
+
208
+ ```python
209
+ import trawl_api
210
+
211
+ try:
212
+ sold = client.ebay.sold("rolex submariner", min_price=2000)
213
+ except trawl_api.BadRequestError as error:
214
+ print(error) # the API names the field and the rule it broke
215
+ except trawl_api.InsufficientCreditsError as error:
216
+ print(error) # the month's credits are spent, or too few remain
217
+ except trawl_api.RateLimitError as error:
218
+ print(error.retry_after) # seconds to wait
219
+ except trawl_api.APIStatusError as error:
220
+ print(error.status_code, error)
221
+ ```
222
+
223
+ | Exception | When |
224
+ |---|---|
225
+ | `BadRequestError` | 400, a parameter failed validation |
226
+ | `AuthenticationError` | 403, the key is missing, invalid or deleted |
227
+ | `NotFoundError` | 404, e.g. an item whose details are not available yet |
228
+ | `RateLimitError` | 429 with `Retry-After`, the plan's per-second rate was exceeded |
229
+ | `InsufficientCreditsError` | 429 without `Retry-After`, not enough credits for the request |
230
+ | `ServerError` | 5xx, a failure on trawl's side |
231
+ | `APIConnectionError`, `APITimeoutError` | the request got no answer |
232
+
233
+ All of them inherit from `trawl_api.TrawlError`. Connection errors, 5xx answers and
234
+ per-second rate limits are retried twice with backoff before they are raised; change that
235
+ with `Trawl(max_retries=...)`.
236
+
237
+ ## Async
238
+
239
+ ```python
240
+ import asyncio
241
+
242
+ from trawl_api import AsyncTrawl
243
+
244
+
245
+ async def main():
246
+ async with AsyncTrawl() as client:
247
+ sold = await client.ebay.sold("iphone 15 pro 256gb")
248
+ print(sold.count)
249
+
250
+
251
+ asyncio.run(main())
252
+ ```
253
+
254
+ ## Configuration
255
+
256
+ ```python
257
+ client = Trawl(
258
+ api_key="sk_live_...", # default: the TRAWL_API_KEY environment variable
259
+ timeout=60.0, # seconds
260
+ max_retries=2,
261
+ )
262
+ ```
263
+
264
+ Responses are [pydantic](https://docs.pydantic.dev) models: `model_dump()` gives a dict,
265
+ `model_dump_json()` a JSON string, and fields the API adds later are kept on the object.
266
+
267
+ ## Links
268
+
269
+ - [Documentation](https://trawl.dev/docs)
270
+ - [Pricing](https://trawl.dev/pricing)
271
+ - [Changelog](https://github.com/trawl-inc/trawl-python/blob/main/CHANGELOG.md)
272
+
273
+ trawl is an independent service and is not affiliated with or endorsed by eBay Inc.
@@ -0,0 +1,242 @@
1
+ # trawl-api: Python eBay scraper API for sold listings and prices
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/trawl-api)](https://pypi.org/project/trawl-api/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/trawl-api)](https://pypi.org/project/trawl-api/)
5
+
6
+ The official Python client for [trawl](https://trawl.dev), a data API for eBay. Search 300+
7
+ million completed eBay sales on the US and UK marketplaces and get each one back as typed
8
+ Python objects: final price, sale date, condition, shipping, seller, item specifics, images
9
+ and description. One API key, no proxies, no HTML parsing, no browser to run.
10
+
11
+ ```bash
12
+ pip install trawl-api
13
+ ```
14
+
15
+ Requires Python 3.10 or newer. [Create a free account](https://trawl.dev/signup) to get an
16
+ API key; the free plan needs no card.
17
+
18
+ ## Quickstart
19
+
20
+ ```python
21
+ from trawl_api import Trawl
22
+
23
+ client = Trawl() # reads the TRAWL_API_KEY environment variable
24
+
25
+ sold = client.ebay.sold("iphone 15 pro 256gb", condition="used")
26
+
27
+ for listing in sold.results[:5]:
28
+ print(listing.date_sold.date(), listing.sale_price, listing.title)
29
+
30
+ print(f"{sold.count} results, {sold.credits_charged} credit(s) charged")
31
+ ```
32
+
33
+ The key can also be passed directly: `Trawl(api_key="sk_live_...")`. Keep it on your server
34
+ and out of source control.
35
+
36
+ ## What you can ask
37
+
38
+ | Method | Answers | Costs |
39
+ |---|---|---|
40
+ | `client.ebay.sold(query, ...)` | Sold listings matching a query, newest first, up to 2,000 in one response | 1 credit per page of 100 results (2 with `details=True`); free when nothing matches |
41
+ | `client.ebay.item(item_id)` | One sold listing in full: specifics, seller, every image, description, recorded sales | 1 credit |
42
+ | `client.ebay.categories(query)` | eBay categories by name or id, for the `category` filter | 1 credit; free when nothing matches |
43
+
44
+ The full parameter and field reference is at [trawl.dev/docs](https://trawl.dev/docs).
45
+
46
+ ## Tutorials
47
+
48
+ Each of these is a complete script in
49
+ [`examples/`](https://github.com/trawl-inc/trawl-python/tree/main/examples).
50
+
51
+ ### Get the sold prices of a product
52
+
53
+ ```python
54
+ from trawl_api import Trawl
55
+
56
+ client = Trawl()
57
+ sold = client.ebay.sold(
58
+ "iphone 15 pro 256gb",
59
+ condition="used",
60
+ exclude=["case", "cracked"], # words that must not be in the title
61
+ )
62
+
63
+ for listing in sold.results:
64
+ print(
65
+ f"{listing.date_sold:%Y-%m-%d} {listing.currency}{listing.sale_price:.2f} {listing.title}"
66
+ )
67
+ ```
68
+
69
+ ### Average sold price over the last 90 days
70
+
71
+ `max_pages` sets how many pages of 100 results come back in the one response, and is also the
72
+ most credits the call can cost.
73
+
74
+ ```python
75
+ from datetime import date, timedelta
76
+ from statistics import mean, median
77
+
78
+ from trawl_api import Trawl
79
+
80
+ client = Trawl()
81
+ sold = client.ebay.sold(
82
+ "nintendo switch oled",
83
+ condition="used",
84
+ date_from=date.today() - timedelta(days=90),
85
+ max_pages=5,
86
+ )
87
+
88
+ prices = [listing.sale_price for listing in sold.results]
89
+ print(
90
+ f"{len(prices)} sales, average {mean(prices):.2f}, median {median(prices):.2f} {sold.currency}"
91
+ )
92
+ ```
93
+
94
+ ### Filter by price, marketplace and item specifics
95
+
96
+ ```python
97
+ sold = client.ebay.sold(
98
+ "charizard",
99
+ site="EBAY_GB", # ebay.co.uk; prices are then in GBP
100
+ min_price=100,
101
+ max_price=2000,
102
+ attr={"Set": "Base Set", "Grade": ["9", "10"]}, # Grade 9 or 10, from Base Set
103
+ )
104
+ ```
105
+
106
+ ### Export sold listings to CSV or pandas
107
+
108
+ ```python
109
+ import csv
110
+
111
+ from trawl_api import Trawl
112
+
113
+ COLUMNS = ["date_sold", "title", "sale_price", "currency", "condition", "item_id", "item_link"]
114
+
115
+ client = Trawl()
116
+ sold = client.ebay.sold("charizard base set holo", max_pages=10)
117
+
118
+ with open("sold.csv", "w", newline="", encoding="utf-8") as file:
119
+ writer = csv.DictWriter(file, fieldnames=COLUMNS)
120
+ writer.writeheader()
121
+ for listing in sold.results:
122
+ writer.writerow(listing.model_dump(mode="json", include=set(COLUMNS)))
123
+ ```
124
+
125
+ For pandas: `pd.DataFrame(listing.model_dump() for listing in sold.results)`.
126
+
127
+ ### Get one listing's full details
128
+
129
+ ```python
130
+ from trawl_api import NotFoundError, Trawl
131
+
132
+ client = Trawl()
133
+
134
+ try:
135
+ item = client.ebay.item("256637082114")
136
+ except NotFoundError:
137
+ # Details arrive a few minutes after a sale. A 404 is never billed.
138
+ raise SystemExit("Details are not available yet.")
139
+
140
+ print(item.title, item.sale_price, item.currency)
141
+ print(item.specifics["Brand"]) # item specifics as a dict
142
+ print(item.seller.username, item.seller.feedback_percent)
143
+ print(len(item.images), "images")
144
+ ```
145
+
146
+ To get the details of every result of a search in one call, pass `details=True` to `sold()`
147
+ and read `listing.details`.
148
+
149
+ ### Find a category and search inside it
150
+
151
+ ```python
152
+ found = client.ebay.categories("trading card singles", site="EBAY_US")
153
+ for category in found.categories:
154
+ print(category.category_id, category.name, category.group)
155
+
156
+ sold = client.ebay.sold("charizard", category=found.categories[0].category_id)
157
+ ```
158
+
159
+ Category ids differ per marketplace, so look them up with the same `site` you search with.
160
+
161
+ ## Credits
162
+
163
+ Every answer says what it cost, and how much of the month's allowance is left:
164
+
165
+ ```python
166
+ sold = client.ebay.sold("rolex submariner", max_pages=3)
167
+
168
+ sold.credits_charged # 3 when three pages came back, 0 when nothing matched
169
+ sold.rate_limit.remaining # credits left in this billing window
170
+ sold.rate_limit.reset # when the allowance resets
171
+ ```
172
+
173
+ Errors are never billed.
174
+
175
+ ## Errors
176
+
177
+ ```python
178
+ import trawl_api
179
+
180
+ try:
181
+ sold = client.ebay.sold("rolex submariner", min_price=2000)
182
+ except trawl_api.BadRequestError as error:
183
+ print(error) # the API names the field and the rule it broke
184
+ except trawl_api.InsufficientCreditsError as error:
185
+ print(error) # the month's credits are spent, or too few remain
186
+ except trawl_api.RateLimitError as error:
187
+ print(error.retry_after) # seconds to wait
188
+ except trawl_api.APIStatusError as error:
189
+ print(error.status_code, error)
190
+ ```
191
+
192
+ | Exception | When |
193
+ |---|---|
194
+ | `BadRequestError` | 400, a parameter failed validation |
195
+ | `AuthenticationError` | 403, the key is missing, invalid or deleted |
196
+ | `NotFoundError` | 404, e.g. an item whose details are not available yet |
197
+ | `RateLimitError` | 429 with `Retry-After`, the plan's per-second rate was exceeded |
198
+ | `InsufficientCreditsError` | 429 without `Retry-After`, not enough credits for the request |
199
+ | `ServerError` | 5xx, a failure on trawl's side |
200
+ | `APIConnectionError`, `APITimeoutError` | the request got no answer |
201
+
202
+ All of them inherit from `trawl_api.TrawlError`. Connection errors, 5xx answers and
203
+ per-second rate limits are retried twice with backoff before they are raised; change that
204
+ with `Trawl(max_retries=...)`.
205
+
206
+ ## Async
207
+
208
+ ```python
209
+ import asyncio
210
+
211
+ from trawl_api import AsyncTrawl
212
+
213
+
214
+ async def main():
215
+ async with AsyncTrawl() as client:
216
+ sold = await client.ebay.sold("iphone 15 pro 256gb")
217
+ print(sold.count)
218
+
219
+
220
+ asyncio.run(main())
221
+ ```
222
+
223
+ ## Configuration
224
+
225
+ ```python
226
+ client = Trawl(
227
+ api_key="sk_live_...", # default: the TRAWL_API_KEY environment variable
228
+ timeout=60.0, # seconds
229
+ max_retries=2,
230
+ )
231
+ ```
232
+
233
+ Responses are [pydantic](https://docs.pydantic.dev) models: `model_dump()` gives a dict,
234
+ `model_dump_json()` a JSON string, and fields the API adds later are kept on the object.
235
+
236
+ ## Links
237
+
238
+ - [Documentation](https://trawl.dev/docs)
239
+ - [Pricing](https://trawl.dev/pricing)
240
+ - [Changelog](https://github.com/trawl-inc/trawl-python/blob/main/CHANGELOG.md)
241
+
242
+ trawl is an independent service and is not affiliated with or endorsed by eBay Inc.
@@ -0,0 +1,31 @@
1
+ """Average and median sold price over the last 90 days.
2
+
3
+ export TRAWL_API_KEY=sk_live_...
4
+ python examples/average_price.py "nintendo switch oled"
5
+ """
6
+
7
+ import sys
8
+ from datetime import date, timedelta
9
+ from statistics import mean, median
10
+
11
+ from trawl_api import Trawl
12
+
13
+ query = sys.argv[1] if len(sys.argv) > 1 else "nintendo switch oled"
14
+
15
+ client = Trawl()
16
+ sold = client.ebay.sold(
17
+ query,
18
+ condition="used",
19
+ date_from=date.today() - timedelta(days=90),
20
+ max_pages=5, # up to 500 sales, and at most 5 credits
21
+ )
22
+
23
+ prices = [listing.sale_price for listing in sold.results]
24
+ if not prices:
25
+ sys.exit(f"No sales of {query!r} in the last 90 days.")
26
+
27
+ print(f"{query}: {len(prices)} sales in the last 90 days ({sold.currency})")
28
+ print(f" average {mean(prices):.2f}")
29
+ print(f" median {median(prices):.2f}")
30
+ print(f" range {min(prices):.2f} to {max(prices):.2f}")
31
+ print(f"{sold.credits_charged} credit(s) charged")
@@ -0,0 +1,33 @@
1
+ """Handle the API's errors and keep track of credits.
2
+
3
+ export TRAWL_API_KEY=sk_live_...
4
+ python examples/errors_and_credits.py
5
+ """
6
+
7
+ import trawl_api
8
+ from trawl_api import Trawl
9
+
10
+ client = Trawl(max_retries=3, timeout=30)
11
+
12
+ try:
13
+ sold = client.ebay.sold("rolex submariner", min_price=2000, max_pages=3)
14
+ except trawl_api.BadRequestError as error:
15
+ # A parameter failed validation; the message names the field and the rule.
16
+ print(f"Bad request: {error}")
17
+ except trawl_api.AuthenticationError:
18
+ print("The API key is missing, invalid or deleted.")
19
+ except trawl_api.InsufficientCreditsError as error:
20
+ # The month's credits are spent, or too few remain for this max_pages.
21
+ print(f"Not enough credits: {error}")
22
+ except trawl_api.RateLimitError as error:
23
+ # Raised only after the client's own retries; wait this long and try again.
24
+ print(f"Rate limited, retry in {error.retry_after}s")
25
+ except trawl_api.APIConnectionError:
26
+ print("Could not reach the API.")
27
+ except trawl_api.APIStatusError as error:
28
+ print(f"The API answered {error.status_code}: {error}")
29
+ else:
30
+ print(f"{sold.count} results for {sold.credits_charged} credit(s)")
31
+ if sold.rate_limit:
32
+ print(f"{sold.rate_limit.remaining} of {sold.rate_limit.limit} credits left")
33
+ print(f"The allowance resets on {sold.rate_limit.reset:%Y-%m-%d}")
@@ -0,0 +1,42 @@
1
+ """Export sold listings to a CSV file.
2
+
3
+ export TRAWL_API_KEY=sk_live_...
4
+ python examples/export_csv.py "charizard base set holo" sold.csv
5
+ """
6
+
7
+ import csv
8
+ import sys
9
+
10
+ from trawl_api import Trawl
11
+
12
+ query = sys.argv[1] if len(sys.argv) > 1 else "charizard base set holo"
13
+ path = sys.argv[2] if len(sys.argv) > 2 else "sold.csv"
14
+
15
+ COLUMNS = [
16
+ "date_sold",
17
+ "title",
18
+ "sale_price",
19
+ "shipping_price",
20
+ "currency",
21
+ "condition",
22
+ "buying_format",
23
+ "bids",
24
+ "location",
25
+ "item_id",
26
+ "item_link",
27
+ ]
28
+
29
+ client = Trawl()
30
+ sold = client.ebay.sold(query, max_pages=10) # up to 1,000 sales, and at most 10 credits
31
+
32
+ with open(path, "w", newline="", encoding="utf-8") as file:
33
+ writer = csv.DictWriter(file, fieldnames=COLUMNS)
34
+ writer.writeheader()
35
+ for listing in sold.results:
36
+ writer.writerow(listing.model_dump(mode="json", include=set(COLUMNS)))
37
+
38
+ print(f"Wrote {sold.count} sales to {path} ({sold.credits_charged} credit(s) charged)")
39
+
40
+ # With pandas instead:
41
+ # import pandas as pd
42
+ # frame = pd.DataFrame(listing.model_dump() for listing in sold.results)
@@ -0,0 +1,28 @@
1
+ """Find an eBay category id by name, then search inside it.
2
+
3
+ export TRAWL_API_KEY=sk_live_...
4
+ python examples/find_category.py "trading card singles" "charizard"
5
+ """
6
+
7
+ import sys
8
+
9
+ from trawl_api import Trawl
10
+
11
+ category_query = sys.argv[1] if len(sys.argv) > 1 else "trading card singles"
12
+ query = sys.argv[2] if len(sys.argv) > 2 else "charizard"
13
+
14
+ client = Trawl()
15
+
16
+ # Category ids differ per marketplace: look them up on the one you will search.
17
+ found = client.ebay.categories(category_query, site="EBAY_US")
18
+ if not found.categories:
19
+ sys.exit(f"No category matches {category_query!r}.")
20
+
21
+ for category in found.categories:
22
+ print(f"{category.category_id:>8} {category.name} ({category.group})")
23
+
24
+ best = found.categories[0]
25
+ sold = client.ebay.sold(query, category=best.category_id, site="EBAY_US")
26
+ print(f"\n{sold.count} sales of {query!r} in {best.name}:")
27
+ for listing in sold.results[:10]:
28
+ print(f" {listing.currency}{listing.sale_price:>9.2f} {listing.title}")