VivaReal Scraper: Brazil Property Listing Alerts avatar

VivaReal Scraper: Brazil Property Listing Alerts

Pricing

from $1.52 / 1,000 listing returneds

Go to Apify Store
VivaReal Scraper: Brazil Property Listing Alerts

VivaReal Scraper: Brazil Property Listing Alerts

VivaReal scraper by search: get one row per listing (price, condo fee, IPTU, area, bedrooms, bathrooms, parking, neighbourhood, link). Turn on monitoring for only new listings and price drops since your last run. No login required; empty searches and quiet monitoring runs are free.

Pricing

from $1.52 / 1,000 listing returneds

Rating

0.0

(0)

Developer

Adrian Voss

Adrian Voss

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Watch a property search, get only what's new. This actor runs your search against VivaReal's own search-results pages — one of Brazil's two largest real-estate marketplaces — and returns one clean row per listing: price, condo fee, IPTU, area, bedrooms, bathrooms, parking, neighbourhood, and a permanent link. Turn on monitoring and it becomes a watchlist: new listings and price drops since your last run, charged per new row, free on quiet days. Schedule it hourly and send the results to Discord, Slack, Google Sheets, or n8n.

Who it's for

A renter or buyer watching one city for newly-posted apartments or houses wants a live feed of what's actually on VivaReal right now, instead of refreshing the search page by hand — VivaReal has no saved-search alert of its own. An investor tracking a market for underpriced or recently discounted listings, a relocation agency monitoring several cities for clients at once, or a Sheets/n8n workflow that reacts the moment something new is posted, gets the same shape here: paste a location or a search URL, get back a flat row per listing (including condo fee and IPTU, which most scrapers skip), and pay only for listings actually returned — a search that finds nothing costs nothing.

VivaReal and ZAP Imoveis share inventory and a codebase. The same property is often listed on both sites (Grupo OLX Brasil owns both). This actor's sibling, zapimoveis-listing-lookup, runs the identical search and monitoring shape against ZAP Imoveis — dedupe across the two by listingId, which is the same numeric ZAP/VivaReal property id on both. OLX Brasil's general classifieds site is a different front end entirely and is covered by a separate actor, not this family.

Why this one

  • Monitoring first, not a one-off dump. Turn on "Only return results that are new, or dropped in price" and this actor becomes a watchlist: it remembers every listing ID it has already shown you, and only bills you for what's actually new or has dropped in price since the last run. A quiet run — nothing new, nothing cheaper — still finishes and costs only the base run fee.
  • Reads the condo fee and property tax, not just the headline price. Every card on VivaReal's own search pages carries "Cond. R$ X • IPTU R$ Y" next to the price — this actor parses both into their own condoFee and iptu columns (0 when the listing marks them "isento"/exempt, empty when the site doesn't show the line at all), which most listing scrapers skip entirely.
  • A real posted date, not a guess. VivaReal's page ships a second, easy-to-miss JSON-LD block (RealEstateListing.mainEntity) that carries each listing's actual datePosted — this actor reads it directly rather than fabricating one from "time ago" text.
  • Never charged for a miss, or a quiet monitoring run. A search that finds nothing, or a scheduled watchlist run where nothing changed, still gets a row explaining what happened — and costs nothing.
  • Paste a location or your own saved search URL. Type VivaReal's own <state-slug>/<city-slug> (e.g. "sp/sao-paulo", "minas-gerais/belo-horizonte"), or paste a VivaReal search-results URL you've already filtered on the site — its filters carry straight through.

What you get

One row per listing by default (turn off "Expand rows" in Advanced to get one row per search instead, with the listings nested under listings).

