AltFinance Data API
Access 990,000+ luxury auction records across watches, handbags, and jewelry from 100+ auction houses worldwide — including Christie's, Sotheby's, Phillips, Bonhams, and more.
Base URL
Recommended path for new developers
Follow these steps to go from zero to a working integration.
Get your API key
Your key is provided in your welcome email. Set it in the X-API-Key header for all requests.
Make your first API call
Use the POST query endpoint to search across watches, handbags, or jewelry with filters and sorting.
Go to Querying Data →Explore the datasets
Browse available tables, understand the columns, and discover what data you can query.
See Available Tables →Build with the API
Endpoints
Health checks, table listing, schema inspection, and querying.
Filter Operators
11 operators: eq, like, in, gt, is_null, and more.
Code Examples
Ready-to-use examples in curl, Python, and JavaScript.
Available datasets
Watches
Luxury watch auction results from major houses worldwide.
Handbags
Designer handbag auction results.
Jewelry
Jewelry and gemstone auction results.
Tools & Integrations
Claude MCP Server
Query auction data in natural language directly from Claude Desktop or Claude Code.
Swagger UI
Interactive API explorer with live request testing.
Authentication #
All endpoints except /v1/health require an API key. Pass your key in the X-API-Key header:
X-API-Key: afi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Your API key is provided in your welcome email. Keep it secret — do not commit it to source control or share it publicly.
| Scenario | HTTP Status | Response |
|---|---|---|
| Key missing | 401 | {"detail": "Missing API key"} |
| Key invalid or inactive | 403 | {"detail": "Invalid API key"} |
| Key lacks table access | 403 | {"detail": "No access to table: watches"} |
Endpoints #
The API exposes five endpoints. Replace {name} with watches, handbags, or jewelry.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/health | No | Service health check |
| GET | /v1/tables | Yes | List available tables |
| GET | /v1/tables/{name}/schema | Yes | Get column names & types |
| GET | /v1/tables/{name}/query | Yes | Simple query via URL params |
| POST | /v1/tables/{name}/query | Yes | Advanced query with filters, sorting, pagination |
GET /v1/health
curl https://api.altfndata.com/v1/health{
"status": "healthy",
"version": "1.0.0",
"timestamp": "2026-03-04T13:00:00.000000+00:00",
"athena_database": "altfinancedb"
}GET /v1/tables
curl -H "X-API-Key: YOUR_KEY" https://api.altfndata.com/v1/tables{
"tables": [
{"name": "watches", "description": "Luxury watch auction results", "column_count": 122},
{"name": "handbags", "description": "Luxury handbag auction results", "column_count": 67},
{"name": "jewelry", "description": "Jewelry and gemstone auction results", "column_count": 99}
]
}GET /v1/tables/{name}/schema
curl -H "X-API-Key: YOUR_KEY" https://api.altfndata.com/v1/tables/watches/schema{
"table": "watches",
"columns": [
{"name": "item_title", "type": "string"},
{"name": "designer", "type": "string"},
{"name": "usd_price_decimal", "type": "double"},
{"name": "sale_date", "type": "string"}
]
}Querying Data #
POST /v1/tables/{name}/query (recommended)
Full control over field selection, filters, sorting, and pagination.
Request body
{
"fields": ["item_title", "designer", "usd_price_decimal", "sale_date", "vendor"],
"filters": [
{"field": "designer", "op": "eq", "value": "Rolex"},
{"field": "usd_price_decimal", "op": "gte", "value": 50000}
],
"sort": [
{"field": "usd_price_decimal", "direction": "desc"}
],
"limit": 20,
"offset": 0
}| Field | Type | Default | Description |
|---|---|---|---|
fields | string[] | null | null (all columns) | Columns to return |
filters | FilterItem[] | null | null (no filter) | WHERE conditions (AND-ed together) |
sort | SortItem[] | null | null (no ordering) | ORDER BY clauses |
limit | int (1–1000) | 100 | Max rows to return |
offset | int (≥ 0) | 0 | Rows to skip (pagination) |
FilterItem
{"field": "column_name", "op": "eq", "value": "some_value"}SortItem
{"field": "column_name", "direction": "desc"}Response
{
"table": "watches",
"query_execution_id": "ae3d3e58-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"execution_time_ms": 1505,
"result_count": 5,
"limit": 20,
"offset": 0,
"fields": ["item_title", "designer", "usd_price_decimal", "sale_date", "vendor"],
"data": [
{
"item_title": "Ref.6239, Cosmograph Daytona \"Paul Newman\"",
"designer": "Rolex",
"usd_price_decimal": 17752500.0,
"sale_date": "-",
"vendor": "Phillips"
}
]
}GET /v1/tables/{name}/query (simplified)
For simple queries, use URL parameters:
curl -H "X-API-Key: YOUR_KEY" \
"https://api.altfndata.com/v1/tables/watches/query?designer=Rolex&limit=5&sort=usd_price_decimal:desc"| Parameter | Description |
|---|---|
fields | Comma-separated column names: fields=item_title,vendor,usd_price_decimal |
sort | Column and direction: sort=usd_price_decimal:desc |
limit | Max rows (default 100, max 1000) |
offset | Skip N rows |
| Any column name | Equality filter: designer=Rolex becomes WHERE designer = 'Rolex' |
Pagination
Use limit and offset to page through results:
Page 1: limit=100&offset=0
Page 2: limit=100&offset=100
Page 3: limit=100&offset=200When result_count < limit, you've reached the last page.
Filter Operators #
All filters use the "op" key. Multiple filters are AND-ed together.
| Operator | Description | Value Type | Example |
|---|---|---|---|
eq | Equals | scalar | {"field": "designer", "op": "eq", "value": "Rolex"} |
neq | Not equals | scalar | {"field": "status", "op": "neq", "value": "withdrawn"} |
gt | Greater than | numeric | {"field": "usd_price_decimal", "op": "gt", "value": 100000} |
gte | Greater or equal | numeric | {"field": "carats", "op": "gte", "value": 5} |
lt | Less than | numeric | {"field": "usd_price_decimal", "op": "lt", "value": 1000} |
lte | Less or equal | numeric | {"field": "usd_price_decimal", "op": "lte", "value": 1000} |
like | Contains (case-insensitive) | string | {"field": "item_title", "op": "like", "value": "Daytona"} |
in | In list | array | {"field": "vendor", "op": "in", "value": ["Christie's", "Sotheby's"]} |
not_in | Not in list | array | {"field": "vendor", "op": "not_in", "value": ["eBay"]} |
is_null | Is NULL | none | {"field": "usd_price_decimal", "op": "is_null"} |
is_not_null | Is not NULL | none | {"field": "usd_price_decimal", "op": "is_not_null"} |
Values for numeric operators (gt, gte, lt, lte) are auto-cast to double.
Available Tables #
Three datasets are available. Use the /v1/tables/{name}/schema endpoint to see all columns for any table.
watches
Luxury watch auction results from major houses worldwide.
handbags
Designer handbag auction results.
jewelry
Jewelry and gemstone auction results.
watches
| Column | Type | Description |
|---|---|---|
item_title | string | Watch description from the auction listing |
designer | string | Brand name (Rolex, Patek Philippe, Omega, etc.) |
manufacturer_name | string | Manufacturer (often same as designer) |
model | string | Model name (Submariner, Nautilus, etc.) |
vendor | string | Auction house (Phillips, Sotheby's, Christie's, etc.) |
sale_price | double | Hammer price in original sale currency |
sale_currency | string | Original currency (USD, GBP, EUR, CHF, etc.) |
usd_price_decimal | double | Price converted to USD |
sale_date | string | Auction date (YYYY-MM-DD) |
sale_location | string | Auction city |
sale_location_country | string | Country |
status | string | Lot status (sold, unsold, withdrawn, etc.) |
sale_estimates_low_usd_price | double | Low estimate in USD |
sale_estimates_high_usd_price | double | High estimate in USD |
case_material | string | Case material (steel, gold, platinum, etc.) |
case_diameter | string | Case diameter |
dial_color | string | Dial color |
movement | string | Movement type |
reference | string | Reference number |
item_image | string | Primary image URL |
lot_url | string | Original auction lot URL |
handbags
| Column | Type | Description |
|---|---|---|
item_title | string | Handbag description from listing |
brand_name_clean | string | Standardized brand name (Hermes, Chanel, Louis Vuitton, etc.) |
designer | string | Designer name |
model | string | Model name (Birkin, Kelly, Classic Flap, etc.) |
vendor | string | Auction house |
sale_price | double | Hammer price in original currency |
sale_currency | string | Original currency |
usd_price_decimal | double | Price converted to USD |
sale_date | string | Auction date |
sale_location | string | Auction city |
sale_location_country | string | Country |
status | string | Lot status |
sale_estimates_low_usd_price | double | Low estimate in USD |
sale_estimates_high_usd_price | double | High estimate in USD |
materials | string | Bag materials |
item_type | string | Bag type (Shoulder Bag, Clutch, Tote, etc.) |
item_image | string | Primary image URL |
lot_url | string | Original lot URL |
jewelry
| Column | Type | Description |
|---|---|---|
item_title | string | Jewelry description from listing |
designer | string | Designer or brand name |
manufacturer_name | string | Manufacturer |
vendor | string | Auction house |
sale_price | double | Hammer price in original currency |
sale_currency | string | Original currency |
usd_price_decimal | double | Price converted to USD |
sale_date | string | Auction date |
sale_location | string | Auction city |
sale_location_country | string | Country |
status | string | Lot status |
sale_estimates_low_usd_price | double | Low estimate in USD |
sale_estimates_high_usd_price | double | High estimate in USD |
gem_type | string | Gemstone type (Diamond, Ruby, Sapphire, Emerald, etc.) |
primary_gem | string | Primary gemstone |
carats | double | Carat weight |
gemstone_cut | string | Cut type |
gemstone_clarity | string | Clarity grade |
color | string | Gemstone color |
item_type | string | Jewelry type (Ring, Necklace, Bracelet, etc.) |
item_image | string | Primary image URL |
lot_url | string | Original lot URL |
Rate Limits #
Each API key has configurable rate limits:
| Limit | Description |
|---|---|
| Requests per minute | Sliding 60-second window; configurable per API key |
| Daily row quota | Total rows returned per calendar day (UTC); resets at midnight |
| Max rows per request | Caps the limit parameter per API key |
When limits are exceeded, you receive HTTP 429:
{"detail": "Rate limit exceeded (30 requests/minute). Try again later."}{"detail": "Daily row quota exceeded (10000 rows/day). Resets at midnight UTC."}Error Handling #
| Status | Cause | Example Response |
|---|---|---|
400 | Invalid column, operator, or value | {"detail": "Unknown column: invalid_col"} |
401 | Missing API key | {"detail": "Missing API key"} |
403 | Invalid key or unauthorized table | {"detail": "Invalid API key"} |
404 | Table not found | {"detail": "Table not found: cars"} |
429 | Rate limit or quota exceeded | {"detail": "Rate limit exceeded..."} |
500 | Athena query failure | {"detail": "Athena query failed: ..."} |
All error responses return JSON with a "detail" key. For 500 errors, a "query_execution_id" is also included for debugging.
Code Examples #
curl
# List tables
curl -H "X-API-Key: YOUR_KEY" https://api.altfndata.com/v1/tables
# Simple GET query
curl -H "X-API-Key: YOUR_KEY" \
"https://api.altfndata.com/v1/tables/watches/query?designer=Rolex&limit=10&sort=usd_price_decimal:desc"
# POST query with filters
curl -X POST https://api.altfndata.com/v1/tables/watches/query \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"fields": ["item_title", "designer", "vendor", "usd_price_decimal", "sale_date"],
"filters": [
{"field": "designer", "op": "eq", "value": "Rolex"},
{"field": "usd_price_decimal", "op": "gte", "value": 50000}
],
"sort": [{"field": "usd_price_decimal", "direction": "desc"}],
"limit": 20
}'Python
import requests
API_KEY = "YOUR_KEY"
BASE = "https://api.altfndata.com"
HEADERS = {"X-API-Key": API_KEY}
# List tables
tables = requests.get(f"{BASE}/v1/tables", headers=HEADERS).json()
for t in tables["tables"]:
print(f"{t['name']}: {t['column_count']} columns")
# Get schema
schema = requests.get(f"{BASE}/v1/tables/watches/schema", headers=HEADERS).json()
for col in schema["columns"][:10]:
print(f" {col['name']}: {col['type']}")
# Query: Top 10 Rolex watches by price
resp = requests.post(
f"{BASE}/v1/tables/watches/query",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"fields": ["item_title", "vendor", "usd_price_decimal", "sale_date"],
"filters": [
{"field": "designer", "op": "eq", "value": "Rolex"},
{"field": "usd_price_decimal", "op": "is_not_null"},
],
"sort": [{"field": "usd_price_decimal", "direction": "desc"}],
"limit": 10,
},
)
data = resp.json()
print(f"\n{data['result_count']} results ({data['execution_time_ms']}ms)\n")
for row in data["data"]:
print(f" ${row['usd_price_decimal']:,.0f} {row['vendor']} {row['item_title'][:60]}")
# Pagination
all_results = []
offset = 0
while True:
resp = requests.post(
f"{BASE}/v1/tables/watches/query",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"fields": ["item_title", "usd_price_decimal"],
"filters": [{"field": "designer", "op": "eq", "value": "Patek Philippe"}],
"sort": [{"field": "usd_price_decimal", "direction": "desc"}],
"limit": 100,
"offset": offset,
},
)
page = resp.json()
all_results.extend(page["data"])
if page["result_count"] < 100:
break
offset += 100
print(f"Total Patek Philippe records: {len(all_results)}")JavaScript / Node.js
const API_KEY = "YOUR_KEY";
const BASE = "https://api.altfndata.com";
// List tables
const tables = await fetch(`${BASE}/v1/tables`, {
headers: { "X-API-Key": API_KEY },
}).then((r) => r.json());
console.log(tables);
// Query watches
const resp = await fetch(`${BASE}/v1/tables/watches/query`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
fields: ["item_title", "designer", "usd_price_decimal", "vendor"],
filters: [
{ field: "designer", op: "eq", value: "Omega" },
{ field: "usd_price_decimal", op: "gte", value: 10000 },
],
sort: [{ field: "usd_price_decimal", direction: "desc" }],
limit: 10,
}),
}).then((r) => r.json());
console.log(`${resp.result_count} results (${resp.execution_time_ms}ms)`);
resp.data.forEach((row) => {
console.log(` $${row.usd_price_decimal} ${row.item_title}`);
});Claude MCP Server #
The AltFinance MCP server lets Claude query the API directly in conversation. Instead of writing code, just ask questions in natural language.
Install
pip install altfinance-mcpRequires Python 3.10+.
Configure for Claude Desktop
Edit your Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"altfinance": {
"command": "altfinance-mcp",
"env": {
"ALTFINANCE_API_KEY": "YOUR_KEY"
}
}
}
}Restart Claude Desktop after saving.
Configure for Claude Code
export ALTFINANCE_API_KEY="YOUR_KEY"
claude mcp add altfinance -- altfinance-mcpMCP Tools
| Tool | Description |
|---|---|
list_tables() | Shows available tables with column counts |
get_table_schema(table) | Lists all columns and their types |
query_table(table, ...) | Queries with full filter support |
Example Prompts
Once configured, ask Claude:
- "What tables are available in the AltFinance database?"
- "Show me the top 10 most expensive Rolex watches sold after 2020"
- "How many Hermes handbags were sold by Christie's?"
- "What diamonds over 5 carats sold for more than $1M?"
- "Compare average Rolex vs Omega prices by year"
Claude will automatically call the right tools, handle schemas, and format results.
Environment Variables
| Variable | Required | Default |
|---|---|---|
ALTFINANCE_API_KEY | Yes | — |
ALTFINANCE_API_URL | No | https://api.altfndata.com |