# Changelog of DBA Scraper \[Just 💰$2] — Denmark's Largest Marketplace (`blackfalcondata/dba-listings-scraper`) Actor

- **URL**: https://apify.com/blackfalcondata/dba-listings-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/blackfalcondata/dba-listings-scraper.md

## Changelog

All notable changes to DBA.dk Marketplace Listings Scraper are documented here.

### Unreleased — 2026-10-01

- Fixed: `sellerId` is now filled on detail-enriched listings (it was empty on every row). It is DBA's seller id; a shop's listings share one id.
- Fixed: `sellerName` falls back to the shop name from the listing page when the search result carries none.
- Changed: the `model` field is now documented as DBA's model group (for example "iPhone 17/Air/Pro/Max"); values are unchanged.

### Unreleased — 2026-09-30

- Fixed: when some result pages could not be read, the run now says so in its status message instead of finishing quietly with fewer listings than requested.

### 0.3.2 — 2026-09-06

#### Billing correctness across restarts and overlapping runs

- A run that the platform restarts mid-way no longer delivers a listing it had already delivered, and no longer overshoots `maxResults` or `maxResultsPerQuery` when it resumes.
- Two runs that share one incremental state no longer bill the same change twice: a run that resumes after another has moved the state on keeps what the other one delivered.
- An expiry that has already been reported is remembered as reported, so it is not reported a second time after a restart.
- The content hash on an EXPIRED row is now the hash of the version you were actually delivered.

#### Result caps are filled when they can be

- A listing several searches match now fills whichever of their caps has room, so a run no longer stops short while a valid assignment exists — including after a restart, and after a detail fetch fails and frees a slot.
- Repeated or differently spelled searches in one run are recognised as one search: scanned once, capped once, and sharing one incremental state instead of re-billing.

### 0.3.1 — 2026-05-20

#### EV-specific typed fields (detail-page enrichment)

Mobility detail-page parsing now promotes electric-vehicle spec rows from the raw `vehicleSpecs` dict into typed first-class fields:

- **`batteryCapacityKwh`** (number) — parsed from `Batterikapacitet` (e.g. "100 kWh" → 100).
- **`chargingPowerKw`** (number) — parsed from `Ladekapacitet` / `Maks. ladekapacitet` (e.g. "150 kW" → 150).
- **`accelerationSec`** (number) — parsed from `0-100 km/t` / `0-100 km/h` (e.g. "7,4 sek" → 7.4).
- **`drivingRange`** (number, existing field) — now overlaid from the detail-page `Rækkevidde` spec when present. The SERP-supplied value is still used as fallback. Detail-page values are typically more reliable than the API's `driving_range` field for older or imported EVs.

Previously these required parsing `vehicleSpecs["Batterikapacitet"]` etc. as raw strings — typed fields make them sortable, filterable, and ML-ready.

Dataset schema "all"-view exposes the new fields. No input changes; no breaking changes to existing fields.

### 0.3.0 — 2026-05-12

#### Behavior change

- **`omitNulls` default flipped to `true`.** Output rows now drop fields with null, empty string, empty array, or empty object values by default. Set `omitNulls: false` if you need a stable schema where every row has every documented key (the prior behaviour). Affects every row in every run.

#### Mobility (vehicle) detail page

- New parser reads the `<dt>/<dd>` spec table, description, postnummer+by, "Sælgers kendskab" Q\&A, synsrapport link, "Opdateret" timestamp, and dealer infokort (name, CVR, dealer-page URL, years on DBA, total ads, street address).
- New typed fields from the spec table: `bodyType`, `color`, `seats`, `doors`, `previousOwners`, `power` (HP), `engineSize` (L), `co2Emission` (g/km), `fuelConsumption` (km/L), `maxTowingWeight` (kg), `drivetrain`, `weight` (kg), `country`, `firstRegistrationDate`, `lastInspectionDate`, `nextInspectionDate`, `interiorColor`, `vehicleCategory`, `vehicleCondition`, `vehicleType`.
- Boat-specific: `boatBerths`, `boatSeats`, `hullMaterial`, `boatDraft` (cm), `topSpeed`, `motorManufacturer`, `motorIncluded`.
- Detail-page extras: `description`, `inspectionReportUrl`, `streetAddress`, `updatedAt`.
- Dealer fields: `dealerCvr`, `dealerPageUrl`, `dealerYearsOnDba`, `dealerTotalAds`.
- Raw maps preserved: `vehicleSpecs` (full label → value spec table), `sellerKnowledge` (raw Q\&A).

