---
title: "Search products by keyword with full-text search"
method: GET
path: "/products/search"
---

# Search products by keyword with full-text search

`GET /products/search`

## Request

- Base URL: `https://api.buywhere.ai/v1`
- URL: `https://api.buywhere.ai/v1/products/search`
- Auth: none required
- TypeScript: <https://skmtc.dev/buywhere/apis/buywhere-product-catalog-api/docs/products/search/get.ts> (a function with typed inputs; its response is checked against the schema)

Public read endpoint: works without credentials at anonymous rate limits. Sending a Bearer API key raises limits to your tier (free tier: 10 requests/min, 10,000/day) and enables usage attribution.

## Query parameters

- `q` string
- `query` string
- `deliver_to` 'SG' | 'US' | 'VN' | 'TH' | 'MY' | 'ID' | 'PH'
- `domain` string
- `region` string
- `country_code` 'SG' | 'US' | 'VN' | 'TH' | 'MY'
- `include_unshippable` boolean
- `min_price` number
- `max_price` number
- `currency` string
- `compact` boolean
- `limit` integer
- `offset` integer
- `mode` 'keyword' | 'semantic' | 'hybrid'

## Response `200`

Product list with meta (total, response_time_ms, cached, mode)

- object
  - `data` object[]
    - `id` string, uuid: Unique product identifier
    - `title` string: Product title
    - `price` object: Current price object
      - `amount` number
      - `currency` string
    - `merchant` string: Merchant slug
    - `url` string, uri: Product page URL
    - `image_url` string, uri: Product image URL
    - `country_code` 'SG' | 'US' | 'VN' | 'TH' | 'MY': ISO country code
    - `category_path` string[]: Category hierarchy
    - `updated_at` string, date-time
    - `click_url` string, uri: BuyWhere tracked click URL
    - `affiliate_disclosure` string: Affiliate disclosure text
    - `metadata` object: Additional product metadata
  - `meta` object
    - `total` integer: Total matching products (approximate for large catalogs)
    - `limit` integer: Items returned per page
    - `offset` integer: Pagination offset
    - `response_time_ms` integer: Server processing time in milliseconds
    - `cached` boolean: Whether this response was served from cache
    - `mode_used` 'keyword' | 'semantic' | 'hybrid': Search mode used
    - `mode_used_engine` string: Detailed engine description (e.g. "keyword (fts)")
    - `has_more` boolean: Whether more results are available
    - `hint` string: Usage guidance for agents (e.g. deliver_to recommendation)
    - `degraded` boolean: Whether the response is degraded due to upstream issues
    - `status` 'ok' | 'degraded': Response status
    - `emptiness_reason` string: Why results are empty (e.g. timeout, no_match)
    - `confidence` 'high' | 'medium' | 'low': Result confidence
    - `diagnostic` object
      - `engine_status` string
      - `indexed_for_region` boolean
      - `category_recognized` boolean
      - `timed_out_stage` string: Stage that timed out (catalog_search, offer_aggregation)
      - `deliver_to_present` boolean
      - `rate_limit_remaining` integer, nullable
    - `degraded_kind` string: Type of degradation (timeout, api_error, auth_failure)
    - `degraded_reason` string: Human-readable degradation reason
    - `deliver_to` string: Buyer delivery country used for ranking
    - `deliver_to_inferred` boolean: Whether deliver_to was inferred from country_code
  - `search_mode` object: Mode honesty: which engine the request asked for, which one actually ran, and why any fallback happened.
    - `requested_mode` 'keyword' | 'semantic' | 'hybrid' | 'null', nullable
    - `executed_mode` 'keyword' | 'semantic' | 'hybrid'
    - `fallback_reason` string, nullable: e.g. vector_error, query_embed_failed, vector_db_unavailable, sort_forces_keyword

## Other responses

- `400`: Invalid query parameters (limit out of range, malformed country code, inverted or negative price range, unknown mode)
- `429`: Rate limit exceeded

## Changes

- **2026-09-02** `36d50d246db5`: 1 breaking, 4 info
  - the response property `meta/diagnostic/rate_limit_remaining` became nullable for the status `200`
  - the endpoint scheme security `BearerAuth` was removed from the API
  - added the non-success response with the status `400`
  - removed the non-success response with the status `401`
  - …1 more
- **2026-08-29** `bee66d8c0140`: 4 info
  - added the new optional `query` request parameter `deliver_to`
  - added the new optional `query` request parameter `include_unshippable`
  - added the new optional `query` request parameter `query`
  - added the media type `application/json` for the response with the status `200`

[Operation history](https://skmtc.dev/buywhere/apis/buywhere-product-catalog-api/changes/products/search/get.md)

---

[API](https://skmtc.dev/buywhere/apis/buywhere-product-catalog-api.md) - [All operations](https://skmtc.dev/buywhere/apis/buywhere-product-catalog-api/llms.txt) - [OpenAPI document](https://skmtc.dev/buywhere/apis/buywhere-product-catalog-api/revisions/4f6fccb33645?raw)
