# OPENLANE → mobile.de: repeatable search and dealer-review guide

**Snapshot documented:** 3–4 October 2026. **Scope:** OPENLANE Buy Now, documents country Germany or Belgium, displayed mileage below 200,000 km. This describes the work actually done, the files that preserve it, and the steps to repeat it. The earlier [screening runbook](OPENLANE_SCRAPPA_RUNBOOK.md) records additional search-planning details; this guide adds the signed-in review and the unified dashboard.

For the proposed next stage—about **700 signed-in detail captures** followed by a **Gemini 3.8 Flash** review—use [the 700-car collection and review plan](OPENLANE_700_DETAIL_AND_GEMINI_PLAN.md). That plan is prospective; this document remains the record of the completed snapshot.

## The result, and what it means

| Stage | Saved result |
| --- | --- |
| OPENLANE listing screen | 1,476 distinct visible cards, their original text and preview photos. OPENLANE's header said 1,482; six cards were not visible in the captured pages, so this is **all visible**, not a guaranteed complete inventory. |
| Scrappa/mobile.de search | 1,338 saved request attempts across the old and new archives; 22,209 returned ad occurrences and 16,587 distinct listing IDs in the shared database. Responses, including failures, remain archived. |
| Automated price screen | 840 cars with at least three close ads, 596 with broader model indication only, and 40 without a supported price. |
| Deeper OPENLANE review | The 100 highest **percentage** asking gaps at the default VAT assumption were opened in a signed-in session. Two had sold by review time. The signed-in page text, condition/delivery sections, report URLs and gallery references were saved. |
| Dealer assessment | Eight 1–5 scores, condition and equipment notes, cost evidence and next check for each of the 100. Fourteen composites are blank because key condition evidence was inadequate; 15 captures have limited condition text. |

Open the [single dashboard](openlane_market_prices_all_visible.html) for the full list and the 100 deeper reviews. Use **Show 100 dealer-reviewed cars**, then sort by price gap or any dealer score. The [screening CSV](openlane_market_screen_all_visible.csv) and [dealer-review CSV](openlane_top100_dealer_review.csv) are derived exports.

## 1. Capture the OPENLANE pool before searching prices

1. In an authorized OPENLANE session, apply **Buy Now**, documents country **Germany + Belgium**, and mileage **0–200,000 km**. Save the exact filtered URL, sort order, capture time and the site's reported count. The saved URL is in `openlane_buy_now_all_visible_raw.json`.
2. Page through **every visible result page**. Save each page capture separately (`openlane_capture_*.json`) as soon as it is read; do not rely on going back later, because listings and prices move. The first 300 were saved separately before the remaining pages were appended.
3. For each card, keep auction ID, result rank, full displayed title, make/model, additional descriptor, registration date, mileage, power, fuel, gearbox, body, documents country, Buy Now price and previous price, VAT label, badges, end time, photo count, image URL, detail URL, raw card text and captured HTML when available. The auction ID is the stable join key.
4. Check uniqueness, continuous visible ranks, filter membership and mileage. `extend_to_all_visible.py` performs those assertions when it combines the page files into `openlane_buy_now_all_visible_raw.json` and `openlane_buy_now_all_visible.sqlite`.
5. Preserve the original card and photo separately from later corrections. If a detail page contradicts a card, keep both with timestamps and flag the discrepancy; do not silently replace the original snapshot.

The card is a **preview**. It cannot establish accident history, exact van layout, mechanical condition, complete equipment, delivery price or total acquisition cost.

## 2. Reuse saved mobile.de ads before spending Scrappa credits

Scrappa searches returned mobile.de **search-result advertisements**, not complete seller pages. A result can include listing ID, title, advertised gross price, mileage, registration, engine, fuel, transmission, body, URL and some subtitle/highlights. It may omit important equipment, damage, VAT or seller details. Indexed web search can sometimes surface a more specific live advertisement because it uses a different index and query; that does not invalidate the saved Scrappa result, and a single web result is not a market sample.

The request strategy was:

