# Changelog of InvestorLift · Wholesale Deals, Emails & Phones (`corent1robert/investorlift-scraper`) Actor

- **URL**: https://apify.com/corent1robert/investorlift-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/corent1robert/investorlift-scraper.md

## Changelog

All notable changes to the InvestorLift Marketplace Scraper are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### \[1.13] - 2026-08-26

*Apify Actor version: 1.13*

#### Added

- Store examples: Ohio, Georgia, Arizona, and North Carolina active deals with seller-site contacts (same recipe as Florida / Texas).

### \[1.12] - 2026-08-26

*Apify Actor version: 1.12*

#### Changed

- Console **Start** now matches the Store promise: Florida, 8 active deals, **Find public website…** on. The first CSV can include published `website` / `email` / `phone`.
- **Max deals** applies before seller-site lookup (was ignored when Find public website was on, so a Try of 8 walked the whole state).
- Company hostname match no longer treats a single first token as enough (`Freedom Equity Group` ≠ `freedom.to`). Company sites must use a `.com` / `.net` / `.org` (etc.) host. Placeholder mailboxes (`user@domain.com`, `support@agentfire.com`) are dropped.

### \[1.11] - 2026-08-26

*Apify Actor version: 1.11*

#### Added

- Seller-site lookup now reads footer / JSON-LD phones (not only `tel:` links), `mailto:` addresses, and a DuckDuckGo fallback when Bing has no company-matching result.
- Store examples: Florida and Texas active deals with seller-site contacts. Outreach examples now turn **Find public website…** on.

#### Changed

- Store title leads with emails & phones. Company hostname match accepts one distinctive token (e.g. Korr LLC). Unique sellers are looked up in parallel (cap 6).

### \[1.10] - 2026-08-25

*Apify Actor version: 1.10*

#### Added

- Optional **Find public website, email, phone, LinkedIn** (`findSellerContacts`, off by default). One lookup per wholesaler (`account_id`). Company site from the listing or Bing (en-US); then homepage + contact pages over HTTP. Never invents emails. Does not open LinkedIn.com.

#### Changed

- Console **Listed from / until** use a date picker instead of YYYY-MM-DD text fields.
- Console form as four steps: mode → optional buy box (dates, US states, cities, min/max price) → seller website → **Max deals** (visible, default 20).
- Store / Console title: **Off-Market Deals & Seller Contacts**. Short description names optional public-site email without selling it as the default export.
- Active listings: seller ratings fetched in parallel (was one HTTP call per deal, in series). Removed unused ID-walk checkpoint and no-op scraper teardown.
- Store README and run logs: buyer-first copy (Quick start, PPE tier table, FAQ, ASCII phase banners; no marketplace HTTP codes in default logs).

### \[1.9] - 2026-08-19

#### Changed

- Charge-then-export: `$0` budget or remaining PPE `0` does not write deal rows.
- Store categories: `REAL_ESTATE`, `LEAD_GENERATION` (not Automation / Developer Tools).

### \[1.8] - 2026-08-19

#### Changed

- **Free plan cap:** 20 deals per run (`APIFY_USER_IS_PAYING`). Status: `Free plan: 20 deals. Upgrade for bulk.`
- Anonymous Try-without-account runs disabled (`isAnonymouslyRunnable: false`).

### \[1.7] - 2026-08-18

#### Added

- **8 more Store Examples** (12 total) — date windows, seller enrichment, larger active/historical samples, numeric ID enrich.

### \[1.6] - 2026-08-17

#### Changed

- **README** — dedicated **Who is this for?** table (buyer + suggested mode).
- **Store SEO** — `seoTitle` includes `| Apify`; `seoDescription` trimmed to snippet length.

### \[1.5] - 2026-08-17

#### Added

- **4 published Examples** — Active listings, wholesaler outreach, sold/pending history, and URL enrichment. Each has a Store landing page under the Actor **Examples** tab.
- **Outreach dataset view** — wholesaler name, company, rating, and deal URL for CRM exports.

#### Changed

- **Default RAM 4096 → 512 MB** — HTTP + Cheerio only; 4 GB was unused compute. Paid runs and Try stay on 512 unless you raise memory in Run options.
- **Console Try input** — `exampleRunInput` is a real Active sample (`maxItems: 5`), not `{ helloWorld: 123 }`.

#### Fixed

- **Historical + maxItems** — stop after the requested row count instead of rating/enriching a full 5,000-deal page first. Published Examples stay fast.

### \[1.4.0] - 2026-03-11

#### Added

