← Back to list

How I Compare Birkin/Kelly and Chanel Classic Flap Resale Prices Across Platforms

I do not trust one resale listing by itself.

DataKazKN · 2026-06-18 23:35 · 0 claps · 8.4 min read
#web-scraping #apify #luxury-resale #data-analysis #price-tracking
Open on Medium ↗
Wiki topics: 👗 · Fashion

How I Compare Birkin/Kelly and Chanel Classic Flap Resale Prices Across Platforms

I do not trust one resale listing by itself.

A Birkin 30 at $16,500 can look cheap until you compare the leather, hardware, condition notes, fees, shipping, tax, repair buffer, authentication buffer, and the price of similar bags on other marketplaces. A Chanel Classic Flap can look fairly priced until you notice that the same size, leather, and condition band sits lower somewhere else.

That is the whole problem I wanted to solve.

I built an Apify Actor that compares public resale listings for Hermes Birkin, Hermes Kelly, and Chanel Classic Flap bags across Vestiaire Collective, Rebag, Fashionphile, and 1stDibs:

Run the Hermes Birkin/Kelly + Chanel Classic Flap resale comparison Actor on Apify

The goal is not to replace authentication. It is not a sold-comps database. It is not an investment advisor. It is a repeatable way to build a cleaner shortlist before doing human due diligence.

The short version:

  • Scrape active public listings from four resale platforms.
  • Normalize model, size, color, leather/material, hardware, condition, and price.
  • Group comparable bags.
  • Calculate the gross price gap before costs.
  • Estimate net ROI after fee and cost assumptions.
  • Mark each row as a listing, review candidate, strict opportunity, watchlist match, unmatched listing, or run summary.

That workflow sounds simple. In practice, the details matter.

Why manual resale comparison breaks quickly

Most resale research starts as a browser tab problem.

You open Vestiaire. Then Rebag. Then Fashionphile. Then 1stDibs. You search “Birkin 30 black”, copy prices into a spreadsheet, then realize the rows are not actually comparable.

One listing says “Very good”. Another says “Excellent”. One exposes hardware in the title. Another hides it in the detail page. One has Togo in the title. Another only says leather. One price is low because the condition is lower. Another price is high because the bag is rare, full set, or simply overlisted.

This gets worse with Chanel Classic Flap because “Classic Flap” can mean mini, small, medium, jumbo, single flap, double flap, lambskin, caviar, seasonal colors, vintage, current series, full set, missing accessories, or condition issues.

The community language around this is consistent: people want market context before they buy. They ask whether a price is fair, whether resale is better than retail, whether seller photos are enough, whether the condition report is hiding something, and whether one platform is pricing the same type of bag higher than another.

That is why I prefer a dataset first.

Not because the dataset makes the decision. Because it makes the decision auditable.

The workflow

The Actor runs the same basic process every time.

1. Scan marketplaces

It searches public listings on:

  • Vestiaire Collective
  • Rebag
  • Fashionphile
  • 1stDibs

For the first run, I usually keep it broad:

{
  "platforms": ["vestiaire", "rebag", "fashionphile", "1stdibs"],
  "models": ["birkin", "kelly", "classic_flap"],
  "watchlistMode": "broad_scan",
  "maxListingsPerPlatform": 60,
  "maxPagesPerPlatform": 1,
  "minSpreadPercent": 12,
  "minNetRoiPercent": 8,
  "shippingEstimate": 150,
  "repairBuffer": 250,
  "fxBufferPercent": 2,
  "rebagCommissionPercent": 20,
  "firstDibsCommissionPercent": 20,
  "includeUnmatchedListings": true
}

The two limits matter:

  • maxListingsPerPlatform controls how many listings are kept from each marketplace.
  • maxPagesPerPlatform controls how deep the Actor paginates.

For a first scan, one page and 60 listings per platform is enough to see whether the output is useful without creating a heavy run.

2. Normalize condition and attributes

The raw listing titles are messy.

The Actor tries to normalize the fields that matter for comparison:

  • brand
  • model
  • size
  • color bucket
  • material bucket
  • hardware bucket
  • condition bucket
  • price
  • currency
  • listing URL
  • platform
  • source completeness

The condition field is especially important.

If one platform says “Excellent”, another says “Very good”, and another only exposes condition on the listing detail page, the scraper has to normalize those into consistent buckets before a comparison is even possible.

That does not make the condition true. It makes the platform’s public condition language easier to compare.

