Skip to main content

Live Example

Overview

Use product discovery to power search bars, product detail pages, and catalog browsers across any merchant on the internet. Both products.search and products.details are async jobs - they return a refId, and results arrive once the background work completes. See Polling or Webhooks for how to handle the async pattern.

Prerequisites

import HenrySDK from '@henrylabs/sdk';

const henry = new HenrySDK({
	apiKey: process.env.HENRY_SDK_API_KEY!,
});

Search the catalog

1

Kick off a product search

Call products.search with a text query and optional filters. Returns immediately with a refId.
const search = await henry.products.search({
  type: 'global',
  filters: {
    type: 'text',
    query: 'Nike Air Max',
    country: 'US',
    price: { min: 50, max: 300, currency: 'USD' },
    sortBy: 'price-low-to-high',  // or 'price-high-to-low'
  },
  limit: 20,              // 1–100, default 20
  mode: 'async',          // or 'sync' to wait up to 30s for results
});

console.log(search.refId, search.status);
// → "prs-ref_3fa8..." "pending"
2

Poll for results

Use products.pollSearch to check status until it’s "complete" or "failed". See Polling for the reusable helper and strategies.
const result = await henry.products.pollSearch({ refId: search.refId });
// result.status: "pending" | "processing" | "complete" | "failed"
3

Read the results

const { products, pagination } = result.result!;

for (const product of products) {
  console.log({
    name: product.name,
    price: `${product.price.value} ${product.price.currency}`,
    merchant: product.merchant,
    link: product.link,       // use this as the cart item `link`
    inStock: product.availability === 'in_stock',
  });
}

// Paginate with the cursor
if (pagination.nextCursor) {
  const nextPage = await henry.products.search({
    type: 'global',
    filters: { type: 'text', query: 'Nike Air Max' },
    cursor: Number(pagination.nextCursor),
  });
}

Fetch product details

If you need more granular product information, such as available options and pricing, call products.details with the link from search for the full enriched payload.
1

Request product details

const detailJob = await henry.products.details({
  link: 'https://www.nike.com/t/air-max-270-shoes/AH8050-002',
  country: 'US',          // optional default country
  mode: 'async',          // or 'sync' to wait up to 30s for results
});

console.log(detailJob.refId, detailJob.status);
// → "prd-ref_3fa8..." "pending"
2

Poll for results

Use products.pollDetails to check status. See Polling for the reusable helper.
const detail = await henry.products.pollDetails({ refId: detailJob.refId });
// detail.status: "pending" | "processing" | "complete" | "failed"
3

Read the enriched product

const product = detail.result;

console.log({
  name: product?.name,
  price: product?.price,
  options: product?.options,    // recursive option tree with availability
  featuredImage: product?.images?.find(i => i.isFeatured)?.url,
});
Henry caches product details internally. If details for a link are already fresh, pollDetails will return "complete" on the very first call.

Search by image instead of text. Pass a URL, data URL, or base64 payload to find visually similar products.
// Search with an image URL
const imageSearch = await henry.products.search({
	type: 'global',
	filters: {
		type: 'image',
		imageUrl: 'https://static.nike.com/a/images/t_web_pdp_535_v2/f_auto/example.png',
		country: 'US',
	},
	limit: 10,
	mode: 'async',
});

// Poll the same way as text search
const result = await henry.products.pollSearch({ refId: imageSearch.refId });
You can also pass a base64-encoded image:
const imageSearch = await henry.products.search({
	type: 'global',
	filters: {
		type: 'image',
		imageUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...',
		country: 'US',
	},
	limit: 5,
});

Sync mode

All async operations support mode: "sync", which waits up to 30 seconds for the operation to complete before returning. This can simplify your code for quick lookups by avoiding polling entirely.
// Sync mode - returns results directly (no polling needed)
const search = await henry.products.search({
	type: 'global',
	filters: { type: 'text', query: 'Nike Air Max' },
	limit: 5,
	mode: 'sync',
});

// If the search completed within 30s, results are already here
if (search.status === 'complete') {
	console.log(search.result?.products);
}
If the operation takes longer than 30 seconds, sync mode returns with a non-terminal status and you’ll need to poll normally. For operations that may take longer (e.g. large searches), use mode: "async" with Polling.

Use type: 'merchant' to search within a specific merchant’s catalog. Pass the merchant name, host, or any URL from that merchant.
const search = await henry.products.search({
	type: 'merchant',
	filters: { merchant: 'nike.com' },
	query: 'running shoes', // optional text query within the merchant
	limit: 20,
	mode: 'sync',
});
Merchant search accepts a case-insensitive name (e.g. "Nike"), a host (e.g. "nike.com"), or any URL from the merchant. When query is omitted, the merchant’s catalog browse is returned.

Search parameters reference

Top-level parameters

ParameterTypeDescription
type"global" | "merchant"Required. "global" for cross-merchant search, "merchant" for single-merchant
filtersobjectRequired. Search filters (see below)
querystringOptional text query (merchant search only)
limitnumberResults per page - 1 to 100, default 20
cursorintegerPagination cursor from a previous response
mode"async" | "sync""async" (default) returns immediately; "sync" waits up to 30s

Global search filters (type: 'global')

ParameterTypeDescription
filters.type"text" | "image"Required. Text query or image-based search
filters.querystringRequired for text. Full-text search term
filters.imageUrlstringRequired for image. HTTP URL, data URL, or base64 payload
filters.countrystringISO country code (e.g. "US", "JP")
filters.price.minnumberMinimum price filter (text search only)
filters.price.maxnumberMaximum price filter (text search only)
filters.price.currencystringCurrency for price filter (e.g. "USD")
filters.sortBy"price-low-to-high" | "price-high-to-low"Sort by price (text search only)

Merchant search filters (type: 'merchant')

ParameterTypeDescription
filters.merchantstringRequired. Merchant name, host, or URL (e.g. "nike.com")
filters.price.minnumberMinimum price filter
filters.price.maxnumberMaximum price filter
filters.price.currencystringCurrency for price filter

Error handling

ErrorCauseResolution
401 UnauthorizedInvalid API keyCheck HENRY_SDK_API_KEY
400 Bad RequestMissing query or invalid filter valuesValidate input before calling
status: "failed"Job error - check error fieldLog and retry

Next steps

Universal Cart

Use the product link to add items to a cart

Merchants

Browse supported merchants beforehand