Normalizing Luxury Resale Attributes Across 7 Inconsistent Departments
Secondary luxury resale markets do not follow uniform product schemas. If you write scrapers for authenticated resale platforms like The RealReal, you immediately hit schema fragmentation across departments. Clothing use
Secondary luxury resale markets do not follow uniform product schemas. If you write scrapers for authenticated resale platforms like The RealReal, you immediately hit schema fragmentation across departments. Clothing uses standard apparel sizes (CLOTHING_SIZE or FOREIGN_SIZE), footwear uses shoe sizing (SHOE_SIZE), jewelry stores dimensions as RING_SIZE alongside METAL_TYPE, and watches store physical dimensions under CASE_DIAMETER.
Extracting these disparate properties into a clean analytics pipeline usually requires writing custom normalization logic for each product category. If you try to track resale margins or build pricing models across multiple luxury departments, raw API responses leave critical fields empty unless your scraper resolves department-specific attribute mappings.
The therealreal-scraper Actor solves this by consolidating department-specific attributes across seven top-level departments (women, men, jewelry, watches, art, home, and kids) into unified output fields like size and material, while surfacing specialized luxury metadata when present.
Resolving Department-Specific Attributes
When crawling authenticated listings, general-purpose scrapers often drop department-level metadata. The RealReal's internal facet architecture separates fields based on category context. For example, a collector tracking luxury timepieces needs movement mechanisms and reference numbers, while a jewelry analyst needs clarity and gemstone details.
The scraper maps these underlying attributes into concrete top-level fields:
-
Watches: Populates
watchModel,referenceNumber,watchCollection,movementType,caseMaterial,bandMaterial,watchStyles[], andcomplications[]. -
Jewelry: Populates
caratWeight(summed total carat weight across stones of that type),clarityGrade,colorGrade,gemstone,stoneType, andstoneShapes[]. -
Apparel: Populates category-specific attributes such as
dressSilhouette(e.g., "Wrap Dress", "Evening Gown") for dresses andchestSize(e.g., "42", "48 +") for men's garments. -
Condition: Separates the structured
conditiongrade (As Is,Fair,Good,Very Good,Excellent,Pristine) from the free-textconditionNotesfield available when fetching complete detail.
Because empty fields are omitted from emitted records, items only contain the attributes disclosed by the source listing.
Discovering Live Facets Before Extraction
Targeting specific categories or designers often fails when scrapers rely on hardcoded URL slugs. If a brand changes its catalog naming or a taxonomy node shifts, scrapers return zero results.
To prevent blind querying, the Actor provides two discovery modes:
-
discoverCategories: Reads The RealReal's live category tree straight from facet data, returning records withname,department,categoryPath,depth, and currentproductCount. -
discoverDesigners: Emits up to 200 designer names for a department or category, returningdesignerName,designerSlug,designerId, andproductCount.
Using discovery runs first ensures that downstream extraction runs target valid categoryPath and designerSlug inputs with active inventory.
Execution Walkthrough: Building a Filtered Watch Dataset
To collect listings for luxury watches matching specific physical attributes, configure the scraper to target the watches department with precise attribute filtering.
1. Configure the Run Input
Define the run parameters using the Actor's filtering fields. The configuration below queries the watches department, applies price boundaries, filters for automatic movements in stainless steel cases, and caps the volume using maxItems.
{
"mode": "byCategory",
"gender": "watches",
"minPrice": 2000,
"maxPrice": 15000,
"movementType": "Automatic",
"caseMaterial": "Stainless Steel",
"condition": ["Very Good", "Excellent", "Pristine"],
"sortBy": "price_asc",
"maxItems": 100
}
2. Execute via the Apify Python SDK
Run the Actor and process the dataset records programmatically:
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run_input = {
"mode": "byCategory",
"gender": "watches",
"minPrice": 2000,
"maxPrice": 15000,
"movementType": "Automatic",
"caseMaterial": "Stainless Steel",
"condition": ["Very Good", "Excellent", "Pristine"],
"sortBy": "price_asc",
"maxItems": 100,
}
run = client.actor("crawlerbros/therealreal-scraper").call(run_input=run_input)
dataset_items = client.dataset(run["defaultDatasetId"]).list_items().items
for item in dataset_items:
print(
f"{item.get('brand')} {item.get('watchModel')} "
f"| Ref: {item.get('referenceNumber')} "
f"| Price: ${item.get('priceUsd')} "
f"| Condition: {item.get('condition')}"
)
3. Handle Product Detail Payloads
When running in byUrl mode with explicit productUrls, the returned dataset items contain deep product data, including measurements[], authenticationInfo, carbonSavedKg, waterSavedLiters, and valuationReportUrl:
{
"recordType": "product",
"productId": "10984211",
"sku": "WCH12345",
"brand": "Rolex",
"name": "Submariner Date 116610LN",
"department": "watches",
"condition": "Excellent",
"conditionNotes": "Minor scratches throughout metal.",
"movementType": "Automatic",
"caseMaterial": "Stainless Steel",
"priceUsd": 11500,
"estRetailPriceUsd": 10250,
"productUrl": "https://www.therealreal.com/products/watches/rolex-submariner-date-116610ln",
"scrapedAt": "2026-07-27T10:15:30.000Z"
}
Actor Billing Mechanics
This Actor operates on a pay-per-event pricing model rather than standard compute runtimes. Charges are calculated directly from two event types:
-
Actor Start (
apify-actor-start): A flat charge of $0.005 per GB of memory allocated to the run, incurred once when execution begins. -
Dataset Result (
apify-default-dataset-item): Billed per emitted item in the default dataset. Pricing follows volume tiers:- FREE: $0.005 per event
- BRONZE: $0.00433 per event
- SILVER: $0.00367 per event
- GOLD: $0.003 per event
- PLATINUM: $0.003 per event
- DIAMOND: $0.003 per event
For example, on the standard FREE tier, a run allocated 1 GB of memory that extracts 500 watch listings incurs $0.005 for the start event and $2.50 for the 500 emitted results ($0.005 × 500).
Pipeline Boundaries
This Actor is designed for extraction and facet discovery; it does not monitor real-time shopping cart state or automate the checkout process. Additionally, while byUrl mode retrieves full condition descriptions and measurement arrays, markdown indicators like discountPercent are rarely present because the underlying browse listing API does not reliably expose markdown signals. Filtering pipelines requiring strict markdown verification must calculate discounts downstream by comparing priceUsd against originalPriceUsd or estRetailPriceUsd.
The RealReal Scraper is the Actor behind these examples. If a selector in your own version breaks, compare your output against the fields listed in its README first.
Prices quoted above are this Actor's published pay-per-event rates on the Apify Store, read from the Apify platform API on 2026-09-21. Check the Actor page for the current rates.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.