- **Property Detail API**: When enriching, uses `/api/properties/{id}` first for structured JSON (account_id, account.title, condition, description, main_image). HTML parsing as fallback if API returns 403/404
- **Seller rating**: `wholesaler_rating` and `wholesaler_review_count` from `/api/account-stats/{accountId}/rating`. Fetched when `account_id` is available (from API or page). Cached per account to avoid duplicate requests
- **Wholesaler from API**: `apiRowToItem` now uses API columns (`account_title`, `wholesaler_company`, `wholesaler_name`, `account_id`) when the properties list API returns them
- **account_id**: Output field for seller account ID. Extracted from Nuxt deal data, profile-photos URL, or API
- **enrichWithDetails in input schema**: Checkbox visible in Apify Console. Required for wholesaler columns to be populated

#### Changed

- **Full details scope**: Now enriches Historical and All modes (Phase 2), not just Active. Wholesaler, description, lot_size, condition populated for historical deals when enabled
- **fetchDealDetail**: Tries Property Detail API first, fallback to HTML page fetch + Cheerio parsing

#### Fixed

- **Wholesaler columns empty**: Root cause was (1) API columns ignored in parser, (2) Phase 2 never enriched. Both fixed

***

### \[1.3.0] - 2026-03-01

#### Added

- **Minimalist input UI**: Step-based layout (What do you want? → Data quality → Proxy). Airbnb Pro Host style
- **Auto-detect range**: Historical/All modes — start and end IDs derived from API. No manual Start/Stop IDs
- **256 workers** for historical (default). Up to 384
- **Proxy sticky per worker**: One proxy per worker for connection reuse
- **API timeout + retry**: 30s timeout, 2 retries on API fetch
- **429 handling**: Backoff on rate limit (5s, 10s) before retry

#### Changed

- **Puppeteer removed**: Switched to HTTP (got-scraping) + Cheerio. Faster, cheaper
- **Checkpoint**: Serialized writes (1 writer) to avoid Apify KVS 429. Every 2000 IDs
- **Full details**: Clarified — only affects Active mode. Historical/Specific already fetch full pages
- **README**: Aligned with minimalist UI, auto-detect range, resume steps
- **Input schema**: Removed from UI — useMaxIdFromApi, historicalIdStart, historicalIdEnd, detailConcurrency, historicalConcurrency, excludeIds (still supported via API)

#### Fixed

- **Too many parallel set requests**: Checkpoint writes serialized; single writer
- **Proxy pool init**: Parallel `newUrl()` calls for faster startup

***

### \[1.2.0] - 2026-03-01

#### Added

- **CHANGELOG**: Version history for the Store page
- **Modular architecture**: Code split into `api.js`, `parser.js`, `scraper.js`, and `utils.js` for maintainability and testability

#### Changed

- **README**: Rewritten in English with orias-scraper-style structure — input/output examples, use cases, local development, troubleshooting
- **Code organization**: Main orchestration in `main.js`; API fetch logic in `api.js`; page parsing in `parser.js`; HTTP/browser scraping in `scraper.js`; date/checkpoint utilities in `utils.js`

#### Removed

- **Dead code**: Unreachable HTML pagination branch (scrape mode is always `active_only`, `historical`, or `date_range` — all use API or historical loop)
- **Unused constants**: `MS_PER_DEAL`, `batchSize`
- **Unused functions**: `fetchPage`, `extractDealLinks` (legacy marketplace HTML pagination)
- **Unused input parsing**: `maxPages`, `startPage`, `currentPage` for non-existent mode

***

### \[1.1.0] - 2026-02

#### Added

- **Enrichment mode**: `enrichWithDetails` — visits each deal page with Puppeteer for description, condition, lot_size, wholesaler, img_url
- **API-first**: Active and date_range modes use marketplace API for fast bulk fetch
- **Proxy-chain**: Anonymized proxy for Puppeteer when using Apify residential proxy (fixes ERR_INVALID_AUTH)

#### Changed

- **Default timeout**: 7200s (2h) for enrichment runs
- **Detail concurrency**: Configurable parallel tabs (1–8, default 6)

***

### \[1.0.0] - 2026-02

#### Added

- Initial release
- **Three scrape modes**: `active_only`, `historical`, `date_range`
- **Bulk mode**: Scrape specific deals by ID or URL
- **Checkpoint & resume**: Persist progress for long runs; relaunch to continue after timeout
- **API integration**: InvestorLift marketplace API for active listings
- **Historical mode**: Iterate deal IDs with configurable range, optional max-ID detection
- **Date filtering**: Filter active listings by publication date
- **Blacklist**: Exclude deal IDs
- **Export**: JSON, CSV, Excel via Apify dataset
