Compare listings against the previous run for the same scope (state key) and only emit NEW or UPDATED rows (plus optional EXPIRED tombstones). Unchanged listings are skipped and not charged. A listing previously marked EXPIRED that is found again is emitted as REAPPEARED with its original firstSeenAt. What drives UPDATED: every emitted listing field except the exclusions below and run bookkeeping (scrapedAt, changeType, changedFields, firstSeenAt, lastSeenAt) - e.g. the services menu (names, prices, durations), rating, reviewsCount, address and coordinates, images, amenities, social links. Never compared, so they never mark a row UPDATED on their own (they rotate or change on their own without the listing changing): portfolioImages, recentReviewers, openingStatus, openingStatusDetails, closed, workingHours, instagramMediaCount, groupingScore, topReviewedServices, reviewTeamMembers. Review order is ignored too: reviews are compared as an orderless set of id, rating, text, date and reply. Extend the exclusions with ignoreFieldsForChanges.