Skip to content

    Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.

    Catalog metadata

    View source

    Three read-only endpoints report what a catalog holds: its object types, the attributes each type carries with their value types, and the distinct values of an attribute. Check them before debugging an empty filter.

    All three need a token for the https://api.<region>.luigisbox.ai/catalog audience, and a grant to read the catalog’s metadata.

    Terminal window
    curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/types' \
    -H 'Authorization: Bearer <token>'

    Every object type the catalog has actually received content for:

    {
    "items": [
    { "name": "brand" },
    { "name": "category" },
    { "name": "product" }
    ],
    "page": 1,
    "size": 20,
    "total": 3
    }

    name is the value in @type, and the value the type parameter takes on discovery requests.

    If a type you expected is absent, no content of it has been indexed. Where a feed supplies that type, check its run history.

    total: 0 means nothing of any type is indexed. An unknown catalog_id answers the same way: this endpoint reads a catalog’s content and does not resolve its identity. To tell the two apart, read the catalogs you can see from get_entitlements.

    Terminal window
    curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/types/product/attributes' \
    --data-urlencode 'query=price' \
    --data-urlencode 'size=50' \
    -H 'Authorization: Bearer <token>'

    Each attribute comes back with the shape of its values:

    {
    "items": [
    { "name": "list_price", "type": { "shape": "number" } },
    { "name": "price", "type": { "shape": "number" } },
    { "name": "price_history", "type": { "shape": "array", "element": "number" } }
    ],
    "page": 1,
    "size": 50,
    "total": 3
    }
    FieldMeaning
    nameThe attribute name, exactly as you use it in filters, sorts and return_fields. Nested attributes appear as dot-paths, e.g. params.color.
    type.shapestring, number, boolean, object or array
    type.elementFor an array, the shape of its items, when known

    query matches the attribute name, case-insensitively, on a substring.

    A field’s type decides what the field can do:

    If a field isYou getYou do not get
    numberRange filters (price < 50), a range facet, numeric sorting
    stringEquality and membership filters, a terms facetRange filters, numeric sorting

    A price reported as string is why price < 50 returns nothing, why the price facet renders as a list of values, and why sorting by price puts 9.90 before 10.00.

    The type is fixed by the first value the field ever receives in the catalog, and later conflicting values are rejected rather than coerced. One early feed run sending "price": "29.90" types the field as text permanently.

    The fix is at the mapping, not the query — add | to_f, then reset the feed’s fingerprints so every record is resent. Ask Luigi’s Box support if the field needs to be re-typed outright.

    Terminal window
    curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/types/product/attributes/color/values' \
    --data-urlencode 'query=bl' \
    -H 'Authorization: Bearer <token>'

    The values present in the catalog, most common first:

    {
    "items": [
    { "value": "blue" },
    { "value": "black" }
    ],
    "page": 1,
    "size": 20,
    "total": 2
    }

    Values come back typed to the attribute’s own shape — numbers as numbers, booleans as booleans, not as strings.

    • At most 1,000 distinct values are considered, ranked by how many objects carry them. total is capped there, so a high-cardinality field reports the 1,000 most common rather than everything.
    • query works on text attributes only. It is a case-insensitive substring match.
    • A non-scalar attribute has no pickable values and returns an empty page. An unknown attribute returns 404.

    A filter or rule builder can read this endpoint to populate a value picker.

    SymptomCheck
    A filter returns nothingIs the field there? Is its shape what you assumed? Is the value spelled that way?
    A range filter returns nothingshape is string
    A facet renders as a list instead of a sliderSame — shape is string
    Sorting looks alphabeticalSame
    A field is missing from hitsIs it in the attribute list at all? If not, it never arrived. If it is, check return_fields and whether it is private:
    A filter.unknown_field errorThe field is not in this catalog under that name

    A script that lists every attribute and its shape:

    BASE = "https://api.eu1.luigisbox.ai/catalog/v1"
    types = client.get(f"{BASE}/{catalog_id}/types").json()["items"]
    for object_type in types:
    attributes = client.get(
    f"{BASE}/{catalog_id}/types/{object_type['name']}/attributes",
    params={"size": 200},
    ).json()["items"]
    print(object_type["name"], len(attributes), "attributes")
    for attribute in attributes:
    print(" ", attribute["name"], attribute["type"]["shape"])

    Run it after the first successful feed run to find mistyped fields before they reach a filter.