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.

SectionDescription
ValuationCreate and retrieve price estimates.
EnrichmentDerive item attributes from uploaded media.
CategoriesList categories and get validation schemas.
ValidateValidate attributes against a category schema.
VisionUpload images and get a fileId for enrich/valuations.
SearchSearch candidates and filter options.
ReferenceReference 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" (see GET /v1/categories).

  • Name
    attributes
    Type
    object
    Description

    Category-specific attributes. See Attributes.

  • Name
    fileId
    Type
    string
    Description

    ID from POST /v1/vision. Triggers enrichment unless skipEnrich is true.

  • Name
    options
    Type
    object
    Description

    Valuation options (see below).

Valuation options:

  • Name
    skipEnrich
    Type
    boolean
    Description

    Skip enrichment even when fileId is 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 (predictionId from 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" (see GET /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" }

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=Rolex when looking up model).

Returns a list of { value, probability } items. Use the exact value string when sending attributes.


Errors

StatusMeaningResponse shape
400Invalid input or validation error{ "error": "..." } or { "success": false, "details": { "<field>": ["..."] }, "metadata": {...} }
404Resource not found{ "error": "Not Found" } or { "error": "Valuation not found" }
500Server 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 }
}

Was this page helpful?