1. Index every previously returned ad by listing ID. Retain every occurrence and raw JSON in the database, even if that ad is rejected for one car. Several OPENLANE cars can reuse one ad, so a new API request is needed only for missing coverage.
2. Normalize make/model family, fuel, transmission, registration year, mileage, power and **body/cabin configuration**. Treat cargo van, windowed passenger van, combi, crew cab, saloon, estate and SUV variants separately when the source identifies them. Mark uncertain van configuration unknown.
3. Plan searches for uncovered model families first. `plan_all_visible_searches.py` groups cars by family and nearby year/km; `plan_targeted_unpriced.py` targets remaining gaps by fuel/engine variant; `plan_close_ads.py` groups thin cars by family, variant, configuration and year/km bands. One search can serve multiple OPENLANE cars.
4. Prefer a precise model/variant query and known mobile.de make/model IDs. Add year and mileage bounds, limit to 20 results, and inspect whether the response reports more pages. A broad family query gives useful context but often misses the exact variant. A second page or narrower query is justified when the current matches are thin or clearly wrong.
5. **Dry-run the plan and set a hard request cap.** `run_scrappa_plan.py` skips parameter signatures already successful or rejected with HTTP 422 in its chosen archive. It appends the complete response, parameters, request time, status and errors to JSONL and flushes each record before making the next call. Its duplicate check covers the selected archive; compare older archives and the shared database too. HTTP 503 or transport errors may be retried after checking the archive.

Example for an already prepared plan:

```powershell
python run_scrappa_plan.py --plan scrappa_close_ads_plan.json --archive scrappa_all_visible_responses.jsonl --max-requests 20 --dry-run
python run_scrappa_plan.py --plan scrappa_close_ads_plan.json --archive scrappa_all_visible_responses.jsonl --max-requests 20
```

The real run reads `SCRAPPA_API_KEY` from the process environment. Never save the key in a plan, response archive, database, CSV or guide. The `--max-requests` value caps **new API calls**, including failed calls. The search endpoint returns at most 20 ads per request in this workflow. Search response count, matched-ad count and unique listing count are different numbers; keep all three.

## 3. Turn the saved ads into two distinct price signals

`price_openlane_300.py` loads the original 100-car searches, the grouped searches and the all-visible archive into a **shared ad pool**. It keeps every result occurrence in `scrappa_all_results_300.raw_json`, but takes the latest occurrence of each listing ID for current matching. A rejected ad stays stored and may fit another vehicle.

**Close ads:** `matched_comparables` in `price_openlane_cars.py` requires compatible model family and known body/cabin, same fuel and gearbox, registration within ±1 year, mileage within ±max(20,000 km, 30%), and engine power within the implemented tolerance. Ads marked damaged are excluded from this tier. It ranks eligible ads by title/spec and age/km closeness, uses at most 10 distinct ads and publishes a median only with **at least three**. The middle-half range and count remain visible. “High” means the sample count/spread passed a rule; it does not mean the OPENLANE car is sound.

**Broader model indication:** `estimate_all_visible_market.py` admits compatible same-family/fuel/body ads up to five years and 120,000 km away, preferring the same gearbox. It adjusts price for year, mileage and power, takes up to 10 ads, and assigns model-medium/low/very-low quality. It is a ranking aid where exact ads are scarce, not an independent verification. The broad tier can reuse the close ads.

The dashboard shows the direct median and broad indication separately. If both exist, its screening asking price weights the direct median about **60–75%** and the broad context **25–40%**, depending on ad count and disagreement. If only one exists, it uses that one and labels the basis. A large direct/broad disagreement is flagged.

**VAT and gap:** mobile.de prices here are advertised **gross asks**. The dashboard defaults to adding an illustrative **19%** to OPENLANE prices labelled `VAT excluded`; a margin-regime displayed price is not multiplied. The user can switch to 21% or 0% scenarios. `asking gap = screening gross asking price − OPENLANE price after assumed VAT`; percentage divides that gap by the VAT-adjusted OPENLANE price. This is **not expected profit**. Buyer fees, actual tax treatment, delivery, repairs, preparation, selling costs and negotiation are still unresolved. Never substitute an auction's *current bid* fee summary for the Buy Now total.

## 4. Select cars for deeper review

