Product Search

Mint a short-lived, search-only token that queries an account's product catalog directly from the browser, with no further call to ibakepro per search.

Intended for headless and custom storefronts. It is products-only: there is no equivalent token for orders, customers, or other resources.

How it works

  1. Your app calls POST /api/v2/products/search-token with an ibakepro API key.
  2. ibakepro returns a scoped search token (valid 1 hour), the search host to connect to, and the recommended query parameters.
  3. Your app queries the search engine (Typesense) directly using that token - no further calls to ibakepro per keystroke.
  4. The token stops working 1 hour after minting. Mint a new one to keep searching.

Call this from the browser with a publishable key (ibp_pk_); secret keys also work. The account is derived from the key, never from the request.


POST/api/v2/products/search-token

Mint a search token

Returns a scoped, search-only token plus the host and query parameters to use. No request body is required, and any body you send is ignored.

Required scope: products:search

This scope is available to publishable keys and is separate from products:read. A products:search key can only mint search tokens - it cannot paginate or bulk-export the catalog the way products:read can.

Response fields

  • Name
    token
    Type
    string
    Description

    The scoped, search-only token. Send it as the X-TYPESENSE-API-KEY header when querying the search host. The parent search key is never returned.

  • Name
    search
    Type
    object
    Description

    host, port, protocol, and collection (products) - where to send your search requests.

  • Name
    query
    Type
    object
    Description

    Recommended search parameters: query_by, group_by, group_limit, per_page, sort_by. Use them as-is for behaviour matching the ibakepro storefront. They are defaults, not constraints - you may override per_page or sort_by on your own request.

  • Name
    expires_at
    Type
    integer
    Description

    Unix timestamp in seconds when the token stops working. This is the expiry baked into the token itself, not a re-derived clock read. Tokens last 1 hour from minting.

Errors

If search is not configured for the account's region, or key minting fails, the endpoint returns 503 with code search_not_configured. This is an infrastructure state rather than a bad request, and it is retryable.

Request

POST
/api/v2/products/search-token
curl -X POST https://au.api.ibakepro.com/api/v2/products/search-token \
  -H "Authorization: Bearer ibp_pk_live_au_ABC"

Response

{
  "token": "<scoped search-only token>",
  "search": {
    "host": "n7k2qx4p1v9t3wzb-1.a1.typesense.net",
    "port": 443,
    "protocol": "https",
    "collection": "products"
  },
  "query": {
    "query_by": "name,category,description",
    "group_by": "productId",
    "group_limit": 1,
    "per_page": 8,
    "sort_by": "_text_match:desc"
  },
  "expires_at": 1781694000
}

Querying the catalog

Send your search directly to the returned host, using the token as the X-TYPESENSE-API-KEY header. With group_by=productId, results arrive under grouped_hits (one group per product, because the index holds one document per variant); take the first hit in each group.

Browser

// 1. Mint a token. It is valid for 1 hour.
const res = await fetch(
  'https://au.api.ibakepro.com/api/v2/products/search-token',
  { method: 'POST', headers: { Authorization: 'Bearer ibp_pk_live_au_ABC' } },
)
const { token, search, query } = await res.json()

// 2. Query the catalog directly, with no further ibakepro call.
async function searchProducts(q) {
  const params = new URLSearchParams({ q, ...query })
  const r = await fetch(
    `${search.protocol}://${search.host}:${search.port}` +
      `/collections/${search.collection}/documents/search?${params}`,
    { headers: { 'X-TYPESENSE-API-KEY': token } },
  )
  const data = await r.json()
  return (data.grouped_hits || []).map((g) => g.hits[0].document)
}

Result fields

The token bakes in an include_fields allowlist, so a hit can only ever carry these public catalog fields (any that the document actually has):

productId, name, slug, description, category, imageUrl,
price, priceMin, priceMax, pricePerPortion, portions,
variantName, variantCount, size, availableSizes, availableTiers, tierCount,
storefrontEnabled, featured, collections, isUpsell, upsellCategories,
allergens, mayContain, dietaryFlags, metaTitle, metaDescription

Because it is an allowlist rather than a denylist, internal fields added to the index later can never start leaking. Costs, margins, recipe composition, revenue, and order counts are not in it.

A field is present on a hit only when the indexed document carries it, so slug and the price fields can be absent. Search hits are for discovery only - fetch the product by productId before building an order line, since variants, choices, and the authoritative pricing are not in the index.

allergens and mayContain here are the raw indexed lists, not the { allergen, level } shape the products endpoint returns. Read allergens from the product endpoint before displaying them on a product page.

Security

The token embeds a filter of the issuing account plus status:=active and storefrontEnabled:=true, and that filter is signed into the token, so a client cannot widen it or reach another account's catalog. It is search-only: no writes, no other collections, and no way to add fields beyond the allowlist above. It expires 1 hour after minting.

Rate limits

The mint endpoint is rate-limited per key, under the same api-write and api-public-ip rules as any other POST. See Rate limiting. Requests sent directly to the search host do not count against ibakepro's limits.