Chrono24 API

The same reference, priced by a hundred different sellers

The Chrono24 API returns luxury-watch marketplace data as clean JSON.

no credit card1,000 free credits · instant API key · pay by card or crypto
Missing a Chrono24 endpoint, or need a source we don't have yet?Contact us real people · same-day reply.
C
/chrono24/v1

3 active endpoints. Every call is 1 credit.

  • POST/chrono24/v1/search
  • POST/chrono24/v1/browse
  • POST/chrono24/v1/detail

What Chrono24 endpoints does ReefAPI ship?

3 live read endpoints. Read-only data API: no writes, no account actions, no dashboard access on the target site.

3 endpoints

search

1 cr

Search Chrono24 by free-text keyword (brand + model + reference, e.g.

required
query
optional
page, price_min, price_max, condition, movement, year, sort

browse

1 cr

Browse all listings for a brand (e.g.

required
brand
optional
model, page, price_min, price_max, condition, movement, year, sort

detail

1 cr

Full watch listing by url OR listing_id.

required
optional
url, listing_id

Every parameter, every allowed value →

Chrono24 API

3 of 3 endpoints, ready to run

View docs ↗

Free-text search across the marketplace — brand, model and reference in one string. Each card carries the listing id, title, price, currency, image and whether it is still in stock, and the response adds the price range and the result count for the query.

1 credit1 required · 7 optional
POST/chrono24/v1/search
ok4544 ms · 60 records · sample
{
  "ok": true,
  "meta": {
    "api": "chrono24",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 4543.7,
    "record_count": 60,
    "cache_hit": false,
    "completeness_pct": 100
  },
  "data": {
    "listings": [
      {
        "listing_id": "46046973",
        "title": "Rolex 16808/16618 Ghiera in Blist blu",
        "price": 650,
        "currency": "USD",
        "url": "https://www.chrono24.com/rolex/1680816618--id46046973.htm",
        "image": "https://img.chrono24.com/images/uhren/q9pn7jy4yxbb-gshj7507nuycyjgtf1ce6rwo-ExtraLarge.jpg",
        "availability": "InStock"
      },
      {
        "listing_id": "47509925",
        "title": "Rolex Submariner Ancora,submariner oro giallo",
        "price": 776,
        "currency": "USD",
        "url": "https://www.chrono24.com/rolex/submariner--id47509925.htm",
        "image": "https://img.chrono24.com/images/uhren/beu3hk155xxe-4eupeqa9d079i9dta28sqnbw-ExtraLarge.jpg",
        "availability": "InStock"
      },
      {
        "listing_id": "48229816",
        "title": "Rolex 1680 Oyster Stainless Steel Black 36mm Rolex Submariner Ref. 1680 | Cal. 1575 | Black Dial & Bezel | Oyster Bracelet",
        "price": 995,
        "currency": "USD",
        "url": "https://www.chrono24.com/rolex/1680-oyster-stainless-steel-black-36mm--id48229816.htm",
        "image": "https://img.chrono24.com/images/uhren/48229816-xxsrpvafk4nwuhtlyfxwqqhl-ExtraLarge.jpg",
        "availability": "InStock"
      }
    ],
    "aggregate": {
      "low_price": "7701",
      "high_price": "311700",
      "currency": "USD",
      "offer_count_on_page": "60",
      "total_results": 9110
    }
  }
}
Real response, fetched from the live endpoint with the parameters on the left — trimmed to the first few rows, with seller names left out. Press Try it for the untrimmed response.

How the Chrono24 API works

Chrono24 is a normal ReefAPI surface — the same four rules that hold for every other engine on the key.

01
Authenticate
x-api-key header

No OAuth app, no request signing, no per-site account. One key covers all 184 engines.

02
Call
POST /chrono24/v1/…

Every route is a POST with a JSON body. Parameters are validated against the published schema before anything is charged.

03
Pay
1 credit per call

Credits, not seats. Failed and blocked calls are never charged, and cache hits cost nothing.

04
Read
{ ok, data, meta, error }

One envelope everywhere. meta carries latency_ms, record_count and the endpoint that answered.

From a model family to the one listing worth writing to

Prices for a single reference spread out enormously, and the spread is mostly condition, completeness and where the watch is. The rows tell you the price; only the listing tells you what you are buying.

01browse
POST/chrono24/v1/browse
{"brand": "omega", "model": "speedmaster", "price_max": 6000}

Sixty cards a page for the whole family under your ceiling, each with a listing id and the price the seller is asking today.

02detail
POST/chrono24/v1/detail
{"listing_id": "25251343"}

The reference number that makes the listing comparable to any other, the condition in the seller's own words, whether the box and papers are there, and which country it would ship from.

A price comparison anchored on a reference number rather than a title, with the two things that actually explain the spread attached to every row.

request
curl -X POST https://api.reefapi.com/chrono24/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"rolex submariner"}'
response envelope
{
  "ok": true,
  "data": { … },
  "meta": {
    "api": "chrono24",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": …,
    "record_count": …
  },
  "error": null
}

Chrono24 fields: reference_number vs listing_id, and the numbers that mislead

Watch buyers search by reference number and developers page by listing id, and confusing the two is the most common Chrono24 integration bug. The second trap is the aggregate block, which looks like a summary of your query and is not. Every value below came from live search, browse and detail calls on 2026-08-27.

FieldWhat it isMeasured example
listing_idOne seller's offer. A relisted watch gets a new one46892665, taken from the URL suffix --id46892665.htm
reference_numberThe manufacturer's model reference. Many listings share it, and only detail returns it"16800" for a Rolex Submariner Date
listing_codeAn internal Chrono24 model code returned by detail but absent from the documented field list"RX2M16"
price / currency (detail)Numeric price and its currency7732.0 / "USD"
listings[].currency (search, browse)null on every card measured, 60 of 60use aggregate.currency instead, which returned "USD"
aggregate.total_resultsSite-wide listing count, not the size of your result set671,486 / 671,485 / 671,497 across three unrelated queries
aggregate.low_price / high_priceStrings from the page's aggregate offer that did not match the page's own listingsa price_asc Submariner page whose cheapest card was 5,500 reported low_price "8283"
condition / condition_detailThe filterable token, then the seller's own wording"used" / "Used (Fair) The item shows major, visible signs of wear like scratches and dents."
yearFree text rather than an integer, and it can carry a qualifier"1986 (Approximation)"
scope_of_deliveryBox and papers status; the leading sentence is clean and the tail leaks page markup"No original box, no original papers" followed by escaped HTML
seller_locationA postal-address object with country and city only{"addressCountry": "JP", "addressLocality": "Tokyo"}
availabilityA schema.org availability string"InStock" on all 60 cards measured

search takes free text combining brand, model and reference ("omega 3861") while browse takes a brand slug plus an optional model slug (brand=omega, model=speedmaster). Both return roughly 60 cards per page across pages 1 to 100, and meta.pagination.has_more is the only paging signal worth trusting.

What the count above the grid counts, and what a spec sheet is worth

Measured by running the same query with and without each filter and comparing the totals, by browsing one brand under two spellings, and by opening listings that were live, sold, and never real.

The result total follows some of your filters and not others

Adding a price ceiling moved the reported total sharply. Adding a movement filter to the same query did not move it at all — the count stayed identical with and without. So the number above the grid is a dependable ceiling and an undependable count: use it to know roughly how deep the market is, and count the rows themselves when the answer has to be right.

The price band in the summary can sit outside the price band you asked for

Search under a ceiling and the summary's high price came back above that ceiling, on two different queries. It is the range the model's page advertises, not a summary of the rows your filters actually selected. The rows respect the filter; the summary beside them describes the wider market.

Brand spelling is normalised for you

Chrono24's own brand paths run the words together, but you do not have to know that. The hyphenated and the run-together spelling of a two-word maker both resolve to the same brand and return the same catalogue, so you can pass a brand the way a human writes it.

A specification is only as complete as the seller made it

The detail record has a slot for movement, case material, crystal, strap and dial — and on a live listing we opened, every one of those was empty while the reference number, year, condition, jewels and the box-and-papers note were all filled. Two listings for the same watch will disagree about how much they tell you, because the spec table is the seller's data entry. The reference number is the field that is reliably there, which is exactly why it is the one to join on. You also get the country and town a watch ships from rather than the seller's name.

A sold listing is a clean not-found, and it says which kind of nothing it is

A watch that has been bought and a listing id that never existed are different answers and are reported as such: one says the listing is no longer on sale, the other says no listing was ever there. That matters when you are re-checking a saved shortlist, because the first means the price you recorded was real and the market moved, and the second means your id is wrong.

What people build with Chrono24

The jobs this data is most often used for.

3

endpoints

1

credit per call

01

Watch-price tools call search to track the market range for a reference across dealers.

02

Resale and investment products use the aggregate low/high to value a specific model.

03

Luxury-market analysts use browse to monitor supply and pricing for a brand or collection.

What Chrono24 data costs

The cheapest call here is 1 credit, so $15/mo (Pro) buys 10,000 of them — $1.50 per 1,000 credits. Credits roll over and never expire, and failed or blocked calls are not charged.

Full pricing →
$0.67–$1.50 / 1,000 credits
  • 1,000 free credits on signup, no card
  • One key, all 184 APIs, one credit pool
  • Failed and blocked calls are never charged
  • Credits roll over and never expire

Call it in two lines

Sign up, get 1,000 credits and one key that works on every engine. Then this is the whole protocol.

curl
curl -X POST https://api.reefapi.com/chrono24/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"rolex submariner"}'
python
import requests

r = requests.post(
    "https://api.reefapi.com/chrono24/v1/search",
    headers={"x-api-key": REEF_KEY},
    json={
  "query": "rolex submariner"
},
)
print(r.json()["data"])
FAQ

Have a question? We got answers.

The questions people actually ask before wiring up Chrono24.

Get a free key →
What is the difference between a Chrono24 listing_id and a reference number?

listing_id identifies one seller's offer; reference_number identifies the watch model itself. The measured record shows it clearly: listing_id 46892665 sits at chrono24.com/rolex/rolex-submariner-date--id46892665.htm and carries reference_number "16800", the Rolex reference that hundreds of other listings also carry. Track a specific watch for sale by listing_id, and build a price history for a model by grouping on reference_number. Note that only detail returns reference_number: search and browse cards do not include it, so grouping costs one detail call per listing.

Why is currency null on Chrono24 search and browse results?

It sits at the aggregate level rather than on each card. Measured across 60 cards on 2026-08-27, listings[].currency was null on all 60 while data.aggregate.currency was "USD" for the whole page. The prices themselves are real numbers in that currency. detail does return currency per watch, "USD" alongside price 7732.0, so if you cache cards, take the currency from aggregate and stamp it onto the rows yourself.

Is aggregate.total_results the number of matches for my query?

No, and this is worth knowing before you build a market-size report on it. Three unrelated queries measured on 2026-08-27 returned total_results 671,486 ("rolex submariner" filtered to used at 2,000 to 15,000), 671,485 (browse omega speedmaster) and 671,497 (browse rolex, condition new). That is the site-wide listing count drifting with the marketplace, not your result set. Page with page and meta.pagination.has_more instead, and if you need a count for a query, count what you actually fetch.

Can I trust aggregate.low_price and high_price as the price range of my results?

Not as the range of the cards you received. They are strings lifted from the page's aggregate offer block, and on a price_asc-sorted Submariner search whose cheapest card was 5,500 the aggregate reported low_price "8283" and high_price "15700". A browse of Omega Speedmaster reported "1674" to "587700". They are directionally useful for the model family but they do not describe your page. Compute min and max from listings[].price when the number matters.

How do I tell whether a watch comes with its box and papers?

Through scope_of_delivery on the detail action, and you have to trim it. The measured record began "No original box, no original papers" and then trailed off into escaped page markup from the tooltip that explains the field. Take the text up to the first "Original box" or the first escaped angle bracket and treat the remainder as noise. There is no separate boolean for box or papers, so a text match on "no original box" and "no original papers" is the honest way to derive one.

Does the API say whether the seller is a dealer or a private seller?

Not on the record measured on 2026-08-27. detail returned seller_location as a postal-address object with addressCountry "JP" and addressLocality "Tokyo" and nothing else about the seller: no name, no dealer or private flag, no trust badge, no years-active figure. The closest shipping signal is delivery_estimate, which came back as free text ("Anticipated delivery: 8/28 - 9/8"). If seller type matters to your product, treat it as data this API does not currently expose rather than assuming a field you have not seen in a response.

Why did detail return TARGET_BLOCKED when search worked fine?

Individual listing pages are guarded more tightly than result pages. Measured on 2026-08-27, four detail attempts on listing 46892665 produced two full records in 1.7 and 2.1 seconds and two TARGET_BLOCKED errors with error.retryable: true, while search and browse against the same engine succeeded every time. Treat a retryable TARGET_BLOCKED as an instruction to repeat the call, not as evidence that the listing is gone. Returning a clean error beats returning a half-parsed record that looks real.

What format is the year field, and how does it relate to the year filter?

They are not the same type. The year parameter on search and browse is an integer between 1900 and 2030. The year field returned by detail is free text and can carry a qualifier: the measured record returned "1986 (Approximation)", not 1986. Parse it as a string, pull the leading four digits, and keep the qualifier if you display the value, since "Approximation" is a meaningful caveat on a vintage watch.

What is the Chrono24 API?

Chrono24 API is a ReefAPI endpoint group for chrono24 It returns live JSON through POST requests under /chrono24/v1.

Is the Chrono24 API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. Chrono24 calls use the same shared credit balance as every other ReefAPI engine.

Do I need a Chrono24 login or account?

No login to Chrono24 is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the Chrono24 data?

The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the Chrono24 API use?

Chrono24 actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.

Can I call Chrono24 from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call chrono24 actions with the same key, credit pool and JSON envelope used by normal REST requests.

35 E-commerce & Marketplaces APIs on the same key

One key, one credit pool, one response envelope. If you are pulling Chrono24, you are one call away from the rest of the category — no second contract, no second integration.

Need something this API does not do?

Name the endpoint, the field, or a source we do not carry yet. We ship new APIs every week and you would be first to get the key. Real people read every message and reply the same day.

0/4000

No account needed · we reply from [email protected]

Try it on your own data before you pay anything

The call above is the real endpoint, not a recording. A free key gives you 1,000 credits, the other 183 APIs, and the same envelope everywhere.

Endpoints, parameters and credit costs on this page are read from the live catalog and cannot drift from what the API accepts. Field notes were captured on 2026-08-30.