The saved `openlane_top100_pct_manifest.json` selected the 100 cars with the largest **asking-gap percentage** at the default 19% illustration from `openlane_market_screen_all_visible.csv`. It keeps each car's original result rank and its new selection rank, auction ID, prices, VAT regime, close-ad evidence, broad evidence and flags.

This is a **work queue, not a deal ranking**. Percentage ranking can lift very cheap damaged cars to the top. For example, an apparent several-hundred-percent gap became much less attractive after the signed-in condition section showed extensive rear and roof damage. Preserve the original screen so the reason for the selection remains auditable.

**A concrete trace:** auction `11630548`, a Citroën C3, showed **€1,200** in the margin regime. Ten rule-eligible ads gave a **€7,050** median; the broader adjusted indication was **€6,350**. The blended screen was **€6,900**, producing an apparent **€5,700 / 475%** asking gap. The saved request history includes a broad `Citroën C3` search and a later tighter year/km search with make/model IDs. Some selected ads describe a **1.2-litre/82-hp** C3 while the OPENLANE card says **1.0-litre/68-hp**: they passed the present power tolerance but are not exact engine twins. The signed-in condition text then showed large rear/roof damage and interior damage. It is a clear example of why both variant matching **and** condition review are needed before interpreting a high gap. The original ads, query parameters and report text remain saved under the auction ID.

## 5. Capture each selected car in the signed-in OPENLANE account

This stage was done in the user's authenticated Edge session, opening each URL `https://www.openlane.eu/en/car/info?auctionId=...`. It requires an account session; a public detail page does **not** expose all the same condition and delivery information.

For each auction ID:

1. Wait for that car's specifications/title to load. If OPENLANE says it was sold, record `sold_at_review=true`, click **Show it to me, anyway**, and then capture its historical detail. Do not treat it as available inventory.
2. Read the identity/specifications, documents, keys, inspection, service history, equipment and pickup location. Expand **Condition** and **Delivery options** and copy their complete displayed text. Record both actual delivery quotes and `no quote shown` explicitly, including destination and **ex-VAT** basis when quoted. A quote for the account's destination is not automatically valid for another buyer or address.
3. Save the complete signed-in page text, expanded section text, capture timestamp, report URL and every gallery image URL, joined by auction ID and selection rank. The append-only source is `openlane_top100_full_details.jsonl`. The 100 public-page captures in `openlane_top100_detail_pages.jsonl` remain as an earlier, less complete layer.
4. Keep contradictions between card and detail, external report references, unusually short/empty condition sections, missing documents, prior accident, non-drivable status and sold overlays as explicit flags. An empty condition field means **unknown**, not damage-free.
5. The **Download** link was captured as an authenticated `carreport?auctionId=...` URL for all 100. Browser PDF saving was not reliable in this run, so the PDFs themselves were **not** archived locally. The saved text and report links are the actual evidence available here; a future run should separately save/verify PDFs if required for bidding.

The local gallery copies and per-photo manifest are in `openlane_top100_galleries/` and `openlane_top100_gallery_manifest.jsonl`. All 3,353 referenced photos were saved and used for the dashboard carousel. The rapid dealer assessment below used the **descriptions**, not a claim that every photo or PDF page was visually inspected. Do not spend vision-model calls on all photos during first-pass screening; inspect them for finalists or conflicting damage descriptions.

## 6. Produce a conservative dealer assessment

`DEALER_ASSESSMENT_RUBRIC.md` defines eight 1–5 scores: condition, delivery burden, repair burden, preparation burden, equipment value, liquidity, buyer appeal and overall risk. Higher is better. The weighted composite uses **20/10/15/5/15/15/10/10%** respectively. Evidence level is separate: A = reports and key costs verified, B = report reviewed but estimates remain, C = partial report or key costs unknown, D = preview/public only. Leave a score or composite blank where the evidence cannot support it.

The 100 text-based assessments were completed in 20-car batches by lower-cost `gpt-6-sol` subagents, using the signed-in captures and the rubric. Their source records are `dealer_assessment_001_020.jsonl` through `dealer_assessment_081_100.jsonl`. Each row retains facts, flags, cost basis, a next step and the report link. `build_openlane_top100_dealer_review.py` joins these with the manifest and detail captures into `openlane_top100_dealer_review.json` and CSV; `build_market_report_all_visible.py` puts them on the same full-list dashboard.

