Motivated Seller Score — Listing Motivation Rank avatar

Motivated Seller Score — Listing Motivation Rank

Pricing

from $3.50 / 1,000 motivated listings

Go to Apify Store
Motivated Seller Score — Listing Motivation Rank

Motivated Seller Score — Listing Motivation Rank

Motivated seller score for property listings you already have. Same rows, plus a 0-100 score from keywords and days on market. Listing motivation rank from 200 rows. Paste JSON or a dataset ID. No portal scrape and no phones.

Pricing

from $3.50 / 1,000 motivated listings

Rating

0.0

(0)

Developer

Noah Adler

Noah Adler

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

17 days ago

Last modified

Categories

Share

Motivated seller score for property listings you already have. Paste JSON, or pass an Apify datasetId. Each output row is the same listing plus a motivated seller score from 0 to 100. From 200 rows, the run also adds a listing motivation rank.

This Actor does not scrape Zillow, Idealista, Fotocasa, SeLoger, or any other portal. It does not return phones, emails, or owner names. With redactContactsInText left on (the default), phones and emails inside the description are replaced with [redacted].

What you get

FieldWhen
motivationScoreAlways. Integer 0–100.
motivationFactorsAlways. Short reasons.
keywordHitsAlways. Empty when nothing matched.
daysOnMarketFrom publishedAt, firstPublicationDate, daysOnPortal, or another listed date. null if there is no date.
scoreVersionAlways "0.2".
rarityAppliedfalse under 200 rows. true from 200 rows up.
motivationPercentile, relativeTiernull under 200 rows. From 200 rows, a rank inside this run.
specialCasetrue when a zero-point case phrase matched (subasta, asta, nuda propiedad, …).
detectedLanguageHint only (es, fr, en, de, it). Every dictionary still runs.
priceDropped, priceCutPctOnly when this row brings previousPrice, priceHistory, or priceDropped: true.
previousPriceObserved, priceCutPctObserved, relisted, firstSeenAt, daysObservedOnly when previousDatasetId was set.

relativeTier is "top" at or above the 95th percentile of this run, "high" from the 80th up to that line, and "normal" below the 80th. It is not an absolute motivated-seller threshold.

How the score is calculated

Deterministic. No LLM.

  • Base weight 25: urge, herencia, estate sale, as-is, must sell, foreclosure, venta urgente, liquidación, Nachlass, Zwangsversteigerung, and the other distress phrases in the dictionary.
  • Base weight 12: por traslado, por divorcio.
  • Base weight 8: a reformar, para reformar, à saisir.
  • Base weight 5: oportunidad, negociable, coup de coeur, price reduced. A weak hit alone is marketing language, not distress.
  • Weight 0, specialCase only: subasta, procedente de banco, procedente de entidad, nuda propiedad, asta.
  • A negation within three tokens (no, sin, nicht, non, pas de, no hay) cancels that phrase.
  • Days on market ≥ 60: +15. ≥ 120: +25 more (both apply). Cap 100.
  • Price cut on this row: +20, only from previousPrice, oldPrice, priceBefore, originalPrice, priceHistory, or priceDropped: true.
  • Relist versus previousDatasetId: +15.

From 200 rows up, rarity reweights motivationScore even when you pass no new inputs. Each phrase weight is multiplied by clamp(log(1/df) / log(100), 0.1, 1.5), where df is the share of rows in this run that contain the phrase. A phrase everyone uses (high df) falls near the floor, about 0 to 1 point instead of 5. Under 200 rows, rarity is off and oportunidad stays at 5.

On a normal Madrid sample, strong distress phrases are rare. Below 200 rows the score mostly moves with days on market and weak words such as oportunidad. This is not a probate detector.

No description (some Leboncoin list feeds): keyword hits stay empty unless the title itself contains a phrase. Days on market still score.

Works with datasets from

Pass datasetId from a dataset you already stored. Examples, none of which include phone numbers:

  • fotocasa-spain-property-scraper
  • seloger-scraper
  • leboncoin-immobilier (often title and date only, no description)
  • a Zillow or Idealista export you already downloaded and saved as an Apify dataset

You can also paste listings JSON. Contact fields are dropped either way.

Weekly schedule

Run once a week. Point datasetId at this week's dataset and previousDatasetId at last week's dataset:

{
"datasetId": "DATASET_ID_THIS_WEEK",
"previousDatasetId": "DATASET_ID_LAST_WEEK",
"maxItems": 5000
}

A fingerprint that matches exactly one older row with a different listingId is relisted. The same listingId with a newer publishedAt is relisted as well. Day points then use max(daysObserved, daysOnMarket).

SUMMARY.notInThisFile counts previous rows that are absent from this input. It does not mean those listings sold. The count changes if filters, area, or maxItems change.

If previousDatasetId cannot be read, the run returns an error row. It does not pretend the diff was empty.

Input

  • listings or datasetId (one of them).
  • maxItems: default 100, max 5000.
  • asOfDate: ISO date for days on market. Default is the run time in UTC.
  • redactContactsInText: default true.
  • customKeywords: [{ "phrase": "vendido por divorcio", "weight": 12 }]. Weight must be an integer from 1 to 25. Anything else stops the run.
  • previousDatasetId: optional.

Pricing

Pay per enriched listing: $0.005. No residential proxy.

Limitations

  • Not a skip-trace product and not a portal scraper.
  • priceDropped means the previous price was already on the row. A drop seen only by comparing previousDatasetId is priceCutPctObserved, and it does not change priceDropped.
  • relativeTier compares rows inside the run you just scored.