Returns a ranked list of matching products with prices, stock status, images, and URLs,
plus the category facets available for the query (facets.categories) — the names that
work in `category` to narrow the same search.
Supports cursor-based pagination — pass next_cursor from one response into the next call.
Pagination is capped at 3 pages per query to discourage catalog enumeration; for broader
discovery, refine the query or narrow with a facet instead.
Queries must be at least 3 characters and contain specific terms — pure stopword queries
(e.g. "the", "a an") are rejected.
Relevance: the API matches ANY single query word, so extra words add loosely related
results rather than narrowing. Search the thing the customer wants (the head noun) and
check the product names actually contain it before presenting them.
Prices and min_price/max_price are both in `currency` — pass the customer's own budget
in their own currency; do not convert it. A row's `price` is the discounted price
checkout charges (the bounds filter on it); `compare_at_price` is set when on sale.
Search results carry NO delivery-scope information. Food, hotel cakes and liquor
are delivered only to selected cities — never infer deliverability from a search
hit. Before quoting delivery on a specific item, call kapruka_get_product (read
`delivery.island_wide`) or kapruka_check_delivery with `product_id`.
Args:
params (SearchProductsInput):
- q (str): Search query (e.g. 'birthday cake', 'roses', 'tea gift'). Min 3 chars.
- category (Optional[str]): A facet name from a previous search's facets.categories
- limit (int): Results per page, 1–50 (default 10)
- cursor (Optional[str]): Pagination cursor from previous response
- currency (str): LKR (default), USD, GBP, AUD, EUR
- min_price (Optional[float]): Min price (inclusive) in `currency`
- max_price (Optional[float]): Max price (inclusive) in `currency`
- in_stock_only (bool): Restrict to in-stock items (default false)
- sort (str): 'relevance' | 'price_asc' | 'price_desc' | 'newest' | 'bestseller'
- include_stubs (bool): Deprecated, no effect
- response_format (str): 'markdown' (default) or 'json'
Returns:
str: Search results in the requested format.
JSON schema:
{
"results": [
{
"id": str,
"type": "product",
"name": str,
"summary": str,
"price": {"amount": float | null, "currency": str},
"compare_at_price": {"amount": float, "currency": str} | null,
"in_stock": bool,
"stock_level": str,
"image_url": str | null,
"category": {"id": str, "name": str, "slug": str},
"rating": null,
"ships_internationally": bool,
"url": str
}
],
"next_cursor": str | null, # null after page 3 even if upstream has more
"total_estimate": int, # index hits for the query words (any-word match)
"applied_filters": {"q": str, "category": str, "currency": str, ...},
"facets": {"categories": [{"name": str, "count": int}]},
"category_filter_dropped": str, # only when `category` was not a facet here
"valid_categories": [str] # ...and the facet names that are
}
Error: "Error: <message>" or "No products found for '<query>'" on failure.