Keep **delivery, repairs and preparation separate**. Preserve observed quotes as ex-VAT or gross as stated. Broad repair/prep allowances are labelled estimates; major or undiagnosed crash damage stays unpriced until a workshop quote. Do not calculate a dealer margin without achievable sale price, Buy Now acquisition total, buyer fees, transport, tax treatment, repairs, prep and selling costs. The eight scores organize review; they are not a vehicle inspection or liquidity measurement from actual sales.

### Finalist-only live market check

This was **not performed for every one of the 100**. For a car that survives the condition screen, use the exact engine, body/cabin, roof/length, gearbox, year, mileage and important options in an indexed search and on live mobile.de. Open promising seller ads, check gross/net VAT display, damage/history, seller type and current availability, and save the URL, retrieval date and why it is truly comparable. A search engine may find an excellent exact ad that a 20-result Scrappa page missed. Use it as an additional price point and potentially as a better targeted search query; do not replace the full saved ad distribution with one convenient listing. Compare achievable selling price, not merely the highest asking price, before making a bid decision.

## 7. Rebuild the current snapshot without buying new data

The files above are the source of truth. The HTML/CSV exports can be regenerated. From this folder, with the current source files intact:

```powershell
$env:OPENLANE_PRICE_DB = 'openlane_buy_now_all_visible.sqlite'
$env:OPENLANE_PRICE_CARS = 'openlane_buy_now_all_visible_raw.json'
$env:OPENLANE_PRICE_EXPORT = 'scrappa_all_returned_ads_all_visible.csv'
$env:OPENLANE_PRICE_AUDIT = 'scrappa_search_coverage_all_visible.csv'
$env:OPENLANE_PRICE_EXTRA = 'scrappa_all_visible_responses.jsonl'
python price_openlane_300.py
python estimate_all_visible_market.py
python build_openlane_top100_dealer_review.py
python build_market_report_all_visible.py
```

These commands make **no new Scrappa requests**. They rebuild database tables and derived exports from the saved archives. The top-100 manifest and assessments describe this dated snapshot; do **not** silently apply them to a newly captured pool. For a future run, use a dated copy or new snapshot directory, create a new manifest after pricing, then capture and assess those selected auction IDs. Keep the old snapshot available for comparison.

## Preservation and quality checks before calling a run complete

- Save original OPENLANE cards and every page capture **before** pricing. Keep the site's reported total, visible count, exact filters and capture times; investigate mismatches rather than inventing missing cards.
- Save every Scrappa request and full result JSON, including errors, duplicate appearances and rejected ads. Deduplicate **listing IDs within one valuation**, not by deleting observations. Record query parameters and source/ad IDs behind each price.
- Keep close and broad prices, sample size, ranges, dates and exact configuration evidence visible. If an indexed search finds a better exact ad, store its URL, date, specs and reason for inclusion as another evidence item; do not overwrite the original Scrappa pool.
- Link all stages by auction ID and time. Do not merge a later listing price, sold status or condition page into an earlier card without retaining the earlier version.
- Keep authenticated raw details private. They can include the account delivery destination; derived dashboard/review exports redact the specific street address. Do not put credentials or account details into a public report.
- Count unique cars, unique auction IDs, request attempts/statuses, returned rows, unique ad IDs, three-ad close coverage, missing prices, signed-in detail records, sold cars, limited condition records and saved image references. For this snapshot those counts are recorded above.
- Before bidding, manually verify the live OPENLANE listing, full report/PDF, current Buy Now total and fees, VAT treatment, documents, damage, exact variant, transport quote and live resale comparables. Treat the dashboard as a **triage tool** until those checks are complete.

**Known limits of this snapshot:** six header-count cards were not visible; some search results are sparse or broad; retail asks are not achieved sales; VAT is illustrative; two reviewed cars had sold; 15 condition captures are limited; report PDFs were linked but not locally saved; dealer cost estimates are not workshop or transport quotes. These limits should travel with any exported ranking.
