DEV Community

Devil Scrapes
Devil Scrapes

Posted on

A datacenter IP gets 0 listings; a residential one gets 12. Same build.

Quick answer: the same Facebook Marketplace scraper, same build, same query, same hour, scored 0 rows from a datacenter IP and 12 real listings from a residential one. Not a 403 — a login wall served with a 200. Marketplace does not rate-limit datacenter egress so much as quietly decide it is not a logged-out browser at all.

Here is the full 2x2 we actually measured, rather than assumed:

Egress Result
Apify runner, no proxy FAILED — login wall, 0 rows
Shared DATACENTER (useApifyProxy: true, no groups) FAILED — login wall, 0 rows
RESIDENTIAL, country pinned to US SUCCEEDED — 12 real listings

So residential is a requirement for this target, not an optimisation. Our own input schema had said "defaults to no proxy, opt in if rate-limited." That copy was simply wrong, and no customer would have discovered it as a proxy problem — they would have discovered it as an Actor that returns nothing.

Why does a bare useApifyProxy: true still fail?

Because useApifyProxy: true with no apifyProxyGroups does not mean "use a proxy." It means datacenter.

This is the single most expensive line of config in our fleet. It looks like you opted into proxying, it passes every local test, and it fails only in the cloud — against exactly the targets that matter. We have now hit it twice on two unrelated Actors, months apart, and both times the symptom was a green laptop and an empty cloud run.

If your proxy config does not literally name a group, you are on datacenter and your local success proved nothing about it.

Why pin the country instead of letting the exit land anywhere?

Because Marketplace is location-scoped, and a geo-random exit does not error — it succeeds with the wrong city's listings.

That is the dangerous failure mode. A 403 tells you something is broken. A 200 carrying plausible listings from a region your customer did not ask about is silently wrong data, and it will sit in their dataset looking completely fine. Pinning apifyProxyCountry costs nothing and removes an entire class of quiet corruption.

Meta rotates the GraphQL doc_id on every deploy

The search page talks to an internal GraphQL endpoint keyed by a persisted-query id. Meta rotates that id whenever they ship. A scraper that hardcodes it works beautifully until a Tuesday afternoon and then returns hard errors forever.

So we do not hardcode it. On a hard error we re-derive the current doc_id straight from the search page's own JS bundles, cache it, and retry the request. The customer never files a ticket, and we never ship a patch for it — the rotation is a normal Tuesday, not an outage.

What the failing runs cost the customer: nothing

Both failing cells above charged $0.00.

That is worth stating plainly because it is a design decision, not luck. We charge after delivery, not on attempt. A run that reaches a login wall reports per-query why it failed and bills nothing — no per-listing charge for listings that were never there, and the flat start fee only once a query's results are genuinely read.

We have watched another Actor in this fleet bill a customer a start fee on seventeen consecutive runs that returned zero rows. Once is an incident; the architecture that allows it is the actual bug.

The order that matters: fix the copy before the qualifying run

One process note, because it costs a release day every time it is forgotten.

Our publisher only counts a successful QA run against the exact build being published. So if you run cloud QA, then notice the README says something wrong, then fix it — you have just created a new build and orphaned the green run you already paid for. The Actor silently goes from publishable to gate-refused, and nothing tells you why.

The schema fix, the README and the promo manifest in this Actor all changed in the same commit, before any qualifying run. Copy first, evidence second.


Facebook Marketplace Scraper is live on the Apify Store at $5.20 / 1,000 listings — a $0.20 start fee plus $0.005 per unique listing landed. Give it a search term and a buy-location; it walks public Marketplace search results and returns title, price, seller, location and delivery type as structured JSON. No login, no browser automation.

👉 https://apify.com/DevilScrapes/facebook-marketplace-scraper

Captchas are our cardio. 😈

Top comments (0)