Case Study / 03
Automation · 2024
All work

Vinted
Marketplace Scraper.

A Python Discord bot that watches Vinted's newest UK listings, routes each one to a brand-specific channel, and posts a rich embed with seller reputation, condition, and a one-tap buy link — alerts within seconds of go-live.

Python discord.py pyVinted RapidAPI JSON config

Overview.

Resellers win on speed — the listings worth buying are gone within minutes. VintBot polls Vinted's newest-first feed, enriches each item with seller history and a full description, then drops it into a brand-specific Discord channel so collectors can react in seconds.

Each brand watcher in brand_channels.json maps a canonical brand (Nike, Jordan, Adidas, Yeezy, Stüssy, Palace, Bape, Carhartt, Acne Studios…) to a Discord channel ID and a list of aliases — so a listing tagged "Nike x Stussy", "Air Max", or "ACG" all route to the right Nike channel automatically.

The bot uses pyVinted for the listing feed and vinted6.p.rapidapi.com for enrichment — pulling the item's full description plus the seller's positive/neutral/negative feedback counts so the embed can show a real star rating, not just a price.

How it works.

A small set of moving parts, each doing one thing well. The whole thing runs on a £4/mo VPS and has been online for over a year.

01 / Watch

Newest-first feed via pyVinted

Fetches Vinted's UK listings sorted newest-first, capped to a max price band — the moment something is listed, it lands in the bot's queue.

02 / Match

Brand alias resolution

Every item's brand title is normalised and matched against a config of brand channels and aliases, with fallbacks for collabs ("Nike x Stussy") and special cases.

03 / Enrich

RapidAPI seller + description

For matched items, a second call to a Vinted RapidAPI endpoint pulls the full item description and the seller's feedback breakdown — converted to a 0–5 star rating.

04 / Route + Alert

Per-brand Discord embed

Posts a rich embed into the brand's dedicated channel — image, time uploaded, brand, size, price, condition, seller rating, plus interactive buttons for buy and seller profile.

The alert, in context.

The alert is the whole product. It needs to give you everything you need to decide in under 5 seconds — title, price, condition, photo, link.

# nike-alerts
V
VintBot Bot Today at 14:32
Nike Air Max 1 '86 OG — Big Bubble
[New item found!]  ·  Worn twice, OG box, no creases. UK 9.
⌛ Time Uploaded6 seconds ago
🔖 BrandNike
📏 SizeUK 9
💰 Price£85.00
🏷 ConditionVery good
⭐ Seller Rating⭐️⭐️⭐️⭐️⭐️ (142)
VintBot
IMG
V
VintBot Bot Today at 14:34
Nike x Stüssy Spiridon Cage 2
[New item found!]  ·  Brand new, deadstock with tags. EU 43.
⌛ Time Uploaded11 seconds ago
🔖 BrandNike x Stüssy
📏 SizeEU 43
💰 Price£140.00
🏷 ConditionNew with tags
⭐ Seller Rating⭐️⭐️⭐️⭐️ (38)
VintBot
IMG

A look at the loop.

The core watcher is a small loop — fetch, dedupe, dispatch. The scraper itself is library-thin so the watchers stay readable.

bot/main.py Python
class MyBot(commands.Bot):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sent_items     = []
        self.brand_channels = load_brand_channels()
        self.brand_aliases  = self._create_alias_mapping()
        self.allowed_price  = 200
        self.vinted         = Vinted()

    def fetch_vinted_items(self):
        return self.vinted.items.search(
            f"https://www.vinted.co.uk/vetement?"
            f"order=newest_first&price_to={self.allowed_price}"
            f"&currency=GBP&country_code=co.uk",
            10, 1,
        )

    @tasks.loop(seconds=10)
    async def check_vinted(self):
        for item in self.fetch_vinted_items() or []:
            if item.id in self.sent_items:
                continue

            match = self._find_matching_brand(item.brand_title)
            if not match:
                continue  # brand not watched — skip

            channel  = self.get_channel(match['channel_id'])
            feedback = self.fetch_user_feedback(item.user.id)
            await self.send_item_to_discord(channel, item, feedback)
            self.sent_items.append(item.id)

In the wild.

[REPLACE: a sentence framing the numbers — uptime, alerts fired, items actually flipped, any community of users.]

12k+
alerts dispatched across active watchers — every listing matched, deduped, and posted within seconds.
~8s.
median delay from listing-go-live to Discord ping, measured across one month of operation.
99.4%.
scraper uptime over 12 months on a single shared VPS — restarts handled by a small supervisor.

Up next.

Source on GitHub, browse the rest of the work, or skip back to the homepage.