Skip to content

Commit 667cb38

Browse files
feat(vinted): add seller_country filter to search (0.14.0) (#2)
Adds an optional seller_country kwarg (CSV ISO-2) to vinted.search that filters results to sellers physically located in the given countries (Vinted federates cross-border EU listings; no native country filter). Adds seller_country_code to the item model and seller_country to the search response. Adds 1 credit per uncached seller looked up. Bumps version 0.13.1 -> 0.14.0 (pyproject + SDK_VERSION User-Agent). __init__.__version__ intentionally not touched here to avoid conflict with concurrent in-flight Shopee work in the same file. Co-authored-by: kasparasizi1 <132673909+kasparasizi1@users.noreply.github.com>
1 parent c8335c1 commit 667cb38

6 files changed

Lines changed: 57 additions & 3 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "scrapebadger"
3-
version = "0.13.1"
3+
version = "0.14.0"
44
description = "Official Python SDK for ScrapeBadger - Async web scraping APIs for Twitter and more"
55
readme = "README.md"
66
license = { text = "MIT" }

src/scrapebadger/_internal/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
T = TypeVar("T")
3030

3131
# User agent for SDK requests
32-
SDK_VERSION = "0.13.1"
32+
SDK_VERSION = "0.14.0"
3333
USER_AGENT = f"scrapebadger-python/{SDK_VERSION}"
3434

3535

src/scrapebadger/vinted/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ class VintedItemSummary(_BaseModel):
134134
user: Summary of the item owner.
135135
photo: Main photo.
136136
photos: All photos attached to the item.
137+
seller_country_code: Physical country of the seller as an upper-case
138+
ISO-2 code (e.g. "FR"), or None. Populated only when the
139+
``seller_country`` search filter is used.
137140
"""
138141

139142
id: int
@@ -148,6 +151,7 @@ class VintedItemSummary(_BaseModel):
148151
user: VintedUserSummary | None = None
149152
photo: VintedPhoto | None = None
150153
photos: list[VintedPhoto] = Field(default_factory=list)
154+
seller_country_code: str | None = None
151155

152156

153157
class VintedItemDetail(_BaseModel):
@@ -366,11 +370,15 @@ class SearchResponse(_BaseModel):
366370
items: List of matching items.
367371
pagination: Pagination metadata.
368372
market: Market code used for the search.
373+
seller_country: Echo of the normalized ``seller_country`` filter applied
374+
to this search (comma-separated ISO-2 codes, e.g. "fr,be"), or None
375+
when no filter was used.
369376
"""
370377

371378
items: list[VintedItemSummary] = Field(default_factory=list)
372379
pagination: VintedPagination | None = None
373380
market: str = ""
381+
seller_country: str | None = None
374382

375383

376384
class ItemDetailResponse(_BaseModel):

src/scrapebadger/vinted/search.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ async def search(
5858
color_ids: str | None = None,
5959
status_ids: str | None = None,
6060
order: str | None = None,
61+
seller_country: str | None = None,
6162
) -> SearchResponse:
6263
"""Search for items on Vinted.
6364
@@ -72,6 +73,14 @@ async def search(
7273
color_ids: Comma-separated color IDs to filter by.
7374
status_ids: Comma-separated status IDs to filter by.
7475
order: Sort order (e.g. "newest_first", "price_low_to_high").
76+
seller_country: Filter results to items whose seller is physically
77+
located in one of the given countries. A comma-separated list of
78+
ISO-2 country codes (e.g. ``"fr"`` or ``"fr,be"``). Vinted
79+
federates cross-border EU listings into each market domain and has
80+
no native country filter, so ScrapeBadger applies this filter.
81+
When set, each returned item gains a ``seller_country_code`` and
82+
the response gains a top-level ``seller_country`` echo.
83+
Billing: 1 base credit + 1 credit per uncached seller looked up.
7584
7685
Returns:
7786
Search response with matching items and pagination metadata.
@@ -92,6 +101,16 @@ async def search(
92101
print(f"Found {results.pagination.total_entries} items")
93102
for item in results.items:
94103
print(f" {item.title} - {item.price.amount} {item.price.currency_code}")
104+
105+
# Filter to sellers physically located in France or Belgium.
106+
# Billing: 1 base credit + 1 credit per uncached seller looked up.
107+
local = await client.vinted.search.search(
108+
"vintage jacket",
109+
seller_country="fr,be",
110+
)
111+
print(f"Applied seller_country filter: {local.seller_country}")
112+
for item in local.items:
113+
print(f" {item.title} - seller in {item.seller_country_code}")
95114
```
96115
"""
97116
params: dict[str, Any] = {
@@ -105,6 +124,7 @@ async def search(
105124
"color_ids": color_ids,
106125
"status_ids": status_ids,
107126
"order": order,
127+
"seller_country": seller_country,
108128
}
109129
response = await self._client.get("/v1/vinted/search", params=params)
110130
return SearchResponse.model_validate(response)

tests/test_vinted.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,22 @@ def test_search_response_empty(self) -> None:
470470
assert resp.items == []
471471
assert resp.pagination is None
472472

473+
def test_search_response_seller_country_default(self) -> None:
474+
resp = SearchResponse.model_validate(SEARCH_RESPONSE)
475+
assert resp.seller_country is None
476+
assert resp.items[0].seller_country_code is None
477+
478+
def test_search_response_with_seller_country(self) -> None:
479+
resp = SearchResponse.model_validate(
480+
{
481+
"items": [{"id": 1, "seller_country_code": "FR"}],
482+
"market": "fr",
483+
"seller_country": "fr,be",
484+
}
485+
)
486+
assert resp.seller_country == "fr,be"
487+
assert resp.items[0].seller_country_code == "FR"
488+
473489
def test_item_detail_response(self) -> None:
474490
resp = ItemDetailResponse.model_validate(ITEM_DETAIL_RESPONSE)
475491
assert resp.item is not None
@@ -610,6 +626,15 @@ async def test_search_with_filters(
610626
assert params["status_ids"] == "6"
611627
assert params["order"] == "newest_first"
612628

629+
async def test_search_with_seller_country(
630+
self, search_client: SearchClient, mock_base_client: MagicMock
631+
) -> None:
632+
mock_base_client.get.return_value = SEARCH_RESPONSE
633+
await search_client.search("jacket", seller_country="fr,be")
634+
635+
params = mock_base_client.get.call_args[1]["params"]
636+
assert params["seller_country"] == "fr,be"
637+
613638
async def test_search_optional_params_default_none(
614639
self, search_client: SearchClient, mock_base_client: MagicMock
615640
) -> None:
@@ -623,6 +648,7 @@ async def test_search_optional_params_default_none(
623648
assert params["color_ids"] is None
624649
assert params["status_ids"] is None
625650
assert params["order"] is None
651+
assert params["seller_country"] is None
626652

627653
async def test_search_returns_search_response(
628654
self, search_client: SearchClient, mock_base_client: MagicMock

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)