FieldTypeDescription
querytextThe search you submitted, echoed back
foundbooleantrue if VivaReal returned a real search-results page for this search
statustextOK on a match; NOT_FOUND, BLOCKED, or BAD_FORMAT on a miss
messagetextPlain-English reason for a miss — only present when found is false
searchQuerytextThe search line you submitted (location, or the pasted URL)
listingCountnumberListings returned and billed for this search
truncatedbooleantrue if a page couldn't be fetched (even after one retry) before the requested count was reached
newCountnumberNew listings this run (monitoring only)
priceDropCountnumberPrice drops this run (monitoring only)
monitorStatustextNO_NEW_ROWS on a quiet monitoring run, WATCHLIST_SEEDED on a silent first run, otherwise empty
listingIdtextVivaReal's own numeric listing ID (shared with ZAP Imoveis for the same property)
titletextListing title
operationtextbuy or rent
pricenumberPrice in BRL
currencytextAlways BRL
condoFeenumberMonthly condo fee in BRL; 0 when the listing marks it "isento" (exempt)
iptunumberAnnual property tax (IPTU) in BRL; 0 when marked "isento"
areaM2numberFloor area in square metres
bedroomsnumberBedroom count
bathroomsnumberBathroom count
parkingSpacesnumberParking spaces
floorLeveltextFloor level, when VivaReal's listing data has one
streettextStreet address
neighbourhoodtextNeighbourhood (bairro)
citytextCity
statetextState (two-letter UF code)
adTiertextVivaReal's own ad tier for this listing (e.g. "SUPER PREMIUM"), when it has one
advertiserNametextThe advertiser's name, only when it is clearly a business/agency — a private seller's name is never output (see "Data & privacy")
postedAtdateThe listing's real posted date, from VivaReal's own data
photoCountnumberNumber of photos VivaReal's listing data lists
imageUrltext (link)First listing image
descriptiontextListing description, with any email/phone/WhatsApp-link-like text redacted
changeTypetextnew, price-drop, or seen (monitoring only)
isNewbooleantrue on this listing's first appearance (monitoring only)
previousPricenumberThe price last recorded for this listing, when it dropped (monitoring only)
priceDropPctnumberPercentage drop from the previous recorded price (monitoring only)
firstSeenAtdateWhen this watchlist first saw this listing (monitoring only)
urltext (link)Permanent listing link
scrapedAtdateWhen this row was fetched

Deselect any field in the Input tab's column picker to drop it from every row — query, found, status, message, and scrapedAt always stay. This actor does not declare a sellerHash or totalAvailable column: VivaReal's search pages don't expose an advertiser ID (so a hash can't be computed) or a reliable total-match count on this route — see the FAQ.

Pricing

Pay-per-event. A flat per-run fee covers session/proxy warmup; you're billed per item only when data is actually found and returned — see .actor/pay_per_event.json for exact prices. A miss is never charged.

Monitoring / only new results

Turn on "Only return results that are new, or dropped in price" and a search line becomes a watchlist instead of a one-off dump:

  • A listing counts as new the first time its VivaReal listing ID is returned by this watchlist.
  • A listing counts as a price drop when its price falls by at least "Minimum price drop" (5% by default) since it was last seen — measured against the most recently seen price, so a listing that drops twice is compared against its latest price each time, not its original one.
  • Already-seen, unchanged listings are dropped before you're billed. A quiet run — nothing new, nothing cheaper — still finishes and returns one free row with monitorStatus: NO_NEW_ROWS, so a scheduled task shows it actually ran.
  • The first run has nothing to compare against. By default it returns everything it finds as "new" (so you see your whole current search result once). Turn on "Seed silently" to instead have the first run bank everything quietly with zero rows and zero charge, and start alerting from the second run on — this is what stops a fresh watchlist from firing a hundred "new listing" messages into your Discord channel on day one.
  • Results are sorted by each listing's own posted date whenever monitoring is on (VivaReal's own search pages carry no sort parameter to force this server-side), so two runs of the same watchlist are always comparing the same ordering.

Schedule recipe. Save this actor as an Apify Task with deltaMode: true and a deltaName you choose, add an hourly (or daily) Schedule, and point the task's webhook at Discord, Slack, Google Sheets, or an n8n/Make webhook URL to get pinged only when something actually changes.

Price-drop window limitation. A price drop is only ever seen while the listing is still inside what a run actually fetches — if a listing falls out of your maxListingsPerSearch window or your price filters between runs, this actor has no way to notice it dropped. Keep searches reasonably narrow (one city, a price band) rather than one huge catch-all if price-drop coverage matters to you.

