All posts

Facebook Marketplace Alerts: Get Notified of New Listings

November 8, 2026

Good deals on Marketplace go in minutes. If you hunt for a specific bike, camera or sofa, you want a ping when a matching listing appears. This post shows how to build that alert yourself, what it costs to run, and how to keep it polite.

Check the free option first

The Facebook app has its own notification settings for Marketplace, and they cost nothing. Try them first. Meta's Help Center pages on this did not load for us when we checked, so we can't describe the current behavior. Third-party guides disagree on whether alerts arrive as push notifications or only inside the app, so test it on your own phone before you rely on it.

Build your own when you need more: several searches at once, tighter filters, delivery to Slack or a team channel, or a log of every listing for later price research.

How an alert works

  1. Search with your filters, newest listings first.
  2. Remember every listing id you have already seen, in a small file.
  3. Notify only for ids you have not seen before.
  4. Repeat on a schedule.

The first run will mark everything as new. Run it once silently to fill the seen list, then turn notifications on.

The script

import hashlib, json, os, pathlib, requests

KEY = os.environ["SCRAPEIO_API_KEY"]
SLACK = os.environ["SLACK_WEBHOOK_URL"]      # or any webhook, e.g. ntfy.sh
SEEN = pathlib.Path("seen.json")
SEARCH = {
    "query": "road bike 56cm",
    "lat": "30.2672", "lng": "-97.7431",     # Austin
    "radius_km": "40",
    "min_price": "150", "max_price": "700",
    "sort_by": "creation_time_descend",
    "date_listed": "last_24_hours",
}

def first_list(obj):
    """The listings are the first list of objects in the response."""
    if isinstance(obj, list):
        return obj
    if isinstance(obj, dict):
        for v in obj.values():
            found = first_list(v)
            if found and isinstance(found[0], dict):
                return found
    return []

def listing_key(item):
    return str(item.get("id") or hashlib.sha1(json.dumps(item, sort_keys=True).encode()).hexdigest())

seen = set(json.loads(SEEN.read_text())) if SEEN.exists() else set()
r = requests.get("https://scrapeio.com/v1/facebook/marketplace/search",
                 params=SEARCH, headers={"x-api-key": KEY}, timeout=60)
listings = first_list(r.json().get("data"))

new = [i for i in listings if listing_key(i) not in seen]
for item in new:
    requests.post(SLACK, json={"text": "New listing: " + json.dumps(item)[:500]}, timeout=30)
seen |= {listing_key(i) for i in listings}
SEEN.write_text(json.dumps(sorted(seen)))
print(len(new), "new")

Listings are passed through in the source's own shape. Print one listing the first time, then replace the json.dumps(item) in the message with the title, price and URL fields you see.

Several searches at once

To watch more than one item, turn SEARCH into a list of dictionaries and loop over it, keeping one seen file per search. Give each search a short name and put it in the alert text, so "road bike" and "camera lens" pings are easy to tell apart. If two searches overlap, share one seen file so the same listing never pings twice.

Schedule it

On a Mac or Linux box, cron is enough:

*/15 * * * * cd /home/you/alerts && /usr/bin/python3 alert.py >> alert.log 2>&1

A free GitHub Actions schedule or a small cloud function also works. Store the seen file somewhere that persists between runs.

What it costs

One search is one call. The math for a single search:

IntervalCalls a dayCalls a monthAt $1.88 per 1,000
Every 60 minutes24720$1.35
Every 15 minutes962,880$5.41
Every 5 minutes2888,640$16.24

Every 15 minutes is plenty for most items. Faster polling mostly adds cost, and it adds load on someone else's servers.

Troubleshooting

  • Every run says everything is new. The seen file is not being saved, or the job runs in a fresh folder each time. Print the file path and check it persists.
  • Nothing ever arrives. Run the search once by hand and print how many listings came back. If zero, widen the radius or loosen the price range. Check that latitude and longitude are not swapped.
  • The same item alerts twice. Sellers delete and repost to bump a listing, which creates a new id. Also skip items whose title and price match one you saw in the last 7 days.
  • Too many alerts. Your keyword is too broad. "Bike" matches kids' bikes, bike racks and spin bikes. Add a size, a brand or a model.
  • Webhook errors. Slack webhooks reject very long messages. Keep the text short and link to the listing.

Tips that make alerts useful

  • Filter hard. A price floor removes "$1, message me" placeholders.
  • Add a negative word list in your script: "parts", "broken", "wanted".
  • One search per city if you cover several areas. Results depend on location.
  • Be a good buyer. The alert gets you there first; a polite, specific message gets you the item.

How to do it with ScrapeIO

The script uses the Facebook Marketplace search API with sort_by=creation_time_descend and date_listed=last_24_hours. Get coordinates from the Marketplace location search API, and open a listing with the Marketplace item API. Each call is 1 credit, failed calls are free, and credits never expire, so a paused alert costs nothing.

100 free credits, no card. Credits never expire.

Get API key