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.
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.
Object types
Section titled “Object types”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", "object_count": 184 }, { "name": "category", "object_count": 412 }, { "name": "product", "object_count": 28749 } ], "page": 1, "size": 20, "total": 3}name is the value in @type, and the value the type parameter takes on
discovery requests. object_count is the number of
objects currently indexed with that type across the whole catalog. It reflects the catalog
metadata available at the time of the request rather than the source feed’s declared size.
It is not filtered by channel availability and counts variants as individual objects, so it
can differ from a discovery response’s total. The page-level total is the number of
object types, not the number of catalog objects.
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.
Attributes and their types
Section titled “Attributes and their types”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}| Field | Meaning |
|---|---|
name | The attribute name, exactly as you use it in filters, sorts and return_fields. Nested attributes appear as dot-paths, e.g. params.color. |
type.shape | string, number, boolean, object or array |
type.element | For an array, the shape of its items, when known |
query matches the attribute name, case-insensitively, on a substring.
Four namespaces are left out, because no filter or rule may name them — offering them would
only point a picker at a value that cannot be saved. signals:, ml: and lexical: hold
data we derive from your catalog rather than anything you sent. private: is your own data too —
it is simply never returned to a browser token. Everything else you send appears here.
New-arrival flag
Section titled “New-arrival flag”On catalogs configured to derive it from a feed’s new-arrival label,
lbx:is_new_arrival is a boolean: true when the product carries that label and
false when it does not, including when the label is removed. It reflects the shop’s
designation, not a product’s age or introduction date.
The field is catalog-dependent. Check the attribute listing for its availability before
using it in filters or requesting it in return_fields; an absent field does not mean
false.
Why types matter more than they look
Section titled “Why types matter more than they look”A field’s type decides what the field can do:
| If a field is | You get | You do not get |
|---|---|---|
number | Range filters (price < 50), a range facet, numeric sorting | |
string | Equality and membership filters, a terms facet | Range 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.
Distinct values
Section titled “Distinct values”curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/types/product/attribute-values' \ --data-urlencode 'attribute_name=color' \ --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.
- Send the attribute name percent-encoded in
attribute_name. Once encoded, any name resolves —AES/EBU,m²andh+wincluded. Unencoded, a+arrives as a space and a&or#cuts the name short.curl --data-urlencodeand an HTTP client’s query parameters, such asparams=in Python, encode it for you. - At most 1,000 distinct values are considered, ranked by how many objects carry them.
totalis capped there, so a high-cardinality field reports the 1,000 most common rather than everything. queryworks 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
422— as does one in a reserved namespace, which the attribute listing does not carry either.
A filter or rule builder can read this endpoint to populate a value picker.
Using it to debug
Section titled “Using it to debug”| Symptom | Check |
|---|---|
| A filter returns nothing | Is the field there? Is its shape what you assumed? Is the value spelled that way? |
| A range filter returns nothing | shape is string |
| A facet renders as a list instead of a slider | Same — shape is string |
| Sorting looks alphabetical | Same |
| A field is missing from hits | Is it in the attribute list at all? If not, it either never arrived or sits in a reserved namespace. If it is, check return_fields |
A filter_unknown_field error | The 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.
See also
Section titled “See also”- Filters — where these names and types are used
- Facets — why a facet’s type follows the field’s
- Field mapping — fixing a type at the source
- Feed troubleshooting
Was this page helpful?
Thanks.