How to use

  1. In the Apify Console. Open the actor page and click Start — the searches field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
  2. Via the API. Call it directly with a POST request — no Console needed once you have an API token:
    curl "https://api.apify.com/v2/acts/accountable_eel~vivareal-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"searches":["sp/sao-paulo"]}'
  3. On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
  1. Turn on "Only return results that are new, or dropped in price" under Monitoring once you want a watchlist instead of a one-off dump — see "Monitoring / only new results" above.

Input

{
"searches": [
"sp/sao-paulo"
]
}

One per line: paste a full vivareal.com.br search-results URL (its own filters are kept), or type a location as

operation (buy/rent) and propertyType (apartment, house, house in a condominium, land, or commercial) apply to any search line that's a location rather than a pasted URL — a pasted URL keeps whatever operation and property type it already has. VivaReal's own state slugs aren't all 2-letter codes (sp and rj are, but most others are spelled out in full, e.g. minas-gerais, parana) — find the right one by browsing to that state on vivareal.com.br and reading it off the URL, or just paste the URL directly.

Sample output

queryfoundstatussearchQuerylistingCounttotalAvailabletruncatednewCountpriceDropCountmonitorStatuslistingslistingIdtitleoperationpricecurrencycondoFeeiptuareaM2bedroomsbathroomsparkingSpacesfloorLevelstreetneighbourhoodcitystateadTieradvertiserNamepostedAtphotoCountimageUrldescriptionchangeTypeisNewpreviousPricepriceDropPctfirstSeenAturlscrapedAt
sp/sao-paulotrueOK<monitoring status (quiet / seeded runs)><all listings found (full list)><vivareal listing id (shared with zap imoveis for the same property)> <buy or rent><price (brl)><condo fee (brl/month)><iptu property tax (brl/year)><area (m²)><state (uf)><ad tier (standard / premium / super premium)><advertiser (agencies only)><new / price-drop / seen><is this listing new?><previous price (brl)><price drop %>1970-01-01T00:00:00.000Z

A search that finds nothing comes back as a row with "found": false and is never charged. A quiet monitoring run comes back as a row with "found": true, "monitorStatus": "NO_NEW_ROWS" and is also never charged.

Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

curl "https://api.apify.com/v2/acts/accountable_eel~vivareal-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
-X POST \
-H "Content-Type: application/json" \
-d '{"searches":["sp/sao-paulo"]}'

n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~vivareal-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"searches":["sp/sao-paulo"]} (swap in an expression from an earlier node for a real value).

Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~vivareal-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"searches":["{{search}}"]}, mapping the row's search into the searches array.

MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "VivaReal Scraper | Apify" — the agent will find and run this actor.

For monitoring, point the Task's webhook (Console → your Task → Webhooks) at a Discord or Slack incoming-webhook URL, a Google Sheets automation (via Zapier/Make/n8n listening for the webhook), or an n8n/Make workflow's own webhook trigger, so new listings and price drops land wherever you actually work instead of a dataset you have to remember to check.

Tips

  • Start monitoring with "Seed silently" on for any watchlist you're setting up on a schedule — it avoids a wall of "new" alerts for your entire current search result on day one.
  • Keep a monitored search reasonably narrow (one city, a price band) — see "Price-drop window limitation" above for why a huge catch-all search misses price drops on listings that fall outside what gets fetched.
  • Dedupe against zapimoveis-listing-lookup on listingId — ZAP and VivaReal share inventory, so the same apartment can legitimately appear on both.
  • A BLOCKED status means VivaReal returned a 403/429/503, or an UNBLOCKER 502, even after this actor's own one retry on a fresh session — trying again shortly usually clears it.
  • Paste your own already-filtered VivaReal search URL instead of rebuilding filters in this actor's Input tab — anything the URL already filters on carries straight through.

vs. no dedicated leader

S3b (2026-09-18 research) found no strong dedicated VivaReal actor on the Apify Store — unlike ZAP Imoveis, which has an established leader. This actor adds new-listing and price-drop monitoring as a first-class mode, reads the condo fee and IPTU that most scrapers skip, and bills only for listings actually delivered.