#### Recommerce (marketplace) extras

- Typed columns now promoted from category-prefixed extras (verified against 1320 listings across 320 sub-categories):
  - `brand` matches any `*_brand` extra (`lego_brand`, `shoes_brand`, `mobile_brand`, …)
  - `model` matches `*_model`
  - `size` matches `*_size` (`shoe_size`, `women_clothing_size`, `mobile_memory_size`, …)
  - `subType` matches `*_type` excluding `ad_type` (`shoe_type`, `hifiparts_type`, …)
  - `color` from `colour`/`color` (now shared with mobility)
  - `material` matches `*_material` (`bags_purses_material`, `jewellery_main_material`)
  - `gender` matches `gender` or `*_gender`
  - `theme`, `designer`, `age`, `season`, `platform` (gaming via `games_group`)
  - `depth`, `height`, `width`, `length` (cm — furniture)

#### Shared / cross-vertical

- `zipCode` now appears on mobility output (previously stripped — bug).
- `coordinateAccuracy` populated from SERP coords on both subverticals.
- `mapImageUrl` extracted from recommerce detail JSON.

#### SERP filter

- Added `fiksFerdig` input flag — maps to `shipping_exists=true` URL param. Restricts marketplace SERP to listings with DBA Fiks færdig (transactable shipping via DBA Pay). ~62% of recommerce listings qualify.

#### Internal

- Mixed-mode runs (paste-mode with both marketplace and mobility URLs) now strip per-row by config-of-origin instead of leaving synthetic `false` defaults from the wrong subvertical on each row.
- `Placering` parser scoped to its section instead of full body text to avoid false captures from description content.
- `inspectionReportUrl` resolved as absolute URL.
- New `parseLengthCm` helper handles Danish comma decimals, English period decimals, m → cm conversion, and bare numbers.
- 26 new unit tests in `tests/transform.test.ts` covering extras matching, strip-set integrity, and length parsing.

### 0.2.1 — 2026-05-11

- Fixed: Marketplace `priceMin`/`priceMax` filters now actually narrow results to the requested price range. Previously they were accepted but ignored.
- Fixed: Marketplace `sellerType` (`private` / `dealer`) now actually filters to the requested seller type. Previously it was ignored.
- Strengthened: Canary tests now compare every filtered result count against the unfiltered baseline, so a future regression where a filter is silently ignored fails the test instead of passing on "any docs returned".

### 0.2.0 — 2026-05-11

- Fixed: Vehicle range filters (`yearFrom`/`yearTo`, `priceMin`/`priceMax`, `mileageFrom`/`mileageTo`) now return results reliably. Previously these combinations silently returned zero rows.
- Fixed: Vehicle categorical filters (`fuel`, `transmission`, `dealerSegment`) now actually filter results. They previously had no effect on the returned dataset.
- Improved: `fuel` and `transmission` are now dropdowns with the full set of supported values (Plug-in Benzin, Plug-in Diesel, Brint, etc.).
- Added: Live canary test suite — verifies each filter actually applies on every release.

### 0.1.x — 2026-04-14

- Added: `descriptionHtml`, `descriptionMarkdown` output fields (triple-format descriptions for RAG/LLM pipelines)
- Added: `contentHash` output field (stable hash over content-identifying fields, used for change detection)

### \[0.1] — 2026-03-22

#### Initial release

- SERP scraping via DBA.dk `/recommerce/forsale/search` endpoint
- JSON-LD structured data extraction (`CollectionPage.ItemList` + `Product` schema)
- 35 output fields per listing: title, price, condition, region, GPS coordinates, up to 10 images, seller info, category path, shipping/buy-now flags
- Detail enrichment: full description, nested category tree, zip code, product attributes
- Filters: category, condition, region, price range (min/max), seller type, sort order
- Pagination with configurable `maxResults` (0 = unlimited, max 5000)
- Incremental mode: track new/changed listings across scheduled runs via KV state
- Compact output mode for AI-agent and MCP workflows
- `descriptionMaxLength` truncation
- PAY_PER_EVENT pricing: `actor-start` + `result` events
- Efficient low-cost delivery
- 256 MB memory footprint
