30-Second Quickstart
1. Create a free account and copy your API key from the email we send you.
2. Make your first request — replace YOUR_API_KEY with your key:
curl "https://pricethat.watch/api/v1/valuation?brand=Rolex&reference=116500LN" \ -H "X-Api-Key: YOUR_API_KEY"
3. You'll get a JSON response with market_summary.average_price, floor_price, ceiling_price, and sample_size. See the full response format →
Authentication
Most endpoints require an API key passed in the X-Api-Key header. Anonymous requests are allowed on /api/v1/valuation and /api/v1/brands but are subject to the Free rate limit (10 requests/week).
Paste your key below and it will be injected into all code examples and the live playground.
curl https://pricethat.watch/api/v1/valuation \ -H "X-Api-Key: YOUR_API_KEY" \ -G -d "brand=Rolex" -d "reference=116500LN"
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
params = {"brand": "Rolex", "reference": "116500LN"}
r = requests.get(
"https://pricethat.watch/api/v1/valuation",
headers=headers, params=params
)
data = r.json()
print(data["market_summary"]["average_price"])const res = await fetch(
"https://pricethat.watch/api/v1/valuation?brand=Rolex&reference=116500LN",
{ headers: { "X-Api-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.market_summary.average_price);$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://pricethat.watch/api/v1/valuation?brand=Rolex&reference=116500LN",
CURLOPT_HTTPHEADER => ["X-Api-Key: YOUR_API_KEY"],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
echo $data["market_summary"]["average_price"];Rate Limits
| Plan | Limit | Window |
|---|---|---|
| Free / Anonymous | 10 requests | per week |
| Basic ($30/mo) | 500 requests | per week |
| Pro ($99/mo) | Unlimited | — |
Rate limit headers are returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp).
Check your current usage anytime with GET /api/v1/usage.
GET /valuation
Returns a market valuation for a specific watch reference number, aggregating live data from up to 8 marketplaces including Chrono24, eBay, WatchBox, Jomashop, and more. Results are cached for 24 hours.
Parameters
| Parameter | Description |
|---|---|
| brandrequired | Watch brand, e.g. Rolex, Patek Philippe |
| referencerequired | Reference number, e.g. 116500LN, 5711/1A |
| stateoptional | US state code for tax calculation, e.g. CA, NY |
| materialoptional | Case material filter: steel, gold, platinum, titanium, ceramic, two-tone |
| movementoptional | Movement type, e.g. automatic, manual, quartz |
| dial_coloroptional | Dial color filter, e.g. black, blue |
| detailedoptional | Set to true to include full listing data in the response |
Try It
Code Examples
curl "https://pricethat.watch/api/v1/valuation?brand=Rolex&reference=116500LN" \ -H "X-Api-Key: YOUR_API_KEY"
import requests
r = requests.get(
"https://pricethat.watch/api/v1/valuation",
headers={"X-Api-Key": "YOUR_API_KEY"},
params={"brand": "Rolex", "reference": "116500LN"},
)
data = r.json()
ms = data["market_summary"]
print(f"Floor: ${ms['floor_price']:,}")
print(f"Average: ${ms['average_price']:,}")
print(f"Ceiling: ${ms['ceiling_price']:,}")const params = new URLSearchParams({ brand: "Rolex", reference: "116500LN" });
const res = await fetch(
`https://pricethat.watch/api/v1/valuation?${params}`,
{ headers: { "X-Api-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
const { floor_price, average_price, ceiling_price } = data.market_summary;
console.log({ floor_price, average_price, ceiling_price });$url = "https://pricethat.watch/api/v1/valuation?" . http_build_query([
"brand" => "Rolex",
"reference" => "116500LN",
]);
$opts = ["http" => ["header" => "X-Api-Key: YOUR_API_KEY"]];
$data = json_decode(file_get_contents($url, false, stream_context_create($opts)), true);
echo "Average: $" . number_format($data["market_summary"]["average_price"]);GET /valuation-from-url
Accepts a listing URL from eBay, Facebook Marketplace, or Craigslist and returns a valuation for the watch in that listing. The watch brand and reference are extracted automatically from the listing.
Parameters
| Parameter | Description |
|---|---|
| urlrequired | Listing URL, e.g. an eBay item URL or Facebook Marketplace listing |
curl "https://pricethat.watch/api/v1/valuation-from-url?url=https%3A%2F%2Fwww.ebay.com%2Fitm%2F123456789" \ -H "X-Api-Key: YOUR_API_KEY"
const listingUrl = "https://www.ebay.com/itm/123456789";
const res = await fetch(
`/api/v1/valuation-from-url?url=${encodeURIComponent(listingUrl)}`,
{ headers: { "X-Api-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.product, data.market_summary);POST /valuation/batch
Submit multiple watch valuations in a single request. Requires Basic or Pro tier. Returns an array of valuation results in the same order as the request.
Request Body
| Field | Description |
|---|---|
| watchesrequired | Array of objects, each with brand and reference fields |
curl -X POST "https://pricethat.watch/api/v1/valuation/batch" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"watches": [
{ "brand": "Rolex", "reference": "116500LN" },
{ "brand": "Patek Philippe", "reference": "5711/1A-010" }
]
}'const res = await fetch("/api/v1/valuation/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY",
},
body: JSON.stringify({
watches: [
{ brand: "Rolex", reference: "116500LN" },
{ brand: "Patek Philippe", reference: "5711/1A-010" },
],
}),
});
const { results } = await res.json();
results.forEach(r => console.log(r.product, r.market_summary));GET /brands
Returns the list of all supported watch brands. No authentication required.
curl "https://pricethat.watch/api/v1/brands"
import requests
r = requests.get("https://pricethat.watch/api/v1/brands")
print(r.json()["brands"])const res = await fetch("/api/v1/brands");
const { brands } = await res.json();
console.log(brands);GET /usage
Returns your current week's usage statistics for the authenticated API key.
curl "https://pricethat.watch/api/v1/usage" \ -H "X-Api-Key: YOUR_API_KEY"
const res = await fetch("/api/v1/usage", {
headers: { "X-Api-Key": "YOUR_API_KEY" },
});
const { used, limit, tier, reset_at } = await res.json();
console.log(`${used} / ${limit ?? "Unlimited"} used — resets ${reset_at}`);Response
{
"used": 12,
"limit": 500,
"tier": "basic",
"reset_at": "2026-03-27T00:00:00.000Z"
}For Pro accounts, limit is null (unlimited).
POST /keys/rotate
Invalidates your current API key and returns a new one. The old key stops working immediately.
curl -X POST "https://pricethat.watch/api/v1/keys/rotate" \ -H "X-Api-Key: YOUR_API_KEY"
const res = await fetch("/api/v1/keys/rotate", {
method: "POST",
headers: { "X-Api-Key": "YOUR_API_KEY" },
});
const { key } = await res.json();
console.log("New key:", key);POST /keys/resend
Sends your API key to your registered email address. Useful if you have lost your key. No authentication header required.
Request Body
| Field | Description |
|---|---|
| emailrequired | The email address associated with your account |
curl -X POST "https://pricethat.watch/api/v1/keys/resend" \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com" }'const res = await fetch("/api/v1/keys/resend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "you@example.com" }),
});
const { message } = await res.json();
console.log(message);POST /billing/portal
Returns a Stripe billing portal URL where you can manage your payment method, view invoices, or cancel your subscription.
curl -X POST "https://pricethat.watch/api/v1/billing/portal" \ -H "X-Api-Key: YOUR_API_KEY"
const res = await fetch("/api/v1/billing/portal", {
method: "POST",
headers: { "X-Api-Key": "YOUR_API_KEY" },
});
const { url } = await res.json();
window.location.href = url; // redirect to Stripe portalPOST /billing/change-plan
Upgrades a free account by returning a Stripe Checkout URL, or changes an existing paid subscription immediately. Proration is handled by Stripe.
Request Body
| Field | Description |
|---|---|
| tierrequired | "basic" or "pro" — the target subscription tier |
curl -X POST "https://pricethat.watch/api/v1/billing/change-plan" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tier": "pro" }'const res = await fetch("/api/v1/billing/change-plan", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY",
},
body: JSON.stringify({ tier: "pro" }),
credentials: "include",
});
const data = await res.json();
if (res.ok) console.log(data.message);
else console.error(data.error?.message);DELETE /account
Permanently deletes your account and cancels any active Stripe subscription. This action cannot be undone.
curl -X DELETE "https://pricethat.watch/api/v1/account" \ -H "X-Api-Key: YOUR_API_KEY"
const res = await fetch("/api/v1/account", {
method: "DELETE",
headers: { "X-Api-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.message); // "Account deleted."Response Format
All responses are JSON. A successful GET /valuation response looks like:
{
"product": {
"brand": "Rolex",
"reference": "116500LN",
"model": "Cosmograph Daytona"
},
"market_summary": {
"average_price": 28500,
"floor_price": 24000,
"ceiling_price": 35000,
"sample_size": 42,
"volatility": "medium"
},
"authenticity_check": {
"verdict": "likely_authentic",
"confidence": "high",
"flags": ["Dial text quality appears consistent with genuine examples"],
"reasoning": "External features are consistent with an authentic Rolex Daytona."
},
"tax_considerations": {
"state": "CA",
"rate": 0.0725,
"estimated_tax": 2066,
"total_with_tax": 30566
},
"meta": {
"cached": false,
"sources_successful": ["Chrono24", "eBay"],
"data_quality": "high",
"updated_at": "2026-03-20T03:00:00.000Z"
}
}Volatility Values
| Value | Meaning |
|---|---|
low | Prices are tightly clustered; stable market |
medium | Moderate price spread (13–30% above floor) |
high | Wide price range; market is volatile (>30% above floor) |
Errors
All errors follow a consistent envelope:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Weekly limit of 10 requests reached. Resets 2026-03-27.",
"upgrade_url": "/pricing.html"
}
}| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_PARAMS | Missing or invalid query parameters |
| 401 | UNAUTHORIZED | Invalid or missing API key |
| 403 | FORBIDDEN | Tier does not allow this endpoint (e.g. batch requires Basic+) |
| 404 | NOT_FOUND | No listings found for this reference |
| 429 | RATE_LIMIT_EXCEEDED | Weekly request limit reached |
| 500 | INTERNAL_ERROR | Server error — try again later |