API Reference
Complete reference for the Gemmai Valuation API. Base URL: https://api.gemmai.io
Authentication: All endpoints except GET /v1 require an X-API-Key header. See Getting Started for details.
| Section | Description |
|---|---|
| Valuation | Create and retrieve price estimates. |
| Enrichment | Derive item attributes from uploaded media. |
| Categories | List categories and get validation schemas. |
| Validate | Validate attributes against a category schema. |
| Vision | Upload images and get a fileId for enrich/valuations. |
| Search | Search candidates and filter options. |
| Reference | Reference data and enum search (brands, models). |
Valuation
GET /v1
Returns API info. No authentication required.
Response (200):
{
"name": "Gemmai Valuation API",
"version": "1.0.0",
"description": "API for valuing cars, watches, and rings"
}
POST /v1/valuations
Create a new valuation. Send category and attributes; optionally add fileId to trigger enrichment first (unless options.skipEnrich is true).
Request body:
- Name
category- Type
- string
- Description
"car"|"wristwatch"|"ring"(seeGET /v1/categories).
- Name
attributes- Type
- object
- Description
Category-specific attributes. See Attributes.
- Name
fileId- Type
- string
- Description
ID from
POST /v1/vision. Triggers enrichment unlessskipEnrichistrue.
- Name
options- Type
- object
- Description
Valuation options (see below).
Valuation options:
- Name
skipEnrich- Type
- boolean
- Description
Skip enrichment even when
fileIdis provided. Default:false.
- Name
asOf- Type
- string | number
- Description
Point-in-time valuation. ISO 8601, date-only (
"2025-01-15"), or epoch seconds.
- Name
model- Type
- string
- Description
Pin a specific internal model version (e.g.
"watch-v3.2.1").
- Name
market- Type
- string
- Description
Market region, e.g.
"SE","US","EU".
- Name
currency- Type
- string
- Description
ISO 4217 code, e.g.
"SEK","USD".
- Name
confidenceFloor- Type
- number
- Description
Suppress results below this confidence (0–1).
Request example
{
"category": "wristwatch",
"attributes": {
"brand": "Rolex",
"model": "Submariner",
"referenceNumber": "116610LN"
},
"options": {
"currency": "SEK",
"market": "SE"
}
}
Response (200)
{
"success": true,
"predictionId": "uuid",
"valuation": {
"currency": "SEK",
"priceEstimate": 125000,
"confidence": 0.85,
"range": { "low": 110000, "high": 140000 }
},
"category": "wristwatch",
"attributes": { "..." : "..." },
"metadata": {
"timestamp": "2025-03-06T12:00:00.000Z",
"asOf": "2025-03-06",
"creditUsed": 1
}
}
Response (400)
{
"success": false,
"details": { "<field>": ["error message"] },
"metadata": { "timestamp": "...", "creditUsed": 0 }
}
GET /v1/valuations/:id
Get a valuation by ID.
Path parameters:
- Name
id- Type
- string
- Description
Valuation UUID (
predictionIdfrom a create response).
Response (200)
{
"success": true,
"predictionId": "uuid",
"category": "wristwatch",
"valuation": {
"currency": "SEK",
"priceEstimate": 125000,
"confidence": 0.85,
"range": { "low": 110000, "high": 140000 }
},
"attributes": { "..." : "..." },
"metadata": { "timestamp": "...", "creditUsed": 1, "asOf": "..." }
}
Response (404)
{ "error": "Valuation not found" }
GET /v1/valuations/:id/timeline
Get valuation timeline.
Response (200)
{
"period": { "from": "...", "to": "..." },
"difference": { "percentageChange": 0.02, "absoluteChange": 2500 },
"timeline": [
{
"date": "...",
"priceEstimate": 125000,
"confidence": 0.85,
"range": { "low": 110000, "high": 140000 }
}
]
}
GET /v1/valuations/:id/similar-sales
Get similar sales for a valuation.
- Name
limit- Type
- number
- Description
Max results (default: 40).
- Name
offset- Type
- number
- Description
Pagination offset (default: 0).
Response (200)
{
"results": [
{
"title": "...",
"price": { "currency": "SEK", "amount": 118000 },
"attributes": { "..." : "..." }
}
],
"total": 100
}
Enrichment
POST /v1/enrich
Derive item attributes from a previously uploaded image (by fileId).
Request body:
- Name
category- Type
- string
- Description
"car"|"wristwatch"|"ring"(seeGET /v1/categories).
- Name
fileId- Type
- string
- Description
File ID from
POST /v1/vision.
- Name
attributes- Type
- object
- Description
Optional initial attributes. Enrichment merges them with derived values.
Request example
{
"category": "wristwatch",
"fileId": "file-id-123",
"attributes": { "brand": "Rolex" }
}
Response (200)
{
"normalizedAttributes": {
"brand": "Rolex",
"model": "Submariner",
"strap": "steel"
},
"candidates": [
{ "attribute": "strap", "value": "steel", "confidence": 0.9, "source": "VISUAL_MATCH" }
],
"enriched": [
{ "attribute": "brand", "value": "Rolex", "confidence": 1, "source": "car-registry" }
],
"confidence": 0.92,
"warnings": [
{ "code": "AMBIGUOUS_OPTION", "message": "..." }
]
}
Response (400)
{ "error": "category query param is required" }
Response (500)
{ "error": "Enrichment failed" }
Categories
GET /v1/categories
List all supported categories.
Response (200)
{
"categories": ["car", "wristwatch", "ring", "necklace"]
}
GET /v1/categories/:category/schema
Get the validation schema for a category. Use this to build forms, generate dropdowns, or validate attributes before calling the API.
Response (200)
{
"category": "wristwatch",
"schema": {
"type": "object",
"properties": {
"brand": { "type": "string" },
"strap": { "type": "string", "enum": ["steel", "leather", "..."] }
},
"required": ["brand"],
"additionalProperties": false
}
}
Response (404)
{ "error": "Category not found" }
Validate
POST /v1/validate
Validate attributes for a given category without running valuation or enrichment.
- Name
category- Type
- string
- Description
"car"|"wristwatch"|"ring".
- Name
attributes- Type
- object
- Description
Attributes to validate.
Response — valid
{ "valid": true, "error": null }
Response — invalid
{
"valid": false,
"error": { "<field>": ["validation message"] }
}
Vision
POST /v1/vision
Submit an image by URL. Returns a fileId for use in /v1/enrich and POST /v1/valuations.
- Name
imageUrl- Type
- string
- Description
Public URL of the image to analyze.
Response (200)
{
"fileId": "uuid",
"status": "completed",
"objects": [{ "type": "wristwatch" }],
"usage": { "creditUsed": 1 }
}
If the same image (by hash) was already processed, a cached result is returned with creditUsed: 0.
GET /v1/vision/:id
Get status for a previously submitted media item.
Response (200)
{ "id": "uuid", "status": "pending" }
Search
GET /v1/search
Search candidates with optional filters and pagination.
- Name
category- Type
- string
- Description
Filter by category.
- Name
attributes.{key}- Type
- string
- Description
Filter by attribute (repeat for multiple).
- Name
limit- Type
- number
- Description
Max results (default 10).
- Name
offset- Type
- number
- Description
Pagination offset (default 0).
GET /v1/search/filters
Get available filter keys/aggregations for the search index.
Response (200)
[
{ "key": "brand", "doc_count": 150 }
]
Reference
GET /v1/reference/:ref
Reference/enum search. :ref is the reference type (e.g. brand, model). Only attributes that have an enumRef in the schema support this endpoint.
- Name
q- Type
- string
- Description
Search term (e.g. partial brand or model name).
- Name
category- Type
- string
- Description
Limit results to a category.
- Name
(other)- Type
- string
- Description
Pass already-selected attributes to narrow scope (e.g.
brand=Rolexwhen looking upmodel).
Returns a list of { value, probability } items. Use the exact value string when sending attributes.
Errors
| Status | Meaning | Response shape |
|---|---|---|
| 400 | Invalid input or validation error | { "error": "..." } or { "success": false, "details": { "<field>": ["..."] }, "metadata": {...} } |
| 404 | Resource not found | { "error": "Not Found" } or { "error": "Valuation not found" } |
| 500 | Server error | { "error": "message" } |
Validation errors from Valuations return 400 with a details object keyed by field name:
{
"success": false,
"details": { "brand": ["Required"], "referenceNumber": ["Invalid value"] },
"metadata": { "timestamp": "...", "creditUsed": 0 }
}