Developers

The Dials API, the machine-readable files that describe it, and how to drive this site from an agent.

Last updated

Everything the catalogue knows is available as JSON. The public endpoints need no key, no account and no Authorization header; the catalogue is meant to be read.

Resource Where
Base URL https://api.dials.co/v1
OpenAPI 3.0 document api.dials.co/openapi.json
Browsable reference api.dials.co/docs
Agent index dials.co/llms.txt
Sitemap dials.co/sitemap.xml

Quickstart

Resolve a reference number to a model:

curl -s 'https://api.dials.co/v1/models?q=116610LN&pageSize=1'

Read that model's full specification:

curl -s 'https://api.dials.co/v1/models/rolex/submariner-date-116610ln'

See what it is currently selling for, converted to euros:

curl -s 'https://api.dials.co/v1/listings?modelId=<id>&currency=EUR'

Walk a house's catalogue:

curl -s 'https://api.dials.co/v1/brands/rolex'
curl -s 'https://api.dials.co/v1/brands/rolex/series'
curl -s 'https://api.dials.co/v1/models?brandSlug=rolex&page=1&pageSize=50'

When to reach for this API

It answers structured questions about a specific watch, which is a narrower job than it sounds and the one thing it does better than a search engine:

  • Resolve a reference to a model. Reference numbers are a compressed parts list, not a name, and every house has its own grammar. GET /models?q=<reference> does the lookup and gives you back fields, not prose.
  • Compare two watches on the same axes. Every model here is described to one specification, which is the entire premise of the catalogue. Two GET /models/{brandSlug}/{slug} responses are directly comparable field by field (case dimensions, calibre, power reserve, water resistance) with no translation step.
  • Price a watch against the live market. GET /listings?modelId=… returns current offers from the tracked marketplaces. ?currency= converts, using the same ECB table GET /rates publishes, so your numbers and ours agree.
  • Identify a movement. GET /calibers/{brandSlug}/{ref} gives the calibre's properties and every model across the catalogue that runs it, which is how you find out that two watches from different houses share an ébauche.
  • Enumerate a catalogue. Brand → series → models, paginated, for building a picker or filling a table.

Do not reach for it to value a watch, to authenticate one, to look up anything about a specific physical watch (serial numbers, service history, provenance), or to transact: nothing here buys or sells.

Endpoints

Method Path What it returns
GET /brands A page of watch houses, with counts and a lead picture
GET /brands/highlighted The hand-picked row the home page leads with
GET /brands/{slug} One brand
GET /brands/{brandSlug}/series The series inside a brand
GET /brands/{brandSlug}/series/{seriesSlug} One series
GET /models A page of models; filter by brand, series, caliber or free text
GET /models/{brandSlug}/{slug} One model, full specification and pictures
GET /calibers/{brandSlug}/{ref} One movement, and the models that run it
GET /listings Live marketplace listings
GET /listings/{id} One listing
GET /rates USD → display-currency conversion table

The /admin, /picture-review, /reconciliation and /duplicate-models groups are the back office. They appear in the OpenAPI document for completeness and answer 401 to anyone without an admin session.

Versioning and deprecation

The current version is v1, at https://api.dials.co/v1. Build against the prefixed URL.

The same paths also answer without the prefix (https://api.dials.co/models) as a permanent alias for whatever the current version happens to be. That is convenient for a quick curl and wrong to pin an integration to, because it moves when the current version moves.

Inside a version the rules are:

  • Changes are additive only: new endpoints, new optional parameters, new fields on a response.
  • A field is never removed, renamed or retyped, and a code in error is never repurposed.
  • A breaking change ships as a new prefix (/v2), served alongside the old one.

When a version starts winding down, every response it serves carries Deprecation: true (RFC 9745) and Sunset: <HTTP-date> (RFC 8594) (the date after which it stops answering, never less than six months out), plus a Link header with rel="successor-version" pointing at the replacement. X-API-Version names the serving version on every response, deprecated or not, so a client can assert it is talking to what it thinks it is.

Rate limits

Public callers get 600 requests per 60 seconds. Every response carries the budget so you can pace yourself rather than discover the ceiling:

RateLimit-Limit: 600
RateLimit-Remaining: 573
RateLimit-Reset: 41
RateLimit-Policy: 600;w=60;policy="public"

RateLimit-Reset is seconds until the window rolls. A refused request answers 429 with Retry-After set to the same number of seconds. The limit is enforced per IP address and is deliberately generous; if you need more for something interesting, write to hello@dials.co rather than sharding across addresses.

Errors

Every failure returns JSON in one shape: a rejected parameter, an unknown path, a fault on our side alike. There are no HTML error pages anywhere on the API.

{
  "error": "not_found",
  "message": "Brand not found.",
  "hint": "Check the slug against GET /brands.",
  "docs": "https://api.dials.co/docs"
}

error is the stable, machine-readable part and is safe to branch on: invalid_request, unauthorized, forbidden, not_found, rate_limited, internal_error. message is prose and may be reworded. hint says what to change about the request.

Using this API from an agent

The OpenAPI document is written to be turned into tools without a human naming each one:

  • Every operation has a unique operationId (getModel, listListings, getCaliber) usable directly as a function name.
  • Every operation has a description saying what it returns and when to reach for it.
  • Every parameter and every response is typed, including the error bodies.
  • servers, contact, license and externalDocs are all populated, so a generator has somewhere to point.

Point your tool generator at https://api.dials.co/openapi.json and it should need no hand editing.

Markdown instead of HTML

Every public page on dials.co is also served as markdown, following the acceptmarkdown.com convention. Ask for it:

curl -s -H 'Accept: text/markdown' https://dials.co/about

You get Content-Type: text/markdown; charset=utf-8 and Vary: Accept. A client that asks for a media type we cannot produce gets a 406 naming what is available, rather than an HTML page it then has to strip. This is far cheaper than parsing the rendered site, and it is the intended route for anything reading the journal or the reference pages.

Terms

Free to read and free to build on, within the rate limits above. Two conditions: do not present the catalogue as your own (attribute it, with a link to the model page you took the data from) and do not hotlink the images; they are served from our CDN and paid for by us.

The listings are not ours. Each one belongs to the dealer or platform that published it and links back to them; anything about price, condition, availability, shipping or returns is theirs to answer.

Questions: hello@dials.co.