Building a Personal Facebook Marketplace Watcher: What I Built and Why It’s Useful

If you’ve ever missed a great local deal because you weren’t online at the right time, you know the pain. I set out to solve that with a small, reliable, and privacy-respecting script that watches Facebook Marketplace for you, filters the noise, and pings your phone only when the right items appear.

The Problem

  • Marketplace deals move fast; manual refreshing is tedious.
  • Search results include lots of noise: wrong items, overpriced listings, accessories instead of the product.
  • You want immediate, actionable alerts on your phone with a link to buy.

The Solution

I built a Python service that:

  • Periodically queries Facebook Marketplace for specific searches.
  • Parses listings from the page’s embedded JSON (with an HTML fallback).
  • Stores everything in SQLite to avoid repeat work/alerts.
  • Filters items using per-query rules (like max price).
  • Sends a push notification to your iPhone via Pushover or to any device via ntfy.
  • Optionally runs AI image checks (Gemini) before alerting to ensure the listing actually shows the product you want.

Why This Is Handy

  • Saves time: no more constant refreshing.
  • Reduces noise: alerts are gated by price and optional image verification.
  • Faster response: being first boosts your odds of snagging good deals.
  • Portable: run it locally or deploy to a VPS and forget it.

Key Features

  • Config-driven searches in config.yaml with per-query filters and AI prompts.
  • APScheduler-based periodic jobs or a one-shot CLI mode (--once).
  • SQLite persistence (dedupe + skip logic for already-seen items).
  • Notifications:
  • Pushover: iOS-friendly, battle-tested.
  • ntfy: simple, self-hostable, works across devices.
  • AI image filter (Gemini): only alert if the main image looks like the actual product (e.g., Xbox One S/X).
  • Robust scraping:
  • Primary parsing from Marketplace’s embedded JSON.
  • Fallback to HTML anchors for resilience.
  • Debuggable:
  • LOG_LEVEL=DEBUG to see fetch timing, parse counts, filter decisions.
  • Proxy support (e.g., http://localhost:8080) for traffic inspection.

Technical Highlights

  • Per-query max_price filtering and ai_prompt overrides.
  • JSON-first parsing from the app’s application/json script blocks.
  • AI prompt is configurable at the global or per-query level.
  • Rate-limit backoff for Gemini: reads “retry in Xs” and waits before retrying.
  • Only new items trigger notifications, and only if AI passes (when enabled).
  • Every listing—pass or fail—is stored once with ai_pass and ai_model so it’s never reprocessed.

Privacy & Ethics

  • You supply your own session cookies (optionally) and API keys.
  • No external storage of secrets; everything is on your machine or server.
  • Respect the site’s terms and your local laws. Use responsibly.

Deploy It Anywhere

  • Local dev: python main.py --once to test, then python main.py for scheduled runs.
  • Server: systemd service for reliable background execution on Oracle Linux (or nohup, tmux).

Real-World Benefits

  • Find underpriced items fast (e.g., “Xbox One under $50”).
  • Avoid accessory-only listings through AI image checks.
  • Keep a clean history of seen items and outcomes for auditing.

Future Ideas

  • Multi-image analysis and “any image qualifies” logic.
  • Region-aware price normalization.
  • Web UI for logs, status, and manual triage.
  • Slack/Discord notification channels.

How To Use (Quick Start)

  • Set up Python venv and install requirements.
  • Add your searches, headers, cookies, and notification settings to config.yaml.
  • Optional: add Gemini gemini_api_key and customize the prompt.
  • Run once to test:
  • LOG_LEVEL=DEBUG python main.py --once
  • Run scheduled
  • python main.py (or set up a systemd service on your server).

Configuration

Edit `config.yaml` (example below). You can also use JSON, but YAML is recommended.

queries:
  - query: "xbox one"
    max_price: 50
    ai_prompt: "Does this photo clearly show a Microsoft Xbox One S or Xbox One X console? Respond with only 'YES' or 'NO'."

check_interval_minutes: 10

database:
  path: "marketplace.db"

notification:
  service: "ntfy"  # "pushover" or "ntfy"
  pushover:
    api_token: "YOUR_PUSHOVER_APP_TOKEN"
    user_key: "YOUR_PUSHOVER_USER_KEY"
  ntfy:
    server: "https://ntfy.sh"  # or your self-hosted server
    topic: "your-topic-name"

facebook:
  headers:
    user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
  # You can paste a full raw Cookie header string or a key:value map
  cookies: "c_user=...; xs=...; fr=...; datr=...; sb=..."
  proxy: "http://localhost:8080"  # optional; useful with tools like Burp

# Enable verbose item dump for debugging
debug:
  dump_parsed_items: false

# AI image filter (optional) - filters notifications to qualifying images
ai:
  gemini_api_key: "YOUR_GEMINI_API_KEY"  # or set env var GEMINI_API_KEY
  model: "gemini-2.5-flash"
  prompt: "Does this photo clearly show a Microsoft Xbox One S or Xbox One X console? Respond with only 'YES' or 'NO'."
```

Closing Thoughts

This project turns Marketplace hunting from a manual chore into an automated assistant that works for you. It’s small, configurable, and designed to be extended. Whether you’re chasing a specific console, bike size, or niche tool, you get relevant, timely alerts—and more wins.

If you are interested in the code, send me a message.

Csaba is passionate about Cyber Security, Pentesting and just making things work.

Site Footer

Sliding Sidebar