What it costsWhat you getTrade-off
This actorPay per listing returned; nothing for a miss or a quiet monitoring runCondo fee, IPTU, real posted date, monitoring mode (new + price drop)An advertiser's name is output only when it's clearly an agency; no seller ID exists on this route to hash or dedupe by
Doing it yourselfYour time to build and maintain a scraper, a seen-listing store, and a price-tracking layerThe same dataThis actor's dual JSON-LD parsing, monitoring state, and redaction are the maintenance burden it absorbs

No dedicated actor covers cross-marketplace monitoring: the GitHub project imovel-bot scores listings 0-100 across OLX, ZAP and QuintoAndar by sweeping them itself — this actor and its sibling zapimoveis-listing-lookup are the hosted, schedulable version of that job for Grupo OLX Brasil's two property sites.

Data & privacy

This actor reads public search results that anyone can see without logging in. It doesn't log in, solve CAPTCHAs, or reveal hidden contact details. Seller identity is off by default: VivaReal doesn't expose an advertiser ID anywhere in the pages this actor reads, so this actor only ever outputs an advertiser's display name when it clearly reads as a business or agency — never a private seller's name. Agent and owner phone numbers, WhatsApp numbers and emails are never collected, and any that appear inside a listing's description are redacted to [redacted]. Not affiliated with VivaReal / Grupo OLX Brasil.

LGPD. Brazil's Lei Geral de Proteção de Dados is GDPR-like. Seller info is off by default here, and — unlike a toggle a buyer could turn on — a private seller's name is never output by this actor at all, on this route.

Is this allowed? It collects the same public listing data your browser shows, for the searches you choose. It's built for monitoring a search, not for copying the marketplace. Check that your use fits VivaReal's terms and your local law.

FAQ

Why is a row empty, or why does found say false? Either the input line was blank (status: BAD_FORMAT), the location was missing a state or city slug (status: BAD_FORMAT), VivaReal returned a 403/429/503 or an UNBLOCKER 502 even after one retry (status: BLOCKED), or the search matched nothing on VivaReal right now (

status: NOT_FOUND
). Check the message column for the specific reason. None of these are billed.

Am I charged for a quiet monitoring run? No. When monitoring finds nothing new and no price drops, you get one row with monitorStatus: NO_NEW_ROWS and listingCount: 0Actor.charge() only fires when listingCount is greater than zero.

Why isn't there a sellerHash or seller ID column? VivaReal's search-results pages don't expose an advertiser ID anywhere this actor can read — only a display name, sometimes a private person's, sometimes an agency's. Rather than fabricate an ID or hash a name (which would leak a private seller's identity through the hash's stability), this actor outputs advertiserName only when the name is clearly a business, and nothing at all otherwise.

Why isn't there a totalAvailable column? VivaReal's search pages don't carry a reliable "N properties match this search" total on the static route this actor reads (confirmed directly, not assumed) — so rather than show a stale or partial number, this actor doesn't declare the column at all.

What does truncated: true mean? This actor tried to fetch another page toward your maxListingsPerSearch limit and that page came back blocked (or with an empty/unreadable body) even after one retry on a fresh UNBLOCKER session — so you got fewer listings than requested. Try again shortly.

How do I find the right state slug for my city? Browse to that state and city on vivareal.com.br and read the two path segments after /venda/ or /aluguel/sp and rj are 2-letter codes, most others are the state's full name (minas-gerais, parana, rio-grande-do-sul, espirito-santo, confirmed for those four). Or just paste the full search URL as a search line instead — it always works.

Do I need to configure proxies? This actor requires Apify's UNBLOCKER proxy group by default — VivaReal is Cloudflare-protected and blocks plain datacenter and residential requests alike. UNBLOCKER is already configured; you don't need to change anything.

Can an AI agent call this directly? Yes. It's registered on the Apify MCP server — an agent in Claude, Cursor, or another MCP client can find and run it by name ("VivaReal Scraper | Apify"), or you can call the REST endpoint shown above from any script or workflow tool.

  • ZAP Imoveis Scraper — the same search and monitoring shape for ZAP Imoveis, which shares inventory and a codebase with VivaReal (both owned by Grupo OLX Brasil). Dedupe the two on listingId.