# Changelog of Uber Eats · Restaurant Emails by City (`corent1robert/uber-eats-pro-scraper`) Actor

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

## Changelog

All notable changes to this project will be documented in this file.

### \[1.6.0] - 2026-08-20

#### Added

- Restaurant **website hop** (homepage, legal, contact) when **Collect emails** is on and Uber has no owner inbox — including pages without JSON-LD
- `emailSource` (`uber_page` / `restaurant_website`) and an **Outreach** dataset view
- City catalog fallback: cuisine filter empty → full city list; JSON-escaped store URLs in the listing payload

#### Changed

- City Try no longer defaults to Fast food (empty cuisine = all restaurants)
- Listing retries with regional routing when the city catalog is blocked
- Collect emails copy: legal blocks + website hop; relay inboxes stay flagged, not hidden

### \[1.5.0] - 2026-06-06

#### Changed

- Store SEO: title/description/seo\* — city-search hook, remove jargon (`relay-flagged`), ~152-char `seoDescription` with outreach keywords
- Console input UX: honest **email disclosure (proxy)** copy, mode-specific sections (City / Territory / URL), `Search mode` default City first
- `verboseLogs` removed from Console input form — API-only (`false` by default)

#### Added

- **Pay-per-event two-tier pricing:** `uber-eats-restaurant-with-email` (direct inbox) vs `uber-eats-restaurant-no-email` (no email or relay) via `Actor.pushData(..., eventName)`
- `pricingInfo` in `actor.json` with `apify-default-dataset-item` at **$0** (no double charge)

#### Changed

- README pricing table documents PPE tiers and Console monetization setup

***

### \[1.4.0] - 2026-06-06

#### Added

- `storeId` / `storeSlug` — stable CRM dedup keys from store URLs
- `legalName` — trade name from EU legal disclosure block
- `openingHours` — compact hours string from JSON-LD
- `website`, `instagram`, `facebook` — from JSON-LD `sameAs` / `url` when present
- README **CRM & outreach** workflow section

#### Changed

- Console dataset **overview** view trimmed for outbound review (name, email, relay flag, phone, company ID)
- Store SEO copy highlights relay-flagged emails and opening hours

***

### \[1.3.0] - 2026-06-06

#### Added

- `isRelayEmail` on each row — flags `@uber.fr` and `@net.mcdonalds.fr` forwarding inboxes for outreach filtering
- `RUN_LOG` key-value output and `verboseLogs` input
- `Actor.setStatusMessage` milestones on cloud runs
- City mode **progressive export** — dataset fills after each city completes
- `maxResults: 0` — no row ceiling (Territory and City)
- Monorepo scripts: pull, local smoke, dataset CSV export

#### Changed

- **Collect emails** default **off** — faster, cheaper first runs; phones/addresses unchanged
- Default memory **1024 MB** (was 4096) for HTTP-only workload
- Detail concurrency **5 / 60 rpm** without proxy; **3 / 30 rpm** with email routing
- Store copy: `seoTitle`, `seoDescription`, benefit-first input descriptions
- README: ICP-first hero (suppliers, outbound, research), personas table, `isRelayEmail` fill rates, pricing, legal, interlinks

#### Fixed

- `apify pull` workaround documented via tarball download script

***

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

#### Added

- Email extraction from `__REACT_QUERY_STATE__` script tag (Uber Eats `disclaimerBadge.text`)
- URL-encoding normalization (`%5Cn` → space) to correctly isolate emails in JSON strings
- `failedRequestHandler` to log 429 and other request failures
- `isValidEmail` rejects emails containing `%` (URL-encoded artifacts)

#### Changed

- Removed `sameDomainDelaySecs` — was blocking concurrency after each batch, causing ~60s pauses between groups of 5
- `maxConcurrency` raised to 5, `maxRequestsPerMinute` to 60 (no more artificial serialization)
- `RequestList.open({ sources })` replaces `open(null, sources)`
- `extractEmailFromScriptTag` now falls back to regex search on raw content when JSON.parse fails (handles `__REACT_QUERY_STATE__` nested-JSON issue)

#### Removed

- Dead code: `runListerModeHtml`, `extractRegionLinksFromHtml`, `extractCategorySlugsFromHtml`, `UBEREATS_CATEGORIES`, `RegionLink`
- All debug scripts (`debug-email.js`, `debug-timing.js`, `debug-jsonld.js`, `test-email-extraction.js`)
- Example input files (`input-exhaustive.json`, `input-simple-halal.json`)

#### Fixed

- `dataset_schema.json` — added descriptions to all fields
- `actor.json` — linked dataset schema, improved table view order, `url` rendered as clickable link

***

### \[1.1.0] - 2026-02-20

#### Added

- City mode: auto-detect country from city name (no need to specify `country`)
- Support for `fr:Annecy` syntax to force a specific country in City mode
- Progress logging: `Scraped X/N (Y%)` on each result
- Proxy support (`proxyConfiguration` input) for Apify Cloud email extraction
- `findEmailInObject`: recursive email search inside `__NEXT_DATA__` and `__REACT_QUERY_STATE__`
- `extractEmail` tries JSON-LD → `__NEXT_DATA__` → `__REACT_QUERY_STATE__` in order
- `maxRequestRetries: 3` on CheerioCrawler

#### Changed

- Default `country` / `region` changed from `us` to `fr`
- `cityNames` description updated: case and accent-insensitive matching
- Proxy description updated to explain Uber Eats datacenter IP behavior
- `maxConcurrency` 5→2, `maxRequestsPerMinute` 15→30 (later reverted, see 1.2.0)

#### Fixed

- City mode was defaulting to `country: "us"` even for French cities
- Duplicate `(locale, citySlug)` pairs in city mode now deduplicated via `processedSlugs` Set

***

### \[1.0.0] - 2026-01-15

#### Added

- Initial release
- **Territory mode**: scrape all restaurants in a country/region using `data/reference.json`
- **City mode**: scrape by city name with optional category filter
- **URL mode**: scrape a list of provided store URLs directly
- Schema.org JSON-LD extraction: name, phone, address, GPS, cuisines, rating, price range, cover image
- SIRET and VAT extraction from legal disclaimer section
- Phone formatting and landline/mobile classification (French numbers)
- CSV export (`output.csv`) for local runs
- ETA display for large Territory mode runs
- `data/reference.json` with cities/regions for FR, DE, ES, IT, BE, NL
- `scripts/update-reference.js` to refresh reference data
- `scripts/generate-input-schema.js` to regenerate `.actor/input_schema.json`
- Full Apify Actor configuration (actor.json, input\_schema.json, dataset\_schema.json, Dockerfile)
- Unit tests for all extractors (`src/extractors.test.ts`)