You still need to inspect photos, corners, handles, glazing, hardware, interior, odor notes, return policy, and third-party authentication if you are actually buying.

3. Group comparable bags

The Actor creates match groups.

For strict matches, it wants core fields to line up. For review candidates, it can relax some fields when the data is incomplete but still useful.

That distinction matters.

A strict opportunity is a tighter comp. A review candidate is a signal. I want both in the dataset, but I do not want them mixed together.

If a Birkin 30 brown very-good listing is cheap on one platform and expensive on another, that can be a useful signal. But if one listing says Togo gold hardware and the other only says brown leather with unknown hardware, I do not want the output pretending those are identical.

So the Actor flags the row with warnings.

4. Review spread and ROI

The Actor calculates two gates:

  • grossSpreadPercent: price gap before costs.
  • estimatedNetRoiPercent: estimated profit after costs.

Gross spread is the simple market gap.

grossSpreadPercent = (sell reference price - buy candidate price) / buy candidate price

Estimated ROI is stricter.

estimatedNetRoiPercent = estimated net profit / landed buy price

The landed buy price includes assumptions such as shipping, repair/authentication buffer, and FX/tax buffer. The sell-side value deducts platform fee assumptions.

The point is not to predict profit perfectly. The point is to avoid wasting time on “spreads” that disappear the moment you add costs.

A real example from one run

On June 16, 2026, I ran a broad scan with all four marketplaces, all three model families, one page per marketplace, and 60 listings per marketplace.

The run returned:

  • 128 normalized listings
  • 5 spread candidates
  • 0 strict arbitrage opportunities
  • 118 watchlist candidates
  • 10 unmatched listings
  • 1 platform error on 1stDibs

The zero strict opportunities are not a failure. That is the filter doing its job.

Here is one review candidate from the dataset:

{
  "recordType": "spread_candidate",
  "matchKey": "relaxed:model_size_color_condition:hermes:birkin:30:brown:very_good",
  "model": "birkin",
  "size": "30",
  "colorBucket": "brown",
  "materialBucket": "togo",
  "hardwareBucket": "gold",
  "conditionBucket": "very_good",
  "platformsCompared": ["rebag", "vestiaire"],
  "buyCandidate": {
    "platform": "rebag",
    "price": 16500,
    "currency": "USD",
    "title": "Hermes Birkin Handbag Chocolate Togo with Gold Hardware 30"
  },
  "sellReferences": [
    {
      "platform": "vestiaire",
      "price": 32915,
      "currency": "USD",
      "title": "Hermes Birkin 30 leather tote"
    }
  ],
  "grossSpreadPercent": 99.48,
  "estimatedNetRoiPercent": 73.61,
  "matchStrictness": "relaxed_model_size_color_condition",
  "warnings": [
    "Review candidate, not a strict arbitrage match.",
    "ROI is an estimate from public listing prices, not guaranteed profit.",
    "Material and/or hardware were relaxed for matching.",
    "Verify exact leather, hardware, photos, condition notes, authenticity, fees, and liquidity before buying."
  ]
}

That is the type of row I want to see.

Not because I would automatically buy it. I would not.

I want to see it because it tells me where to inspect further. The gross spread is large. The ROI estimate is large. But the match is relaxed because the public listing data does not fully align on material and hardware.

That row belongs in a human review queue, not in an automatic-buy queue.

What I check first in the output

The dataset can get large, so I do not start by reading every listing.

I check records in this order:

  1. run_summary
  2. spread_candidate
  3. arbitrage_opportunity
  4. watchlist_candidate
  5. listing_review
  6. unmatched_listing
  7. platform_error

Then I check five fields:

recordType

This tells me what kind of row I am looking at.

If it is a spread_candidate, I know the Actor found a price signal but the match may be relaxed.

If it is an arbitrage_opportunity, the comparison passed stricter gates.

If it is listing_review, it is a normalized listing row useful for spreadsheets and market snapshots.

matchKey

This tells me which attributes were used to group listings.

If the key includes model, size, color, and condition, I know the row is trying to compare similar items. If the row has relaxed fields, I read the warnings before trusting the comp.

grossSpreadPercent

This is the price gap before costs.

It answers: “Is there enough visible spread to care?”

estimatedNetRoiPercent

This is the profit signal after cost assumptions.

It answers: “Does the spread still look interesting after fees and buffers?”

warnings

This is the most underrated field.

The warnings tell me when the row is not strict enough, when data is missing, or when manual checks are required. For luxury resale, a good warning field is more valuable than a false sense of precision.

