---
title: "Search and retrieve products"
method: GET
path: "/v1/public/products"
tags: ["Public API", "Public API", "Products"]
---

# Search and retrieve products

`GET /v1/public/products`

## Request

- Base URL: `http://localhost:8000`
- URL: `http://localhost:8000/v1/public/products`
- Auth: none declared

Search products in the Partle marketplace with advanced filtering options.
    No authentication required.

    **Search modes:**
    - **Default (keyword):** Full-text search with English stemming + fuzzy name matching.
    - **Semantic (`semantic=true`):** Cross-language vector search. Finds products by meaning,
      not just keywords. E.g., searching "drill" also finds "taladro" (Spanish) and
      "Bohrmaschine" (German). Best for AI agents that need broad discovery across languages.

    **Use Cases:**
    - `?q=wireless headphones&max_price=100` — keyword search with price filter
    - `?q=drill&semantic=true` — find all drill-related products in any language
    - `?tags=electronics&sort_by=newest` — browse by category

    **Rate Limit:** 100 requests per hour per IP

## Query parameters

- `q` string, nullable: Search term to find products by name or description (e.g., 'wireless headphones', 'adhesive tape')
- `limit` integer: Number of products to return (1-100, default: 20)
- `offset` integer: Number of products to skip for pagination (default: 0)
- `min_price` number, nullable: Minimum price filter in EUR (e.g., 10.50)
- `max_price` number, nullable: Maximum price filter in EUR (e.g., 100.00)
- `tags` string, nullable: Filter by tags, comma-separated (e.g., 'electronics,bluetooth' or 'adhesive,tape')
- `has_price` boolean, nullable: When true, exclude products without a listed price. When false, return only price-less products. Omit to include both.
- `sort_by` string, nullable: One of price_asc, price_desc, name_asc, newest, oldest, random. Omit for default relevance ranking when q is set, or newest otherwise.
- `semantic` boolean: Use semantic (vector) search for cross-language matching. When true, ranks by meaning similarity instead of keyword matching. E.g., searching 'drill' also finds 'taladro' (Spanish) and 'Bohrmaschine' (German).
- `super_search` boolean: Cross-encoder rerank of the top-50 bi-encoder candidates. ~1s/query. Use for descriptive or attribute-style queries ('something with a thread, made of plastic') where relevance, not speed, is the priority. Returns a well-calibrated `relevance_score` (0-1) per product — values <0.2 mean the catalog has nothing close to your query.

## Response `200`

List of products matching the search criteria

- ProductOut[]
  - `store_id` integer, nullable: Store ID to list under (omit for personal listing)
  - `name` string, required: Product name/title
  - `sku` string, nullable: Stock Keeping Unit - unique per store
  - `spec` string, nullable
  - `price` string, nullable: Price in whole currency units (e.g. 15.99 means €15.99). NOT cents.
  - `currency` string, nullable: Currency symbol (e.g. €, $, CHF)
  - `url` string, uri, nullable: Link to the product page
  - `lat` number, nullable
  - `lon` number, nullable
  - `description` string, nullable: Product description
  - `expires_at` string, date-time, nullable: When the listing expires (for second-hand items)
  - `verified` boolean: Whether the responsible human has confirmed this listing. Default true covers humans, scrapers (the source website's listing is the implicit confirmation), and AI agents acting as the seller themselves. Set false ONLY when an AI agent is proposing a listing on behalf of a human who has not explicitly confirmed it (tentative).
  - `listing_type` 'in_stock' | 'tentative': Whether a product row is real stock or a demand probe. ``in_stock`` is a normal listing: someone has the thing and you can go buy it. That is Partle's whole promise, so it is the default and the only type the catalog returns unless a caller opts out. ``tentative`` is a seller floating an item they do not stock yet, to see whether anyone wants it. Interest is recorded in ``product_demand``. A tentative row is excluded from every catalog read by default (see ``apply_visible_products_filter``) — a shopper searching for a drill must never be shown one that cannot be bought. Named ``listing_type`` rather than ``inventory_type`` because ``inventory_items`` / ``InventoryStatus`` in this module already mean a user's personal holdings, which is a different thing entirely. Buyers asking for something nobody sells are NOT modelled here — that is ``BuyRequest`` below, which predates this and has its own table, public feed at /v1/public/wanted, and MCP tools.
  - `id` integer, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `updated_by_id` integer, nullable
  - `creator_id` integer, nullable
  - `creator` AppSchemasProductUserBasic: Basic user info for product creator display.
    - `id` integer, required
    - `username` string, nullable
  - `store` StoreBasic: Minimal store info included with products.
    - `id` integer, required
    - `name` string, required
    - `address` string, nullable
    - `country` string, nullable
  - `tags` Tag[]
    - `name` string, required
    - `description` string, nullable
    - `id` integer, required
  - `images` ProductImageOut[]
    - `id` integer, required
    - `image_filename` string, nullable
    - `image_content_type` string, nullable
    - `image_path` string, nullable
    - `source_url` string, nullable
    - `position` integer
  - `sold_at` string, date-time, nullable
  - `accurate_share` number, nullable
  - `report_count` integer, nullable
  - `last_verified_at` string, date-time, nullable
  - `product_average_rating` number, nullable
  - `product_rating_count` integer
  - `accuracy_average_rating` number, nullable
  - `accuracy_rating_count` integer
  - `relevance_score` number, nullable
  - `demand_count` integer, nullable
  - `type` string
  - `partle_url` string, required
  - `_hint` string, required

## Other responses

- `422`: Validation Error
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/rubenayla/apis/partle-api.md) · [All operations](https://skmtc.dev/rubenayla/apis/partle-api/llms.txt) · [OpenAPI document](https://skmtc.dev/rubenayla/apis/partle-api/revisions/628cce2acee5?raw)