Broad scan vs strict target

I use two modes.

Broad scan is for discovery.

It answers questions like:

  • Which sizes are showing up right now?
  • Which platforms have lower asking prices?
  • Where are the review-worthy spreads?
  • Which listings are too incomplete to match?

Strict target is for a known buyer brief.

For example:

Birkin 25 black Togo gold hardware, minimum very good condition

Or:

Chanel Medium Classic Flap black caviar gold hardware

Strict target mode is useful when the buyer already knows what they want. It keeps the scan focused and makes the dataset easier to review.

What this does not solve

This part matters.

The Actor does not authenticate bags.

It does not know sold prices.

It does not know whether a seller will accept an offer.

It does not know whether the listing photos are complete.

It does not know whether the item has odor, corner wear, handle darkening, glazing issues, missing accessories, or return restrictions unless those details are exposed in the public listing data.

It also does not know whether demand exists at the reference price.

That is why I separate price tracking from authentication and liquidity checks.

The Actor helps me answer:

Should I inspect this comp manually?

It does not answer:

Should I buy this bag?

That second question still belongs to a human.

Why this is useful for resellers, buyers, and analysts

For a reseller, the value is speed.

You can run a daily scan, export the dataset, and review candidates before manually checking the listings. That is better than repeating the same marketplace searches every morning.

For a personal shopper, the value is focus.

You can set a strict target and monitor whether matching public listings appear across several platforms.

For a buyer, the value is context.

You can sanity-check whether a quote looks high or low against current public asking prices. You still need authentication, but at least you are not judging one listing in isolation.

For an analyst or developer, the value is structure.

The output can feed Google Sheets, Airtable, Slack, dashboards, Apify webhooks, or API clients.

How to run it on Apify

The Actor is here:

https://apify.com/kazkn/hermes-birkin-kelly-arbitrage

My recommended first run:

  • marketplaces: Vestiaire, Rebag, Fashionphile, 1stDibs
  • models: Birkin, Kelly, Classic Flap
  • targeting mode: broad scan
  • max listings per marketplace: 60
  • max pages per marketplace: 1
  • minimum price gap before costs: 12
  • minimum profit after costs: 8
  • include unmatched listings: true

After the run, open the dataset and filter by recordType.

Start with run_summary, then spread_candidate, then arbitrage_opportunity.

If the output looks useful, schedule it daily.

FAQ

Is this an authentication tool?

No. It compares public listing data. Authentication is a separate step.

Are these sold comps?

No. The Actor uses active public asking prices. Asking prices are useful for market context, but they are not the same as sold prices.

Why can strict opportunities be zero?

Because strict matching requires enough comparable data. If size, condition, material, hardware, or other fields are missing or inconsistent, the Actor may classify a row as a review candidate instead.

That is a good thing. I prefer fewer strict rows over fake precision.

Why compare four platforms instead of one?

One platform only tells you how that platform is priced.

Cross-platform comparison helps show whether a listing is unusually high, unusually low, or simply normal for the current public market.

Can I use it only for one target bag?

Yes. Use strict target mode when you know the exact model, size, color, leather/material, hardware, and minimum condition.

What is the difference between gross spread and estimated ROI?

Gross spread is the price gap before costs.

Estimated ROI deducts fee and cost assumptions from the comparison. It is a stronger filter, but still only an estimate.

Does this guarantee profit?

No. A resale listing can be overpriced, stale, incomplete, misdescribed, or hard to resell. Treat the output as a shortlist for manual review.

Final thought

Luxury resale research is messy because the data is messy.

The mistake is pretending the mess does not exist.

I would rather have a dataset that says “this looks interesting, but check material and hardware” than a spreadsheet full of copied prices with no warning labels.

That is what I built this Actor for: faster market context, cleaner comparisons, and fewer blind guesses.

Run the Actor on Apify


메타데이터
post_id
d5dec0482c4a
slug
how-i-compare-birkin-kelly-and-chanel-classic-flap-resale-prices-across-platforms-d5dec0482c4a
url
https://medium.com/@DataKazKN/how-i-compare-birkin-kelly-and-chanel-classic-flap-resale-prices-across-platforms-d5dec0482c4a
canonical_url
https://medium.com/@DataKazKN/how-i-compare-birkin-kelly-and-chanel-classic-flap-resale-prices-across-platforms-d5dec0482c4a
author_url
https://medium.com/@DataKazKN
status
ok
fetched_at
2026-06-22 12:55:45