# Brand Source: https://docs.trychannel3.com/api-reference/brand-model A **brand** is the company or label behind a product — for example Nike or Sony. Filter search results with `brand_ids`, or resolve IDs via [`GET /v1/brands/search`](/api-reference/v1/search-brands). Brands are not the same as [websites](/api-reference/website-model) (retailers). A brand makes or labels a product; a website is the online store where you can buy it. One brand's products may appear on many websites. # Category Source: https://docs.trychannel3.com/api-reference/category-model # Websites Source: https://docs.trychannel3.com/api-reference/channel3-api/websites get /v0/websites Resolve a website URL to its ID and best_commission_rate. Tip: website_ids filters accept domains directly, so this lookup is most useful for retrieving commission rates. # Get Price History Source: https://docs.trychannel3.com/api-reference/price-tracking/get-price-history get /v0/price-tracking/history/{canonical_product_id} Get price history for a canonical product. # List Subscriptions Source: https://docs.trychannel3.com/api-reference/price-tracking/list-subscriptions get /v0/price-tracking/subscriptions List your active price tracking subscriptions. # Start Tracking Source: https://docs.trychannel3.com/api-reference/price-tracking/start-tracking post /v0/price-tracking/start Start tracking prices for a canonical product. # Stop Tracking Source: https://docs.trychannel3.com/api-reference/price-tracking/stop-tracking post /v0/price-tracking/stop Stop tracking prices for a canonical product. # Brand Detail Source: https://docs.trychannel3.com/api-reference/v1/brand-detail get /v1/brands/{brand_id} Get detailed information about a specific brand by its ID. # Browse Source: https://docs.trychannel3.com/api-reference/v1/browse post /v1/browse List and page through products for a set of filters. Useful for a static, grid view of products for a brand, website, or category. At least one of `filters.brand_ids`, `filters.category_ids`, or `filters.website_ids` must be provided, unless `config.collection_id` is set. Pagination is capped at 5 pages per browse; `next_page_token` is null on the final page. Contact support@trychannel3.com for deeper paging. Access to this endpoint is restricted. If you think your use case requires it, contact us at [support@trychannel3.com](mailto:support@trychannel3.com) to get access. List and page through products for a set of filters — useful for a static, grid view of products for a brand, website, or category. At least one of `filters.brand_ids`, `filters.category_ids`, or `filters.website_ids` must be provided, unless `config.collection_id` is set. See [Collections](/guides/collections). ### Page depth Browsing is limited to **5 pages** per filter set (up to 150 products at the maximum `limit` of 30). `next_page_token` is `null` on the fifth page, and a request with a token past page 5 returns `422 Unprocessable Entity`. Contact us about custom data deals. # Category Detail Source: https://docs.trychannel3.com/api-reference/v1/category-detail get /v1/categories/{slug} Look up a category by slug. # Create Client Token Source: https://docs.trychannel3.com/api-reference/v1/create-client-token post /v1/conversations/client_tokens Mint a short-lived, browser-safe token. With ``conversation_id`` the token continues and reads that thread; without it, the token's first turn creates the thread and binds the token to it. See [Client tokens](/conversations/client-tokens). # Create Collection Source: https://docs.trychannel3.com/api-reference/v1/create-collection post /v1/collections Create a collection. Clauses are optional so an empty collection can be curated later. Collection CRUD is free. # Create Turn Source: https://docs.trychannel3.com/api-reference/v1/create-turn post /v1/conversations Run one conversation turn. Omit `conversation_id` to create the thread with this turn; pass it to continue an existing thread. See [Run a turn](/conversations/run-a-turn) and [Streaming events](/conversations/streaming-events). # Delete Collection Source: https://docs.trychannel3.com/api-reference/v1/delete-collection delete /v1/collections/{collection_id} Delete a collection owned by this API key. # Find products in an image Source: https://docs.trychannel3.com/api-reference/v1/find-products-in-image post /experimental/find-products-in-image Name objects in a scene image, localize them, and search the catalog per crop. Charges 1 credit for the call plus 1 credit per detected thing. ``filters`` apply to every per-thing search. ``limit`` is per thing, not across the whole response. Pass `config.collection_id` to search each detected thing inside a [collection](/guides/collections). # Get Collection Source: https://docs.trychannel3.com/api-reference/v1/get-collection get /v1/collections/{collection_id} Get a collection owned by this API key. # Get Conversation Source: https://docs.trychannel3.com/api-reference/v1/get-conversation get /v1/conversations/{conversation_id} Thread metadata plus one page of its message history. Paginate `items` with `limit` and `cursor`. See [Read a conversation](/conversations/read-a-conversation). # Image Search Source: https://docs.trychannel3.com/api-reference/v1/image-search post /v1/image-search Search the catalog by image (URL or base64), with pagination support. Provide exactly one of ``image_url`` or ``base64_image``. For text or text+image search, use ``POST /v1/search``. # List Brands Source: https://docs.trychannel3.com/api-reference/v1/list-brands get /v1/brands Paginated list of brands, capped at the top 5,000. # List Categories Source: https://docs.trychannel3.com/api-reference/v1/list-categories get /v1/categories Paginated list of all categories. # List Collections Source: https://docs.trychannel3.com/api-reference/v1/list-collections get /v1/collections List collections, optionally filtered to one partner user. Collection CRUD is free. # Lookup Product Source: https://docs.trychannel3.com/api-reference/v1/lookup-product post /v1/lookup Retrieve product information for any supported product URL. Returns the same Product model as GET /v1/products/{product_id}. The product_id in the response can be used with the Product Detail endpoint. # Product Detail Source: https://docs.trychannel3.com/api-reference/v1/product-detail get /v1/products/{product_id} Get detailed information about a specific product by its ID. # List Clicks Source: https://docs.trychannel3.com/api-reference/v1/reporting-clicks get /v1/reporting/clicks List clicks for your account over a datetime window. Defaults to the last 30 days ending now. Maximum window is 90 days. Pass an offset-aware ISO datetime to express local time (e.g. last 6 hours). Returns a summary plus a paginated list of click events (most recent first). # List Transactions Source: https://docs.trychannel3.com/api-reference/v1/reporting-transactions get /v1/reporting/transactions List transactions for your account over a datetime window. Defaults to the last 30 days ending now. Maximum window is 90 days. Pass an offset-aware ISO datetime to express local time (e.g. last 6 hours). Returns a summary of net commission (after take rate) plus a paginated list of transactions (most recent first). Network-approved commissions appear as pending. # Revoke Client Token Source: https://docs.trychannel3.com/api-reference/v1/revoke-client-token post /v1/conversations/client_tokens/revoke Revoke a client token immediately. The token travels in the request body, not the URL, so it stays out of access logs; only the minting vendor can revoke it. See [Client tokens](/conversations/client-tokens). # Search Source: https://docs.trychannel3.com/api-reference/v1/search post /v1/search Search for products with pagination support. At least one of `query`, `image_url`, `base64_image`, or `page_token` must be provided; requests with none of these will return 422. # Search Brands Source: https://docs.trychannel3.com/api-reference/v1/search-brands get /v1/brands/search Search brands by free-text query. # Search Categories Source: https://docs.trychannel3.com/api-reference/v1/search-categories get /v1/categories/search Search categories by free-text query. # Similar Products Source: https://docs.trychannel3.com/api-reference/v1/similar-products post /v1/similar Find products similar to a given product. Consider setting ``filters`` to narrow results to the same gender, brand, category, price range, etc. when you only want similar items within a specific slice of the catalog. # Update Collection Source: https://docs.trychannel3.com/api-reference/v1/update-collection patch /v1/collections/{collection_id} Rename a collection and/or add, replace, or remove clauses by id. # Website Source: https://docs.trychannel3.com/api-reference/website-model A **website** is an online retailer or merchant that sells products — for example `nike.com` or `bestbuy.com`. Filter search results with `website_ids`, or browse the full list via [`GET /v0/websites`](/api-reference/channel3-api/websites). Websites are not the same as [brands](/api-reference/brand-model). A brand (Nike, Sony) is the company behind a product; a website is the store listing it. One brand's products often appear on many websites, and one website sells products from many brands. # Category Explorer Source: https://docs.trychannel3.com/categories Browse the Channel3 category tree. Every Channel3 category has a stable URL-friendly **slug** (for example `sofas`, `running-shoes`). Pass a slug to filter or look up categories anywhere a category identifier is accepted. Search the full tree below, or hit `GET /v1/categories/search` to do the same from your code. # Announcing the /v1 Search API & SDK 3.0.0 Source: https://docs.trychannel3.com/changelog/announcements/update-1-v1-search-api *March 12, 2026 · API Update #1* Hello Developer! Today, we're excited to announce Channel3's V1 Product Search API. We've learned a lot about how users and agents want to interact with product data in the last year of building a universal product catalog. We believe this new endpoint is a cleaner design that will help you curate a richer shopping experience and better empower agents to find the perfect product. Our **new Search API endpoint lives at `/v1/search`**. Check out [the docs here](https://docs.trychannel3.com/api-reference/v1/search) and **upgrade to SDK version 3.0.0** to try it out today! Scroll to the bottom for migration instructions to handle the breaking changes that come with this major version bump. We'll keep `/v0/search` and `/v0/products` around for a few more weeks, but on Monday, April 13th they'll be officially removed. ## What we added **Search pagination** We updated search to return a richer `SearchResponse`, and we now return a `paging_token`. You can page search results up to 500 products, allowing you to build infinite scroll. **Product offers** Many products are sold by multiple retailers. We now show "offers" for every product, so you can decide where to send your users — the lowest-price, the highest commission, or the best-known retailer. **Collaborations** With our improved `brands` field, our products can now represent collaborations and multiple brands, like "MLB" and "Yankees." **Smart images** We look at every image with AI to give you more control over the product you display. Building a scrollable grid? Display `hero` images. Want a cozier, mood board feel? Try showing `lifestyle` images. Hate those annoying sizing charts that ruin the aesthetic of your image carousel? Filter out `size_chart`. Experimenting with virtual try-on? We built `on_model` exactly for this. ## What we removed (breaking changes) + migration instructions Spring is here in NYC, and we've done some spring cleaning on our API. On the `SearchRequest`, we've pared down our `SearchConfig` and `SearchFilters`. These fields had been deprecated and had no impact on search results. If you have any dangling references to deprecated fields, you'll have to remove them when you upgrade to 3.0.0. We've upgraded our search response from a `list[Product]` to a more expressive `SearchResponse`, which includes the `list[Product]` and a `paging_token`. We also cleaned up deprecated fields off of `Product`, so you have more control over the price, availability, brand, image, and product listings you display. **2-step migration instructions** (alternatively, paste this entire update into Claude Code / Cursor and let it cook): 1. Upgrade your Channel3 [Python or TypeScript SDK](https://docs.trychannel3.com/sdk) to version 3.0.0. * If you are using our API directly, point your new search to `/v1/search` and load product details from `/v1/products/`. 2. Audit any usages of the [`/search`](https://docs.trychannel3.com/api-reference/v1/search) endpoint and `Product` models in your app. * Update usages of `/search` to expect a `SearchResponse`. * Ensure you do not use any deprecated `/search` `SearchRequest` or `Product` response fields: * **Request:** `SearchFilters.exclude_product_ids`, `SearchConfig.redirect_mode` * **Response:** `Product.url`, `Product.price`, `Product.availability` (we now return *all* offers for this product, deduplicated across merchants — you or your users can choose the best price, whether that's the cheapest offer or the one that earns you the highest commission); `Product.image_url`, `Product.image_urls` (we use multimodal AI to understand every product image, so you can better curate your shopping experience); `Product.brand_id`, `Product.brand_name` (replaced with `Product.brands`, to support collaborations). 3. That's it! Ship it! Want to build something, but we don't support your use-case? Hit reply and let us know. And, as always, we love to hear what you're building! George # Internationalization Source: https://docs.trychannel3.com/changelog/announcements/update-2-internationalization *May 1, 2026 · API Update #2* Hey Developer, Channel3's API has officially gone global. Now, **you can filter by [country](https://docs.trychannel3.com/api-reference/v1/search), [language](https://docs.trychannel3.com/api-reference/v1/search), and [currency](https://docs.trychannel3.com/api-reference/v1/search) on your search requests** to only view products that fit your target audience. Many of these products are still monetizable with affiliate, and our team is hard at work partnering with more brands to get you higher commissions. (If there are any brands you'd like to see commissions for, let us know!) Upgrade to SDK version 3.2.0 to try it out now! Also, we're launching a [**Channel3 Discord**](https://discord.gg/6J6MjctXrH), a one-stop shop for all things Channel3 and agentic commerce: request features, report bugs, show off what you've built, or just chat with like-minded builders. Lastly, we've updated our pricing. Now, **every developer gets 1,000 free credits *every month***, resetting on the 1st of every month. Nothing else has changed, just more free credits for everyone. [Full pricing info is available on our docs](https://docs.trychannel3.com/pricing). ## Internationalization FAQ **Are these products monetizable?** Yes, many of these products will be monetizable and tracked with monetizable links. We've made deals with thousands of brands over the last few months, and we're adding new brands every day. **Which locales are available?** We're starting with the markets where we've seen the highest demand from developers: Europe, the UK, Canada, and Australia. You can view a full list of our [supported locales in our docs](https://docs.trychannel3.com/api-reference/v1/search). If you want us to expand to a market where we haven't yet, ask in the Discord (or reply to this email)! **Do you have every product from every locale now?** Not yet, but we hope our coverage is sufficient to replace your current product data solution. If there are gaps that are keeping your app/agent from being a truly great personal shopper, ask in the Discord! These markets are foreign to us (pun intended), and we'd appreciate any help in filling in gaps. **How do I add this to my app?** Locales can be provided in `SearchConfig` on every `/search` request, or you can instantiate your `Channel3` client with a specific locale when using your [preferred SDK](https://docs.trychannel3.com/sdk). The fastest way to get this set up is to install our Channel3 skills and ask your preferred coding agent to add support for your desired locale! Just run: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill channel3-api ``` And copy this into your coding assistant: ``` Use the channel3-ai Skill to only search for products from the UK ``` ## Discord We love the feedback, updates, and requests we've gotten in our inboxes. Instead of having these great ideas sequestered to our inboxes, we want to open up a forum for you all to share what you've built, talk through what you want to build, and socialize amongst other agentic-commerce builders! [Join the Discord at this link.](https://discord.gg/6J6MjctXrH) We have a few channels to start: * **#support** — something broken or misbehaving? Send a message in here! * **#showcase** — show off what you've built. Demo, ideate, offer feedback; get inspiration and beta testers. * **#community** — are your friends tired of you bringing up agentic commerce in conversation? Come hang out in #community! * **#feature-requests** — feeling like your app or agent is *almost* perfect, if *only* Channel3 could do X… send that here! Maybe it's on our radar, maybe it's way too hard, maybe we'll have something up by the end of the day 👀 * **#tips-and-help-forum** — as we see clever ways to build with Channel3 or common pitfalls, we'll broadcast them here. Want to build something, but we don't support your use-case? Drop a request in the Discord or hit reply and let us know. And, as always, we love to hear what you're building! George # Endpoints, Endpoints, Endpoints Source: https://docs.trychannel3.com/changelog/announcements/update-3-new-endpoints *May 9, 2026 · API Update #3* Hello Developer, We added [**/v1/similar**](https://docs.trychannel3.com/api-reference/v1/similar) so you can provide an essential component of any shopping experience: product recommendations. We've all seen a "you might also like this" or "frequently bought with" section of a product detail page. With **/v1/similar**, you can now add the same to your app's PDPs. This is especially powerful when coupled with Channel3's cross-merchant canonicalization, letting shoppers view product alternatives and multiple offers, all in one clean UI and two simple API calls. [**/v1/image-search**](https://docs.trychannel3.com/api-reference/v1/image-search) lets you run visual search across our entire product index. Our /search endpoint is still multimodal, but this is a helpful tool for personal shopping agents specifically. No context leak, just a simple, single-query endpoint. Pricing for both endpoints is 1 credit, like everything else. Pricing info is available [on our docs](https://docs.trychannel3.com/pricing). Lastly, we're kicking off a new section in these updates: a **Developer Spotlight**. In every email, we'll showcase an app or a demo that someone built on the Channel3 API. If you want feedback or just to show off what you've been working on, reply to this email or share it directly in the [#showcase channel of our Discord](https://discord.gg/6J6MjctXrH). ## Similar products **How it works:** `/similar` simply takes a product ID and returns a list of products that are visually similar to the provided product. The easiest way to get set up is to install our Channel3 SDK skills with: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill channel3-api ``` and ask your favorite coding agent to "`/channel3-api` add a product recommendation carousel below my main product view with the /similar endpoint." I asked Claude to do this with our dashboard (literally pasted the exact prompt above) and this is what it one-shotted: A product recommendation carousel one-shotted by Claude using the /similar endpoint The full product detail view with a similar-products carousel beneath it The buck doesn't stop with just a simple lookalike product, though. With our [robust `SearchFilters`](https://docs.trychannel3.com/api-reference/v1/similar), you can build some pretty amazing recommendation experiences. **Some ideas:** * Try excluding the product's returned gender with the gender filter to get a his-and-hers couple shopping experience. * For products in the `shirts-tops` category, set a category filter to `pants` and `shoes` for a "shop the look" response. [Full list of category slugs here.](https://docs.trychannel3.com/categories) * You can get results for the whole family by putting age filters for kids and toddlers. * Set a price filter based on the price of the original product for a deal-finder budget shopper. These are just a few ways we've played with this endpoint internally. If you come up with a clever use case (or for any of our other endpoints), share it in the [Discord #showcase channel](https://discord.gg/6J6MjctXrH)! ## Image search **How it works:** just provide an image URL or base64 and we'll return semantically relevant results based on the image. This is great for shopping someone else's fit recommendations, either from a TikTok or IRL. We'll still support multimodal search and pure image search on our /v1/search endpoint, but we've found agents especially perform better when provided this explicitly as a tool. **An idea:** I'd love to see someone use an image segmentation model + [/v1/image-search](https://docs.trychannel3.com/api-reference/v1/image-search) and build a shop-the-look feature. Take a full-body mirror selfie, grab "top," "bottoms," and "shoes," and run 3 parallel image searches! ## Developer Spotlight: Penny This week's developer spotlight features Penny, a ChatGPT app and browser extension deal finder! They both use the [Channel3 Price Tracking API](https://docs.trychannel3.com/api-reference/price-tracking/start-tracking) to display a time-to-buy graphic and [notify users when prices drop](https://docs.trychannel3.com/price-tracking-webhooks). On ChatGPT, you can install the app and invoke it with **@Penny**. I have a love-hate relationship with ChatGPT shopping, and Penny feels a world better. To me, ChatGPT shopping feels like generic ChatGPT answers with products slapped on top. Penny is a first-class shopping experience; a personal shopper agent embedded in your ChatGPT. Penny running as a shopping app inside ChatGPT Penny surfacing product recommendations in ChatGPT The browser extension is an agentic deal finder that walks the line between finding better deals and recommending alternatives when a deal doesn't exist. You can use it on any PDP either as a simple deal finder or a full-on personal assistant. The Penny browser extension acting as an agentic deal finder on a product page If you want your app spotlighted next, hit reply or check out the [#showcase Discord channel](https://discord.gg/6J6MjctXrH)! Want to build something, but we don't support your use-case? Drop a request in the [Discord](https://discord.gg/6J6MjctXrH) or hit reply and let us know. And, as always, we love to hear what you're building! The Channel3 Team # Brands, Categories, and Search Goes Fast Source: https://docs.trychannel3.com/changelog/announcements/update-4-brands-categories-fast *May 16, 2026 · API Update #4* Hey Developer, **We've overhauled our Brand and Category endpoints** to make it as easy as possible to search, refine, and repeat across this metadata. Our goal here at Channel3 is to help you power a shopping experience that surfaces the perfect product for your user. One key component in filtering keyword/semantic matches that don't match the user's intent is robust brand and category filtering. If you can constrain your search space to a set of products a user would be happy picking between, your odds of converting increase significantly. Category endpoints feature slug support, so you don't need to do a pesky category ID lookup. Internally, we ran migrations over all >100M products to both rebrand and recategorize them. More info below, and check out our [docs](https://docs.trychannel3.com/sdk) for a recommended workflow for an agent to best utilize these endpoints. You might think, with all of these additions to our API, that we might tank our search performance. Well, our second announcement today is that our endpoint timings are in, and after long days of infra grinding, we're excited to announce **sub-second P50 timing on /v1/search**. In our experimentation, this is definitely fast enough for agents to multiplex or chain requests while still blowing traditional product search workflows out of the water. ## Categories Our **Categories module** has 3 endpoints: `/v1/categories/search`, `/v1/categories`, and `/v1/categories/{slug}`. On our SDK, these are `client.categories.search`, `client.categories.list`, and `client.categories.retrieve`. Additionally, every `Product` returned [on our API has a list of `categories`](https://docs.trychannel3.com/api-reference/v1/search) (in practice, this is 1 category), which can be used for category narrowing. Categories are structured in a category tree, which you can [view in its entirety on our docs](https://docs.trychannel3.com/categories). The [`CategoryDetail` returned on our API](https://docs.trychannel3.com/api-reference/v1/category-detail) includes `CategoryRefs` that are sufficient for programmatically traversing the category tree to aid in your development or to provide end users with robust filters. The `CategoryDetail` also returns an [`attributes` field](https://docs.trychannel3.com/api-reference/v1/category-detail), which is a map of structured attributes, such as color, size, and material. It'd be pretty cool to filter deterministically on those attributes… more to come on that 👀. For agents, our [Channel3 product-discovery skill](https://github.com/channel3-ai/skills) already has explicit instructions to refine queries with relevant category searches. If you're building your own agentic harness, we recommend doing the same. **Why?** Categories are most important for filtering out semantic or keyword search noise. For example, if you search "canoe" on our API, you'll get some good canoeing options, but you might just as well get a landscape photo of a canoe. Internally, we do attempt to filter down to correct categories based on query intent. But if you're building a clothing shopping app, there's no reason not to constrain your outputs to categories you actually care about. ## Brands Our **Brands module** mirrors the shape of the Categories module: `/v1/brands/search`, `/v1/brands` for listing, and `/v1/brands/{brand_id}` for getting a brand detail. On the SDK, these are `client.brands.search`, `client.brands.list`, and `client.brands.retrieve`. `/v1/brands/search` is a simple keyword lookup by brand name, so you can map "nike" to its brand ID `MpZs` for use in `SearchFilters`. `/v1/brands` lists our relevant brands, which you can page through, so you can programmatically inspect our brand catalog. These are sorted by our internal brand scores, so if you use this in production, you'll only display relevant brands to users. In the future, we'll support filtering down this list, so you can programmatically curate a brand list for your users. All of these endpoints return the same `Brand` object, which includes a name, logo, and best commission rate. **Why?** If categories are useful for refining individual queries, curating a brand list for your platform ensures a high floor of product discovery. We strive to provide as many tools as possible for you to impose your taste and curate your shopping experience. Brands are an essential step in this process. ## Performance We benchmarked our `/v1/search` endpoint and are excited to announce a **sub-second P50 search time**. I won't bore you with all of the internals of our search, but in the span of (less than) a second, we: use language models, sanitize and understand your query, search (both keyword and semantic) our corpus of >100M products, and rerank these keyword and semantic results into one, pageable batch. Lightning-fast search has obvious benefits. The engagement falloff when a page takes a few or several seconds to load is real. However, we're most excited about making search as fast as possible so we can power the optimal **agentic search experience**. We built a simple agentic product search harness internally to compare how multiplexed queries (>1 `/v1/search` calls in a single agent call turn) compared to Gemini and ChatGPT. Building out this harness was less than a day of work. The results speak for themselves. Gemini is quick, admittedly, but it inundates you with 10 or more results. In the time it takes ChatGPT to spit back a single, verbose response, you can do multiple round trips with Channel3. A bar chart comparing product recommendation response times across Channel3, Gemini, and ChatGPT The difference between waiting 4.5 seconds and 12 seconds for a product recommendation is the difference between converting a sale and churning a user to a traditional shopping experience. Every week, we're adding more tools for you to refine your shopping experience and help surface the perfect products. Want to build something, but we don't support your use-case? Drop a request in the [Discord](https://discord.gg/6J6MjctXrH) or hit reply and let us know. And, as always, we love to hear what you're building! The Channel3 Team # SDK 3.3.1 — Color Filters, Cleaned Images & a CLI Source: https://docs.trychannel3.com/changelog/announcements/update-5-sdk-331 *May 26, 2026 · API Update #5* Hey Developer, **SDK version 3.3.1 has landed!** Before this release, we were already the best (and only) product API when it came to image understanding. We're doubling down on this, launching **search color filters** and **cleaned images**, all in a minor SDK release. These features are in addition to our in-depth image understanding, including classified image shot types and generated alt text, which is already live on our [API `ProductImage` type](https://docs.trychannel3.com/api-reference/v1/search). Also, **we launched a [CLI](https://docs.trychannel3.com/cli)!** If you're in the MCP-is-dead camp, install our CLI with `brew install channel3-ai/tap/channel3` and point your preferred agent at it. We also [**supercharged our Skills**](https://github.com/channel3-ai/skills) to leverage the CLI, and they really feel like magic when used with a personal agent. If you sleuth through our [**SDK changelog**](https://docs.trychannel3.com/changelog/overview), you'll notice we're not going to cover most of the changes here. We did a lot, and some of these changes deserve their own update (variants, structured attributes). ## Cleaned images **Cleaned images** are product images that have the following properties: 1. Square aspect ratio 2. Uniform, monochromatic background and are indicated on our API under the [`ProductImage` type with the flag `is_cleaned_image`](https://docs.trychannel3.com/api-reference/v1/search). Cleaned images are perfect for displaying a uniform, consistent product grid. Take [nordstrom.com](http://nordstrom.com) on our dashboard. Before cleaned images, this is what the product grid looks like. It's good, but those bad aspect ratios stick out like a sore thumb, and the on-model shots are glaring. A Nordstrom product grid before cleaned images, with mismatched aspect ratios and on-model shots Now, check out the page with cleaned images below. It's subtle, but significantly better: * Every image is the perfect aspect ratio * Those on-model shots have been replaced with cleaned, square lay-flat shots * The sunglasses, which were a narrow rectangle, have padding extending the image to a square The same Nordstrom product grid after cleaned images, with uniform square tiles In practice, this increases the conversion rate of both the products that previously displayed inconsistent on-model shots and the entire product grid. And, it looks super slick. **To adopt cleaned images, simply ask your coding agent to "prefer displaying images with `is_cleaned_image` in my product grid view."** ## Search color filters With **`SearchColorsFilter`**, you can provide a `hex` and a `percentage` filter to your `/v1/search` query, so every search result is a product matching the provided color profile. See the [docs for this filter here](https://docs.trychannel3.com/api-reference/v1/search). [Cosmos](https://www.cosmos.so) is an inspiration/moodboard/design app for creatives. One of its entry points is a simple color picker that kicks you off on a truly beautiful journey through a sea of themed images. We were inspired by this experience and decided we could build this for products. Thus, `SearchColorsFilter` was born. We've analyzed, extracted, and indexed color palettes for millions of products. I'll note that this field is in beta. We were so amazed by this tool, we wanted to share it with you ASAP! Two specific cautions: 1. Our coverage for indexed colors is good, but color space is large (16 million hex codes). At times, some irrelevant results will slip in. This is simply a consequence of the search space with the color filter applied being prohibitively small. 2. We might change the API shape — we've marked it as beta in the docs. Feedback is super helpful here, drop it in the [Discord](https://discord.gg/6J6MjctXrH). ## Search color filters + cleaned images We don't actually expose a way to do specifically this search (yet), but let me brag for a second. Look at these search results — perfectly uniform, totally adherent to the provided palette, and incredibly aesthetic. This is Pinterest or Cosmos for shopping. Someone, please build this!! A product grid combining color filters and cleaned images into a uniform, palette-matched result set ## CLI Full setup instructions can be found [on our docs](https://docs.trychannel3.com/cli), but getting running with the CLI is as easy as: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} brew install channel3-ai/tap/channel3 export CHANNEL3_API_KEY="your_api_key_here" ``` (add that environment variable to your `.zshrc` or `.bashrc` so you never have to set it again) CLIs are a great tool for pointing personal agents to if you don't want the context bloat of adding MCPs. The CLI is simply our API, in CLI form, fully equipped with search, price tracking, and all the auxiliary endpoints. The CLI is enhanced when paired with our Skills, which instruct your agent on *how* to use the CLI. [Channel3 Skills](https://docs.trychannel3.com/skills) can be installed in one line as well: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill product-discovery ``` Want to build something, but we don't support your use-case? Drop a request in the [Discord](https://discord.gg/6J6MjctXrH) or hit reply and let us know. And, as always, we love to hear what you're building! The Channel3 Team # Variants How-To Source: https://docs.trychannel3.com/changelog/announcements/update-6-variants-how-to *June 4, 2026 · API Update #6* Hey Developer, Today, we're taking a deep dive into **Variants, launched in SDK version 3.3.1**. TLDR: we figured out the hard stuff, so [you just have to copy/paste our docs into Cursor](https://docs.trychannel3.com/variants). If you run into any problems building with Variants, [drop a question in our Discord](https://discord.gg/6J6MjctXrH) or reply to this email. We also did some housekeeping on categories returned on our Products, and you'll have to clean up a deprecated field. Finally, I want to shout out some old work we've done for all of the new devs that have signed up in the last few months. We're refreshing products at an all-time high rate, and recording their price fluctuations every time we refresh. As a result, our [**price tracking suite of endpoints**](https://docs.trychannel3.com/api-reference/price-tracking/start-tracking) **is more powerful than ever**, featuring real-time webhook updates and rich price history components to power a time-to-buy UI. ## Variants We have an in-depth [guide to building with Variants on our docs](https://docs.trychannel3.com/variants). We also have a [Discord channel](https://discord.gg/6J6MjctXrH) designated for supporting your development, so if you hit any blockers, drop them in there! For now, we'll discuss some important high-level concepts: 1. The Variant data model 2. Selecting Variants 3. Building with Variants ### Variant data model The [full Variant schema can be viewed here](https://docs.trychannel3.com/variants#the-variant-data-model). There are two core design primitives when building with variants: the value and the availability status. The image below shows a breakdown of a fully-featured PDP with a variant selector, and how the components map to our returned data model. An annotated product detail page showing how VariantOption values and availability map to the rendered selector **Variant values** Variant values come in two shapes on our API: strings and images. The fields `thumbnail_url` and `label` determine the *content* of a variant selector button. If a `VariantOption` has a `thumbnail_url`, we recommend displaying a thumbnail icon button. Each of these buttons links to a different `product_id` as well. For configurations of the same product, we omit the `thumbnail_url`; these should just be displayed as a group of buttons or a dropdown selector displaying the `label`. These are the "Size" dimensions above. **Availability status** The values `available` and `exists` govern the *styling* of the variant selector button. There are 4 style states for variants: In Stock (`available=InStock`), Out of Stock (`available=OutOfStock`, `exists=true`), Not Available (`exists=false`), and Selected (`VariantOption == selected`). We have a deep dive into [the distinction between these fields in the docs](https://docs.trychannel3.com/variants#available-vs-exists), and if you use our resources, your coding agent will simply get the UX correct. The simple breakdown is: 1. `availability` determines the stock status of an option. This Puma shoe is InStock in size 5.5, OutOfStock in size 5 and 7. 2. `exists` indicates whether a configuration exists at all for a specific selection, independent of availability. In the example above, there are no known listings for the White Vapor shoe in size 6.5. If the user selects 6.5, the API will return the closest computed match, which would be size 6.5 in a different color. If you want to learn more about this, you can research "variant relaxation." Our current support for variant relaxation is V0; if you need more expressive variant selection, we can discuss in Discord. ### Selecting variants You load variant selections using our [ProductDetail endpoint](https://docs.trychannel3.com/api-reference/v1/product-detail). You can select/reference variants two ways: 1. When an option has a `product_id`, you can use this `product_id` directly. 2. For an `OptionValue` without a `product_id`, append the query parameter `option_{VariantOption.name}={OptionValue.label}` — for the example above: `option_size=5.5`. For example, to load the Hyperlink Blue shoe in size 6 from the current selection of the White shoe, you could equivalently call: ``` https://api.trychannel3.com/v1/products/XAtbsEk?option_Size=6 https://api.trychannel3.com/v1/products/SeAHhDT?option_Color=Hyperlink%20Blue-PUMA%20White&option_Size=6 ``` **Every time you load a variant, you should render the selection from the returned `selected` field and rerender the state of each variant selector option from the returned `options`.** ### Building with variants Variants are really complicated. For something so intuitive and seemingly so simple, there are many edge cases and pitfalls. **The best way to build with variants is to lean on our docs, our skills, and a coding agent.** 1. Copy our [Variants docs page](https://docs.trychannel3.com/variants) into your preferred coding agent. 2. Add our [Channel3 API skill](https://docs.trychannel3.com/skills) to your coding agent: `npx skills add channel3-ai/skills --skill channel3-api` 3. Ask your coding agent to "render variant selectors under my product search results and on my PDP view." **If you hit any snags, reach out in the [Discord](https://discord.gg/6J6MjctXrH) so others can learn alongside you.** One final important distinction to understand with variants is our /v1/search vs /v1/products/ support for them, [outlined in-depth here](https://docs.trychannel3.com/variants#search-vs-product-detail). **/v1/search** returns *all offers* for the product, across all of its variations. **/v1/products** filters offers down to only those present in the provided selection — e.g. `/v1/products/ABC123?option_size=5.5` returns only the listed offers for that selection. ## Category We recategorized all 100M+ products, so they have exactly one, high-quality category assignment. `categories` on the Product response is now deprecated. [Switch over to `category`](https://docs.trychannel3.com/product-model), which returns a rich `CategorySummary`, and integrates seamlessly with our [**Categories** module](https://docs.trychannel3.com/categories). Categories and strong categorization are especially important for refining search results. For example, if you're building a fashion app, you should *always* be filtering to the `apparel-accessories` category on all of your searches. This improvement to categorization also increased the quality of our search results, reducing the number of irrelevant or erroneous products returned. ## Price tracking + history One of the highest-converting shopping experiences you can build is a deal finder. With our [Price Tracking module](https://docs.trychannel3.com/api-reference/price-tracking/start-tracking), you can build a deal finder in a matter of minutes. We're now refreshing tens of millions of products a day and recording rich price history for all of them. For even higher-stakes deal hunting, we offer price tracking webhooks that notify you the moment a price drops. Use the `/v0/price-tracking/history` endpoint to load price history for a product and build your own time-to-buy UI. Or, if you don't want to build a somewhat complex UI component, you can wait a week… we might have some components cooking 👀. A price history chart rendered from the Channel3 price tracking history endpoint Want to build something, but we don't support your use-case? Drop a request in the [Discord](https://discord.gg/6J6MjctXrH) or hit reply and let us know. And, as always, we love to hear what you're building! The Channel3 Team # We Built Your Frontend for You Source: https://docs.trychannel3.com/changelog/announcements/update-7-component-library *June 23, 2026 · API Update #7* Hey Developer, Pause the API talk for a second — **we just launched [an open-source, fully-featured component library](https://github.com/channel3-ai/channel3-ui)!** We've built every core component of a shopping experience, from search to a rich product detail view. And, the best part is, if we don't have a component you want, you can contribute to the repo yourself. This ships as a Shadcn component library. All you have to run is: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx shadcn@latest add https://ui.trychannel3.com/r/all.json ``` and all of the components and hooks land in your repo as editable, customizable code. **We provide the building blocks, and you customize every aspect of them to fit your app.** We're simply handling the boilerplate — variant selectors, price history graphs, image carousels — so you don't have to. And, they look pretty clean. We'll dive into everything that shipped below. TLDR: in two lines of code, you can build a fully-featured shopping app. ## Resources + implementation [Our docs](https://docs.trychannel3.com/ui) have a comprehensive overview. The [README for the repo](https://github.com/channel3-ai/channel3-ui) is well-maintained. And, [the landing page on our website](https://trychannel3.com/developers/ui) shows how easy they are to build with. They even power our interactive chat demo: [trychannel3.com/demos](https://trychannel3.com/demos). If you hit any snags, [hop into the Discord](https://discord.gg/6J6MjctXrH) and ask. You can also contribute back to the library; it's open-source. To build with the component library, simply install it into your app with: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx shadcn@latest add https://ui.trychannel3.com/r/all.json ``` and install the Channel3 skill: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill channel3-api ``` then ask your agent to build with the Channel3 UI components. ## Blocks Rather than me telling you how great these are, let me just show you. The top-level components of the library are `product-search` and `product-details`. `product-search` is a single component for a fully-featured search, including filters and infinitely scrollable results. I love this graphic because it also visualizes how powerful our search has become. Notice the rich brand filters (with logos), and the rust color filter looks fantastic here. The product-search component rendering a full search experience with brand and color filters When a user wants to dive into a search result, use `product-details`. Again, in one component, you get a PDP with variant selection, multiple offers, and similar product search, all built in. The product-details component rendering a full product detail page ## Components The above two screenshots basically constitute an end-to-end shopping app, but they're certainly not exhaustive for our component catalog. The [full list is here](https://github.com/channel3-ai/channel3-ui#catalog), but some notable shoutouts: **Price history and tracking components:** there's a reason these are a staple of the best deal-finding experiences. Almost as important as surfacing *what* to buy is informing your user *when* to buy. Enhance this with our `price-tracking` webhooks to get notified the second a price drops. A price history and tracking component from the Channel3 UI library **Similar product carousels:** a PDP almost looks empty without these. If a user is on a PDP, they're probably interested in the product itself or something like it. Capitalize on the user's intent with `/similar`. A similar-products carousel component from the Channel3 UI library We're also adding more components every week, so keep your eyes on the channel3-ui repo. If you build a great component using our API, we'd love for you to contribute it back to the library. ## Hooks A sneakily challenging part of building an e-commerce app is the UX logic. Variant selection is a nuisance to get perfectly correct. Infinite scroll is another bit of custom logic that you really want to "just work." And every async call is another loading state you have to worry about. So, we solved that in the component library as well. For any core e-commerce business logic, we have a hook: * Don't try to figure out variant relaxation or availability vs. existence states. Use `use-variant-selection`. * You *could* build search filter plumbing and request orchestration yourself. Or, you could use `use-product-search`. * Supercharge your filters with `use-async-options`, so category and brand filters are loaded snappily. Want to build something, but we don't support your use-case? Drop a request in the [Discord](https://discord.gg/6J6MjctXrH) or hit reply and let us know. And, as always, we love to hear what you're building! The Channel3 Team # Changelog Source: https://docs.trychannel3.com/changelog/overview Select a version from the sidebar to view release notes. # v2.0.0 — September 2025 Source: https://docs.trychannel3.com/changelog/v2-0-0 * **Breaking:** New auto-generated, type-safe Python SDK with full async/sync client support * Comprehensive type definitions for all API resources # v2.1.0 — September 2025 Source: https://docs.trychannel3.com/changelog/v2-1-0 * Added `exclude_product_ids` filter to [`POST /v1/search`](/api-reference/v1/search) to exclude specific products from results # v2.10.0 — January 2026 Source: https://docs.trychannel3.com/changelog/v2-10-0 * Simplified [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) documentation — removed variant dimension query parameter details * Fixed file handling in PATCH requests # v2.11.0 — January 2026 Source: https://docs.trychannel3.com/changelog/v2-11-0 * Removed deprecated `variants` description from `Product` and `ProductDetail` response models # v2.12.0 — January 2026 Source: https://docs.trychannel3.com/changelog/v2-12-0 * Added [`GET /v0/list-brands`](/api-reference/v1/list-brands) endpoint with paginated brand listing * Renamed `brands.list` to `brands.find` for the name-based brand search * Updated [`POST /v0/enrich`](/api-reference/v1/lookup-product) to support real-time fallback retrieval when a product isn't in the database * Added support for binary request streaming # v2.13.0 — March 2026 Source: https://docs.trychannel3.com/changelog/v2-13-0 * Upgraded [`POST /v1/search`](/api-reference/v1/search) to **search service v2** with improved relevance and performance * Added `brands` field (with `id` and `name`) to `Product` and `ProductDetail` response models * Added `exclude_category_ids` filter to [`POST /v1/search`](/api-reference/v1/search) # v2.2.0 — September 2025 Source: https://docs.trychannel3.com/changelog/v2-2-0 * Removed `environment` parameter from client — use `base_url` or the `CHANNEL3_BASE_URL` env var instead # v2.3.0 — October 2025 Source: https://docs.trychannel3.com/changelog/v2-3-0 * Expanded [`POST /v0/enrich`](/api-reference/v1/lookup-product) response to include `brand_id`, `brand_name`, `gender`, `image_urls`, `key_features`, `materials`, and `variants` * Added `redirect_mode` config option to [`POST /v1/search`](/api-reference/v1/search) — choose between `price`, `commission`, or `brand` redirect behavior # v2.4.0 — November 2025 Source: https://docs.trychannel3.com/changelog/v2-4-0 * Added `category_ids`, `condition`, and `website_ids` filters to [`POST /v1/search`](/api-reference/v1/search) * Added `monetizable_only` config option to [`POST /v1/search`](/api-reference/v1/search) to return only commissionable products * Added `categories` field to search and product response models * Removed `brands.retrieve` endpoint — use [`GET /v1/brands`](/api-reference/v1/list-brands) with the `query` parameter instead * Fixed streams not being properly closed without full consumption # v2.5.0 — November 2025 Source: https://docs.trychannel3.com/changelog/v2-5-0 * Added compatibility with **Python 3.14** * Fixed `model_dump` and `model_dump_json` signatures for **Pydantic v1** * Dropped support for Python 3.8 # v2.6.0 — December 2025 Source: https://docs.trychannel3.com/changelog/v2-6-0 * Unified product response type — [`POST /v0/enrich`](/api-reference/v1/lookup-product) now returns `ProductDetail` instead of a separate `EnrichEnrichURLResponse` * Removed deprecated `Product` type from exports # v2.8.0 — December 2025 Source: https://docs.trychannel3.com/changelog/v2-8-0 * Added price tracking endpoints: [`POST /v0/price-tracking/start`](/api-reference/price-tracking/start-tracking), [`POST /v0/price-tracking/stop`](/api-reference/price-tracking/stop-tracking), [`GET /v0/price-tracking/history/{canonical_product_id}`](/api-reference/price-tracking/get-price-history), and [`GET /v0/price-tracking/subscriptions`](/api-reference/price-tracking/list-subscriptions) # v2.9.0 — December 2025 Source: https://docs.trychannel3.com/changelog/v2-9-0 * Faster SDK import times # v3.0.0 — March 2026 Source: https://docs.trychannel3.com/changelog/v3-0-0 * **Breaking:** Migrated to the v1 Product API — [`POST /v1/search`](/api-reference/v1/search), [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail), [`POST /v1/lookup`](/api-reference/v1/lookup-product) * Added [`GET /v1/brands/{brand_id}`](/api-reference/v1/brand-detail) for fetching individual brand details * Added pagination support for [`POST /v1/search`](/api-reference/v1/search) results * Removed deprecated fields from product and search responses # v3.1.0 — April 2026 Source: https://docs.trychannel3.com/changelog/v3-1-0 * Fixed file uploads sending duplicate data in multipart requests on [`POST /v1/image-search`](/api-reference/v1/image-search) * Fixed hardcoded query params being overwritten when merging with user-provided params * Fixed endpoint path parameter sanitization # v3.2.0 — April 2026 Source: https://docs.trychannel3.com/changelog/v3-2-0 * Added [`POST /v1/image-search`](/api-reference/v1/image-search) endpoint for visual product discovery * Added [`POST /v1/similar`](/api-reference/v1/similar-products) endpoint for finding related products * Added categories endpoints: [`GET /v1/categories`](/api-reference/v1/list-categories), [`GET /v1/categories/search`](/api-reference/v1/search-categories), and [`GET /v1/categories/{slug}`](/api-reference/v1/category-detail) * Added [`GET /v1/brands/search`](/api-reference/v1/search-brands) endpoint * Added `language`, `country`, and `currency` parameters to the client and [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) * Removed `unisex` from gender filter options on [`POST /v1/search`](/api-reference/v1/search) * Removed `min_price_floor` from search filter price parameters * Clarified that `category` search filter on [`POST /v1/search`](/api-reference/v1/search) accepts slugs # v3.3.0 — May 2026 Source: https://docs.trychannel3.com/changelog/v3-3-0 * Added `variants` to [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) and [`POST /v1/lookup`](/api-reference/v1/lookup-product) responses — includes `options` (e.g. Color, Size) with availability per value, and `selected` for current selections * Added `structured_attributes` to [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) and [`POST /v1/lookup`](/api-reference/v1/lookup-product) responses — extracted product attributes keyed by handle (e.g. `color`, `material`) * Added `attributes` filter to [`POST /v1/search`](/api-reference/v1/search) — filter by attribute key/value pairs (e.g. `{"color": ["blue", "red"]}`) * Added `colors` filter to [`POST /v1/search`](/api-reference/v1/search) — filter products by color palette using hex values **(Beta)** * Added `age` filter to [`POST /v1/search`](/api-reference/v1/search) — filter by age group (`newborn`, `infant`, `toddler`, `kids`, `adult`) * Added support for Greece (`GR`) and Romania (`RO`) locales, `RON` currency, and `el`/`ro` languages * Updated `Category.attributes` description to clarify usability as keys in search filters # v3.3.1 — May 2026 Source: https://docs.trychannel3.com/changelog/v3-3-1 * Added `category` field to [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) and [`POST /v1/lookup`](/api-reference/v1/lookup-product) responses — includes the product's category slug and title * Added `is_cleaned_image` flag to product image models — `true` when the image has a square aspect ratio and uniform background, ideal for product grids # v3.4.0 — July 2026 Source: https://docs.trychannel3.com/changelog/v3-4-0 * Added [`POST /v1/browse`](/api-reference/v1/browse) — list and page through products for a brand, website, or category, ideal for static grid views. Access is restricted; [contact us](mailto:support@trychannel3.com) to enable it * Added `mode` to [`POST /v1/search`](/api-reference/v1/search) config — choose `default` (lexical + semantic, recommended), `keyword` (lexical only, lowest latency), or `agentic` (LLM-planned sub-searches for complex queries). Deprecated `keyword_search_only`; `true` is equivalent to `mode=keyword` * Added `sale` filter to [`POST /v1/search`](/api-reference/v1/search) — pass `on_sale` to only return products with at least one offer priced below its compare-at price * Updated `condition` filter to require at least one offer matching the requested condition, locale, and any price filter — offers without condition data are indexed as `new` * Added `condition` to product offer models — `new`, `refurbished`, or `used`, or `null` when unknown * Added `country` parameter to brand and website endpoints — commission rates now reflect the requested country (default `US`) # v3.5.0 — July 2026 Source: https://docs.trychannel3.com/changelog/v3-5-0 * Added `dimensions` filter to [`POST /v1/search`](/api-reference/v1/search) — match products by physical size (`length`, `width`, `height`) and `weight`. Each field takes an optional `min`/`max` (inclusive) and a required `unit`. See [Dimension Filters](/guides/dimension-filters) * Added `dimensions` to product offer models — `length`, `width`, `height`, and `weight`, each returned as a `number` plus its `unit`; individual members are `null` when unknown * Added `length_unit` and `weight_unit` controls for the unit dimensions come back in — set them per request (search `config`, or [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) query params) or as a client default via the SDK constructor or the `CHANNEL3_LENGTH_UNIT` / `CHANNEL3_WEIGHT_UNIT` environment variables. Precedence is: a dimension filter's own unit, then the per-request value, then the client default; when none is set, the merchant's stated unit is returned * Length units are `mm`, `cm`, `m`, `in`, `ft`; weight units are `mg`, `g`, `kg`, `oz`, `lb`. A merchant-stated value whose unit falls outside these sets is omitted rather than converted # v4.0.0 — August 2026 Source: https://docs.trychannel3.com/changelog/v4-0-0 The API is unchanged in this release. Every change below is in the SDKs, which are now generated by [Fern](https://buildwithfern.com). `@channel3/sdk@4.0.0` is released. The Python SDK is in prerelease — `pip install channel3-sdk` still resolves to 3.5.0; reach the preview with `pip install channel3-sdk==4.0.0b2`. **Newly available in the SDKs** * Conversations — [`POST /v1/conversations`](/api-reference/v1/create-turn) for a buffered turn or an SSE event stream, and [`GET /v1/conversations/{conversation_id}`](/api-reference/v1/get-conversation) to page a thread's history. See [Conversations](/conversations) * Client tokens — [`POST /v1/conversations/client_tokens`](/api-reference/v1/create-client-token) and [revoke](/api-reference/v1/revoke-client-token), for browser-safe tokens scoped to one conversation. See [Client tokens](/conversations/client-tokens) * Reporting — [clicks](/api-reference/v1/reporting-clicks) and [transactions](/api-reference/v1/reporting-transactions) * [`POST /v1/browse`](/api-reference/v1/browse) and `POST /v1/monetize` * Typed variant selection on [`GET /v1/products/{product_id}`](/api-reference/v1/product-detail) — pass `selected_options` as a map of dimension name to value label **Breaking: removed methods.** The methods deprecated in 3.5.x are gone. Use `products.search` instead of `search.perform`, `brands.search` instead of `brands.find`, `products.lookup` instead of `enrich.enrichURL` / `enrich_url`, `websites.retrieve` instead of `websites.find`, and `retrieveHistory` / `retrieve_history` instead of `getHistory` / `get_history`. The `search` and `enrich` resources no longer exist **Breaking (TypeScript)** * The default export is gone — `import { Channel3 } from "@channel3/sdk"` * `ProductDetail` is now `Product`, and the variant types are top-level: `VariantOption`, `OptionValue`, `SelectedOption`. `PriceHistory` is now `PriceHistoryResponse`, kept as a deprecated alias until 5.0 * Pagination returns an iterable you must `await` first: `const page = await client.products.search({ query }); for await (const product of page) { ... }` * Client options renamed — `baseURL` → `baseUrl`, `timeout` → `timeoutInSeconds`, `defaultHeaders` → `headers`, `defaultQuery` → `queryParams`, `logLevel` / `logger` → `logging`. `fetchOptions` has no equivalent **Breaking (Python)** * The base exception is `ApiError`, not `Channel3Error`. `AuthenticationError` is now `UnauthorizedError`; `status_code` is still on the exception * The four per-call options (`extra_headers`, `extra_query`, `extra_body`, `timeout`) are replaced by a single `request_options` * Renamed models: `ProductDetail` → `Product`, `PriceHistory` → `PriceHistoryResponse`, `Subscription` → `SubscriptionResponse`, `VariantsOption` → `VariantOption`, `VariantsOptionValue` → `OptionValue`, `VariantsSelected` → `SelectedOption`. **No compatibility aliases ship** — rename at the import site * `search_by_image(base64_image=...)` is now `base64image` — a generator casing limitation, not a deliberate rename. The wire field and the TypeScript spelling are unchanged * `NOT_GIVEN`, `NotGiven`, `Omit`, `Stream`, `AsyncStream` and `DefaultHttpxClient` are gone. Omitting an optional argument behaves as before **Unchanged.** Field names keep their snake\_case spelling in both languages, client construction is the same, and the `CHANNEL3_*` locale environment variables still work. # v4.1.0 — August 2026 Source: https://docs.trychannel3.com/changelog/v4-1-0 * Added [Collections](/guides/collections) — save a catalog scope (brands, websites, categories, and/or product ids) and pass `config.collection_id` on search, browse, image search, find-products-in-image, and conversations. Collection CRUD is free. # Checkout Source: https://docs.trychannel3.com/checkout Checkout is **coming soon**. Today, Channel3 helps you discover products and earn commissions when shoppers buy through retailer links. Checkout will let you complete a purchase, too — so your app or agent can go from search to paid order without sending the shopper to a retailer site. We'll share endpoint details and SDK support here when it ships. Want early access? Email us. # CLI Source: https://docs.trychannel3.com/cli The Channel3 CLI calls the Channel3 API from a terminal or script. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} npm install -g @channel3/cli ``` To update an existing installation: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} npm update -g @channel3/cli ``` **Upgrading from 0.4.x?** Versions up to `0.4.1` were installed with Homebrew. Remove that build first — it is no longer updated: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} brew uninstall --cask channel3 npm install -g @channel3/cli ``` The binary name and `CHANNEL3_API_KEY` are unchanged, but some flags are not. Most importantly, **`--query` is now `--query-param`** for search terms. See [Migrating to 0.5.0](/cli/migrating). ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} export CHANNEL3_API_KEY="your_api_key_here" ``` Set optional locale defaults: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} export CHANNEL3_LANGUAGE="en" export CHANNEL3_COUNTRY="US" export CHANNEL3_CURRENCY="USD" ``` Add the `export` lines to `~/.zshrc` (or `~/.bashrc`), then `source ~/.zshrc`. You can also pass `--api-key` to an individual command. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "organic cotton t-shirt" ``` ## Explore the CLI Keyword search, filters, pagination, and locale. Search from a public image URL or local image file. Find related catalog products from a product ID. Resolve a retailer page to a canonical product. Browse catalog entities and use them in product filters. Select formats or extract fields with JMESPath. The [full CLI reference](/cli/usage) keeps every command in one place. # Brands Source: https://docs.trychannel3.com/cli/brands Search for a brand: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 brands search --query-param "nike" ``` Browse the catalog: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 brands list --limit 20 ``` Use a returned ID to restrict product search: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "running shoes" \ --filters.brand_ids '["brand_id_here"]' ``` A brand is the maker or label, not the retailer selling the product. See [Brand](/concepts/brand). # Categories Source: https://docs.trychannel3.com/cli/categories Find a category: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 categories search --query-param "sofas" ``` Inspect category detail: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 categories retrieve --slug sofas ``` Pass a category slug to product search: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "sectional" \ --filters.category "sofas" ``` Category detail also exposes structured attributes you can use for more precise searches. See [Category](/concepts/category). # Image Search Source: https://docs.trychannel3.com/cli/image-search Search from a public image URL: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image \ --image-url "https://example.com/shoe.jpg" ``` Search from a local file. Encode it yourself and pass the result: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image --base64-image "$(base64 -i shoe.jpg)" ``` On Linux, `base64` reads from a path without `-i`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image --base64-image "$(base64 -w0 shoe.jpg)" ``` Versions up to `0.4.1` accepted `--base64-image @shoe.jpg` and encoded the file for you. That `@path` syntax is gone — the literal string `@shoe.jpg` would be sent as the image data. Use the command substitution above. You can also combine a visual signal with text: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "black running shoes" \ --image-url "https://example.com/shoe.jpg" ``` See [Image search](/guides/image-search) for behavior and supported inputs. # Look Up a Product URL Source: https://docs.trychannel3.com/cli/lookup Pass any supported retailer product URL: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products lookup \ --url "https://www.nike.com/t/air-force-1-07-mens-shoes-5QFp5Z/CW2288-111" ``` The response is a canonical product and can contain offers from multiple retailers. Price, availability, and buy URLs live under `offers[]`; see [Offer](/concepts/offer). To fetch a product by its returned ID: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products retrieve --product-id 2yh8WH5 ``` See [URL lookup](/guides/url-lookup) for the API behavior. # Migrating to 0.5.0 Source: https://docs.trychannel3.com/cli/migrating CLI `0.5.0` replaces the binary that shipped through `0.4.1`. The command shape (`channel3 `), the binary name, and `CHANNEL3_API_KEY` are unchanged — but installation moved to npm and several flags changed. ## 1. Reinstall ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} brew uninstall --cask channel3 npm install -g @channel3/cli ``` Confirm you're on the new one: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 --version # 0.5.0 which channel3 # should be your npm prefix, not /opt/homebrew/bin ``` Homebrew no longer receives updates, and `go install` is gone. ## 2. Rename `--query` — the one that bites `--query` still exists, but it means something entirely different: it is now a [JMESPath](https://jmespath.org) projection applied to the response. The search term moved to `--query-param`. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} # before channel3 products search --query "running shoes" # after channel3 products search --query-param "running shoes" ``` Left unchanged, a multi-word search fails with a JMESPath parse error. A single-word search is worse: `--query shoes` is *valid* JMESPath, so no search term is sent and you get back `null` or a 422. ## 3. Flag changes | Up to 0.4.1 | 0.5.0 | Notes | | ------------------------------------- | ---------------------------------------- | ------------------------------------------------- | | `--query` | `--query-param` | The search term. See above. | | `--transform` (GJSON) | `--query` (JMESPath) | `.#.` becomes `[]`; keys need explicit names | | `--max-items N` | `--limit N` | See pagination below | | `--format pretty` | `--format table` | | | `--format auto` | *(default)* | Table at a terminal, JSON when piped | | `--format explore` | *(none)* | The interactive browser has no replacement | | `--format-error`, `--transform-error` | *(none)* | Errors can't be formatted or projected separately | | `--base64-image @file.jpg` | `--base64-image "$(base64 -i file.jpg)"` | `@path` is no longer expanded | `--format jsonl`, `--format raw`, `--debug`, `--base-url` and `--api-key` are unchanged. New: `--dry-run`, `--page-all`, `--quiet`, `--schema`, `--spec`, `csv` and `http` output formats, `auth` for keyring-stored credentials, and `completion` for shell completions. ## 4. Pagination inverted Up to `0.4.1` the CLI auto-paginated, and `--max-items` existed to rein it in. `0.5.0` returns a **single page** unless you ask for more: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "socks" --limit 20 # one page channel3 products search --query-param "socks" --page-all --page-limit 3 ``` If your scripts passed `--max-items` defensively, you can usually just drop it and set `--limit`. `--page-all` emits one JSON object **per page**, each containing a `products` array — not one object per product. To iterate products across pages, unwrap them yourself: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "socks" --page-all --format jsonl \ | jq -c '.products[]' ``` Combining `--page-all` with `--query` is not recommended: the projection is applied to each page wrapper before the output is flattened, which makes expressions behave differently than they do on a single-page call. Project with `--query` on single-page calls, and use `jq` when paginating. ## 5. Translate your projections `--transform` used GJSON; `--query` uses JMESPath. | GJSON | JMESPath | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | `products.#.title` | `products[].title` | | `products.0.offers.0.price` | `products[0].offers[0].price` | | `brands.#.{id,name}` | `brands[].{id: id, name: name}` | | `products.#.{id,title,offers:offers.#.{domain,price:price.price}}` | `products[].{id: id, title: title, offers: offers[].{domain: domain, price: price.price}}` | Two rules cover most cases: `.#.` becomes `[]`, and JMESPath has no shorthand for `{id}` — every key needs `name: path`. Note that projections now start at the response wrapper (`products[]`, `brands[]`, `categories[]`), because `--query` applies to the whole response before `--format jsonl` flattens it. ## 6. Commands that moved | Removed | Use instead | | ---------------------------- | -------------------------- | | `channel3 search perform` | `channel3 products search` | | `channel3 enrich enrich-url` | `channel3 products lookup` | | `channel3 brands find` | `channel3 brands search` | New in 0.5.0: `products browse`, `products monetize`, `reporting list-clicks`, `reporting list-transactions`, and `conversations` with its `client-tokens` sub-resource. ## Using the CLI from an agent If you installed the Channel3 skills, update them too — they call the CLI directly and the old flags fail at runtime: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill product-discovery ``` # Format Output Source: https://docs.trychannel3.com/cli/output Output is a table when you're at a terminal and JSON when piped, so `channel3 ... | jq` works without a flag. Choose explicitly with `--format`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "lamp" --format table ``` `--format` accepts `json`, `table`, `yaml`, `csv`, `jsonl`, `raw`, and `http`. `jsonl` emits one compact JSON value per line and flattens arrays; `raw` emits the server's bytes unmodified; `http` prints the full response with status line and headers, like `curl -i`. ## Extracting fields `--query` takes a [JMESPath](https://jmespath.org) expression applied to the response before formatting. Extract only product titles: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "running shoes" \ --query "products[].title" ``` Extract the first offer price: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "merino sweater" \ --limit 1 \ --query "products[0].offers[0].price" ``` Compare retailer offers: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "dyson vacuum" \ --limit 1 \ --query "products[0].offers[].{domain: domain, price: price.price}" ``` `--query` is the projection flag, **not** the search term. The search term is `--query-param`. In CLI versions up to `0.4.1`, `--query` meant the search term, so old scripts must be updated or they will fail. ## Migrating from `--transform` Versions up to `0.4.1` used `--transform` with [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). That flag is gone; `--query` replaces it, using JMESPath syntax: | GJSON (`--transform`) | JMESPath (`--query`) | | ------------------------------------------------- | ----------------------------------------------------------- | | `products.#.title` | `products[].title` | | `products.0.offers.0.price` | `products[0].offers[0].price` | | `products.0.offers.#.{domain:domain,price:price}` | `products[0].offers[].{domain: domain, price: price.price}` | The rule of thumb: `.#.` becomes `[]`, and a numeric index like `.0.` becomes `[0]`. `--format-error` and `--transform-error`, which formatted and projected error output separately, have no equivalent. ## Pagination `--page-all` follows pagination and streams every result as NDJSON, up to `--page-limit` pages (default 10): ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "socks" --page-all --page-limit 3 ``` # Search Products Source: https://docs.trychannel3.com/cli/search Use `channel3 products search` for keyword search. Add filters as JSON, or target an individual field with `--filters.`. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "organic cotton t-shirt" ``` ## Filters ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "running shoes" \ --filters '{ "price": { "min_price": 50, "max_price": 150 }, "availability": ["InStock"], "gender": "male" }' ``` Or specify individual fields: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "sweater" \ --filters.price '{"min_price":50,"max_price":200}' \ --filters.availability '["InStock"]' ``` Use [brand](/cli/brands), [website](/cli/websites), and [category](/cli/categories) IDs or slugs with the corresponding filter fields. ## Pagination ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "running shoes" --limit 20 ``` Pass the response's page token back with the pagination flag shown by `channel3 products search --help`. ## Locale ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "trainers" \ --config.country=GB \ --config.language=en \ --config.currency=GBP ``` For request semantics and filter behavior, see [What you request](/guides/request-overview). # Find Similar Products Source: https://docs.trychannel3.com/cli/similar-products Use a product ID from search, lookup, or product detail: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products find-similar --product-id 2yh8WH5 --limit 10 ``` Narrow recommendations with filters: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products find-similar \ --product-id 2yh8WH5 \ --limit 10 \ --filters '{"price":{"max_price":200},"gender":"female"}' ``` See [Similar products](/guides/similar-products) for use cases and response details. # Usage Source: https://docs.trychannel3.com/cli/usage Commands follow a resource-based structure: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 [resource] [flags...] ``` Pass `--help` on any command for full flags. Price and buy links live on `offers[]` — see [Offer](/concepts/offer). ## Search ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "organic cotton t-shirt" ``` Limit results: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search --query-param "running shoes" --limit 5 ``` ## Filters Pass filters as JSON, or nest fields with `--filters.`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "running shoes" \ --filters '{ "price": { "min_price": 50, "max_price": 150 }, "availability": ["InStock"], "gender": "male" }' ``` ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "sweater" \ --filters.price '{"min_price":50,"max_price":200}' \ --filters.availability '["InStock"]' ``` Brand or website filters need IDs (or use domains for websites — see [website search](/guides/website-search)): ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} # Resolve a brand first channel3 brands search --query-param "nike" # Then filter products channel3 products search \ --query-param "running shoes" \ --filters.brand_ids '["brand_id_here"]' ``` ## Image search From a public URL: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image \ --image-url "https://example.com/shoe.jpg" ``` From a local file (`@` sends the file; binary is base64-encoded automatically): ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image --base64-image "$(base64 -i shoe.jpg)" ``` You can also pass `--image-url` or `--base64-image` on `products search` alongside `--query`. ## Lookup a store URL ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products lookup \ --url "https://www.nike.com/t/air-force-1-07-mens-shoes-5QFp5Z/CW2288-111" ``` Returns the canonical product with every merchant offer. ## Product detail and similar ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products retrieve --product-id 2yh8WH5 ``` ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products find-similar --product-id 2yh8WH5 --limit 10 ``` ## Brands, categories, websites ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 brands search --query-param "nike" channel3 brands find --query-param "Nike" channel3 brands list --limit 20 ``` ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 categories search --query-param "sofas" channel3 categories retrieve --slug sofas ``` ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 websites retrieve --query-param "https://www.bestbuy.com" ``` ## Locale Set defaults via env (`CHANNEL3_COUNTRY`, etc.) or per call: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "trainers" \ --config.country=GB \ --config.language=en \ --config.currency=GBP ``` ## Output formats ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} # Table output (good default for humans) channel3 products search --query-param "lamp" --format table # Titles only (JMESPath) channel3 products search \ --query-param "running shoes" \ --query "products[].title" # First product's first offer price channel3 products search \ --query-param "merino sweater" \ --limit 1 \ --query "products[0].offers[0].price" # Domains + prices for comparison channel3 products search \ --query-param "dyson vacuum" \ --limit 1 \ --query "products[0].offers[].{domain: domain, price: price.price}" ``` `--format` accepts `json`, `table`, `yaml`, `csv`, `jsonl`, `raw`, and `http`. `--query` uses [JMESPath](https://jmespath.org) — note it is the projection flag, while the search term is `--query-param`. See [Format output](/cli/output). ## Files Prefix a path with `@` to send a file as an argument: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search-by-image --base64-image "$(base64 -i shoe.jpg)" ``` Escape a literal `@` with `\@` if you need the character in a string. ## Full reference | Resource | Common commands | | ---------------- | ----------------------------------------------------------------- | | `products` | `search`, `search-by-image`, `lookup`, `retrieve`, `find-similar` | | `brands` | `search`, `find`, `list`, `retrieve` | | `categories` | `search`, `list`, `retrieve` | | `websites` | `retrieve` | | `price-tracking` | see `--help` | ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 --help channel3 products --help ``` API shapes: [What you request](/guides/request-overview) · [API reference](/api-reference/v1/search) # Websites Source: https://docs.trychannel3.com/cli/websites Look up a retailer by domain or URL: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 websites retrieve --query-param "https://www.bestbuy.com" ``` Use website IDs to restrict product search: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} channel3 products search \ --query-param "coffee maker" \ --filters.website_ids '["walmart","target"]' ``` Website filters restrict retailer offers; they do not filter the product's brand. See [Website](/concepts/website). # Commissions Source: https://docs.trychannel3.com/commissions We've partnered with thousands of brands who pay commissions on sales you drive. ## How it works 1. Make requests with your Channel3 API key. 2. Surface products. 3. When a shopper clicks on a link, we track it. 4. If they make a purchase, we track that too. 5. The sale shows up in your dashboard. 6. After the retailer's return window closes, you get paid. (Securely, via Stripe.) Commission rates vary by product, brand, retailer, and shopper geography. Find current rates in the [Brands](https://trychannel3.com/dashboard/brands) and [Websites](https://trychannel3.com/dashboard/websites) sections of the dashboard. Search responses also include an offer's `max_commission_rate`. Pull clicks and transactions programmatically with the [Reporting API](/guides/reporting). ## Your share We take a 50% cut of commissions paid out on our platform. This rate will reduce as you drive more sales. Looking for higher commissions? Contact us. ## Featured retailers Channel3 includes offers from thousands of retailers, such as:
The Home Depot
Best Buy
Mytheresa
Anthropologie
The RealReal
thredUP
Staples
Saks Fifth Avenue
Retailer availability and commission eligibility vary by product and market. A retailer being listed here does not mean every offer is commissionable. Use the rate shown on each offer to determine eligibility. ## Changes and adjustments Retailers can adjust or cancel commissions because of returns, order cancellations, suspected fraud, attribution to another channel, or network-quality concerns. Complete identity verification in the dashboard so earned commissions are not withheld. # Client Tokens Source: https://docs.trychannel3.com/conversations/client-tokens A client token lets the **browser** call Channel3 without your API key. It looks like `c3_ct_…`. It can only run turns and read the one conversation it is bound to. The API key that minted it pays for those turns. Mint on your server. Never mint from the browser. Send the token as `Authorization: Bearer c3_ct_…` — see the [quickstart](/conversations/quickstart). ## Mint Default lifetime is 30 minutes (`ttl_seconds` from 60 to 7200). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const created = await server.conversations.clientTokens.create(); // created.token — c3_ct_… // created.token_id // created.expires_at ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} created = server.conversations.client_tokens.create() # created.token, created.token_id, created.expires_at ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/conversations/client_tokens \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` With **no** `conversation_id`, the token's first turn creates the conversation and the token is bound to it. That is the [quickstart](/conversations/quickstart) path. ## Pin a token to an existing conversation Pass `conversation_id` when the shopper already has a chat — for example they refreshed the page and you minted a new token. The token can only continue and read that conversation. Any other id returns `404`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const scoped = await server.conversations.clientTokens.create({ conversation_id: conversationId, ttl_seconds: 600, // optional, seconds }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} scoped = server.conversations.client_tokens.create( conversation_id=conversation_id, ttl_seconds=600, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/conversations/client_tokens \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{"conversation_id": "conv_123", "ttl_seconds": 600}' ``` An expired or revoked token returns `401`. Mint a new one before it expires. If you already have a `conversation_id`, pass it so the new token stays on the same conversation. ## Revoke Revoke from the server when the session ends. Put the token in the body so it stays out of URL logs. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} await server.conversations.clientTokens.revoke({ token: "c3_ct_..." }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} server.conversations.client_tokens.revoke(token="c3_ct_...") ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/conversations/client_tokens/revoke \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{"token": "c3_ct_..."}' ``` # Collections Source: https://docs.trychannel3.com/conversations/collections Pass `config.collection_id` on **every turn** to bind the chat to a saved catalog scope. Unlike [context](/conversations/context), the collection id is not stored on the thread. Unlike [filters](/conversations/filters), it is a reusable OR-of-clauses bound, not a one-off filter object. Channel3 injects a description of the collection into the model (brands, categories, websites, pinned products) plus any filters on that turn. If the shopper asks for something outside the collection and search returns nothing, the assistant can say the chat is limited to that catalog and suggest a query that still fits. Create the collection with [`POST /v1/collections`](/api-reference/v1/create-collection) (free). Full walkthrough: [Collections](/guides/collections). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [{ type: "text", text: "Trail runners under $150" }], }, config: { collection_id: collectionId }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} stream = client.conversations.create_turn_stream( message={ "role": "user", "parts": [{"type": "text", "text": "Trail runners under $150"}], }, config={"collection_id": collection_id}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{ "type": "text", "text": "Trail runners under $150" }] }, "config": { "collection_id": "COLLECTION_ID" } }' ``` # Context Source: https://docs.trychannel3.com/conversations/context **Context** is who this chat is with, and what your app is. Set it **only on the first turn**. It sticks for the whole conversation. Sending it again on a later turn is a `422`. Each field can be up to 4,000 characters. * `user_context` — the shopper (preferences, budget, size). * `application_context` — context on your app, where this conversation is taking place ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [{ type: "text", text: "Weekend outfits" }], }, context: { user_context: "25 year old software engineer in Williamsburg. Prefers minimalist styles, budget under $200.", application_context: "Shopping assistant inside a banking app.", }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} stream = client.conversations.create_turn_stream( message={ "role": "user", "parts": [{"type": "text", "text": "Weekend outfits"}], }, context={ "user_context": "25 year old software engineer in Williamsburg. Prefers minimalist styles, budget under $200.", "application_context": "Shopping assistant inside a banking app.", }, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{"type": "text", "text": "Weekend outfits"}] }, "context": { "user_context": "25 year old software engineer in Williamsburg. Prefers minimalist styles, budget under $200.", "application_context": "Shopping assistant inside a banking app." } }' ``` For hard catalog rules on a single turn (in-stock, a set of retailers), use [filters](/conversations/filters). To bind the chat to a saved set of brands, websites, categories, or products, pass `config.collection_id` on every turn — Channel3 tells the model what that collection covers so empty results can be explained. See [Collections](/guides/collections). # Errors Source: https://docs.trychannel3.com/conversations/errors If the turn never starts, you get an **HTTP error**. If it starts and then fails, you get an `error` **event on the stream**. HTTP body: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "rate_limited", "message": "…" } } ``` On a stream `error` event, `retryable: true` means a model or provider blip — retrying is reasonable. `retryable: false` means the request was wrong — fix it. | Code | HTTP | Meaning | | ------------------------ | -------- | ---------------------------------------------------------------------- | | `invalid_request` | 400, 422 | Bad body, or `context` sent on a continue turn. | | `unauthorized` | 401 | Bad, expired, or revoked API key or client token. | | `insufficient_credits` | 402 | No credits left. | | `conversation_not_found` | 404 | Wrong id, or a token asking for a conversation it is not bound to. | | `token_not_found` | 404 | Revoke was called for a token this vendor does not own. | | `turn_conflict` | 409 | A turn is already running on this conversation. Wait for it to finish. | | `rate_limited` | 429 | Per-token or per-vendor cap. Check `Retry-After`. | | `model_unavailable` | 503 | All model providers unavailable. Retryable. | | `service_unavailable` | 503 | Token store is temporarily unavailable. Retry. | | `internal` | 500 | Server error. | See [rate limits](/conversations/rate-limits) for `429` caps, and [client tokens](/conversations/client-tokens) for `401` / `404` on tokens. # Filters Source: https://docs.trychannel3.com/conversations/filters **Filters** are hard catalog rules for **this turn only** — in-stock, a set of retailers, a price range. They use the same `SearchFilters` shape as the [Search API](/guides/search). They do not stick across turns. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [{ type: "text", text: "in brown instead" }], }, conversation_id: conversationId, filters: { availability: ["InStock"], website_ids: ["nordstrom.com"] }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} stream = client.conversations.create_turn_stream( message={ "role": "user", "parts": [{"type": "text", "text": "in brown instead"}], }, conversation_id=conversation_id, filters={"availability": ["InStock"], "website_ids": ["nordstrom.com"]}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "Authorization: Bearer c3_ct_..." \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{"type": "text", "text": "in brown instead"}] }, "conversation_id": "conv_REPLACE_ME", "filters": { "availability": ["InStock"], "website_ids": ["nordstrom.com"] } }' ``` For shopper preferences and app notes that should last the whole conversation, use [context](/conversations/context). To limit the whole catalog to saved brands, websites, categories, or products, pass `config.collection_id` — see [Collections](/guides/collections). # Overview Source: https://docs.trychannel3.com/conversations/index The Conversational Agent is a **shopping assistant you can drop right into your app**. Your user sends a message. The agent responds, asks helpful follow-up questions, recommends products, and compares offers. Everything streams to the front-end, so chatting with the agent feels fast and fluid. Each message is one **turn**: one user message in, one assistant reply out. Channel3 remembers the conversation so follow-ups like "in brown" or "something cheaper" make sense without repeating the whole request. Each turn costs **1 credit**. Each catalog search costs **1 credit**. Reading a conversation is free. # Messages and Parts Source: https://docs.trychannel3.com/conversations/messages-and-parts A message has a `role` (`user` or `assistant`) and a list of `parts`. Parts are the blocks your UI renders — text, an image the shopper sent, or product cards from a catalog tool. ## What the shopper sends User parts are `text` and `image`. You can send either, or both. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Text { role: "user", parts: [{ type: "text", text: "I need waterproof hiking boots for day hikes, under $200" }], } // Image search { role: "user", parts: [{ type: "image", url: "https://…/photo.jpg" }], } // Text + image { role: "user", parts: [ { type: "text", text: "find boots like these" }, { type: "image", url: "data:image/jpeg;base64,..." }, ], } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} {"role": "user", "parts": [{"type": "text", "text": "I need waterproof hiking boots for day hikes, under $200"}]} {"role": "user", "parts": [{"type": "image", "url": "https://…/photo.jpg"}]} { "role": "user", "parts": [ {"type": "text", "text": "find boots like these"}, {"type": "image", "url": "data:image/jpeg;base64,..."}, ], } ``` ```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}} { "role": "user", "parts": [ { "type": "text", "text": "find boots like these" }, { "type": "image", "url": "data:image/jpeg;base64,..." } ] } ``` `image.url` accepts a `data:` URL or a public `https://` URL. `data:` URIs are uploaded and rewritten server-side. If you send only an image, Channel3 finds the products in it (same as *Find products in the image.*). ## What the agent sends Assistant parts are `text` and `tool`. A tool part is one catalog call. Buy URLs already carry your affiliate tracking. The `Product` objects are the same type as the [Search API](/guides/response-overview). `tool_name` is `search_products`, `show_products`, `compare_products`, or `image_search`. The model may also run an internal lookup (`lookup_products`) that never appears in your UI. For `search_products`, `show_products`, and `compare_products`, render `output.products` as product cards. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "type": "tool", "tool_call_id": "call_1", "tool_name": "search_products", "input": { "query": "waterproof hiking boots for day hikes under $200" }, "output": { "products": [ /* Product[] */ ], "next_page_token": "…" } } ``` `image_search` runs when the shopper sends a photo. Render `output.things` the same way as [find-products-in-image](/guides/image-search): one heading per `thing` and a product rail from that group's `matches`. There is no top-level `products` list on this tool. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "type": "tool", "tool_call_id": "call_2", "tool_name": "image_search", "input": { "image_url": "https://…/photo.jpg" }, "output": { "things": [ { "thing": "lamp", "matches": [ /* Product[] */ ] }, { "thing": "desk", "matches": [ /* Product[] */ ] } ] } } ``` The assistant message may also include `suggestions`: tap-ready follow-ups for your UI, like "in brown instead". # Quickstart Source: https://docs.trychannel3.com/conversations/quickstart > **Shopper:** I need waterproof hiking boots for day hikes, under \$200 That's the whole request. Next you mint a token and send this message — Channel3 opens the conversation, searches, and streams the reply. Keep the API key on the server. The browser only ever sees a client token. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import { Channel3 } from "@channel3/sdk"; const server = new Channel3(); // reads CHANNEL3_API_KEY export async function POST() { const created = await server.conversations.clientTokens.create(); return Response.json({ token: created.token, expiresAt: created.expires_at, }); } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 server = Channel3() # reads CHANNEL3_API_KEY created = server.conversations.client_tokens.create() # created.token, created.expires_at ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/conversations/client_tokens \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` Send the shopper's message with **no** conversation ID. Read the stream: save `conversation_id` from `turn.started`, append text deltas, and take the full answer from `turn.completed`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import { Channel3 } from "@channel3/sdk"; const client = new Channel3({ auth: () => Promise.resolve({ headers: { Authorization: `Bearer ${token}` }, }), }); let conversationId: string | undefined; const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [ { type: "text", text: "I need waterproof hiking boots for day hikes, under $200", }, ], }, }); for await (const event of stream) { if (event.type === "turn.started") { conversationId = event.conversation_id; // conv_… — save this } if (event.type === "part.delta") { // append event.delta to the assistant text } if (event.type === "turn.completed") { // full answer in event.message } } ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "Authorization: Bearer c3_ct_..." \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{ "type": "text", "text": "I need waterproof hiking boots for day hikes, under $200" }] } }' ``` > **Agent:** Let me pull up some options. Product cards with titles, images, prices, and buy links. > **Agent:** Here are a few solid day-hiking picks under \$200. The Salomon has the best waterproofing if your trails get wet. > **Shopper:** in brown instead Send only the new message plus the saved conversation ID. Channel3 already knows you were talking about sub-\$200 waterproof day-hiking boots, so it runs a new search for brown ones. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [{ type: "text", text: "in brown instead" }], }, conversation_id: conversationId, // from turn.started }); ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "Authorization: Bearer c3_ct_..." \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{ "type": "text", "text": "in brown instead" }] }, "conversation_id": "conv_REPLACE_ME" }' ``` > **Agent:** On it — pulling up brown options in that same price range. Fresh product cards, then a closing message about the brown options. The conversation ID does not change. Read this stream the same way as the first turn. # Rate Limits Source: https://docs.trychannel3.com/conversations/rate-limits Client tokens are limited **per token**, not per conversation. Traffic with your API key uses the [normal account limits](/faq/rate-limits). | Scope | Limit | | ------------------------ | --------------------------- | | Turns (per client token) | 1 per second, 200 per day | | Reads (per client token) | 5 per second, 1,000 per day | | Token mints (per vendor) | 500 per day | Limits reset on UTC days. A `429` includes `Retry-After`. One chat session usually needs **one** token. Refresh it near expiry instead of minting on every message. Send turns only when the shopper types — do not poll `POST /v1/conversations`. # Read a Conversation Source: https://docs.trychannel3.com/conversations/read-a-conversation When the shopper refreshes the page, fetch the conversation and rebuild the chat. `GET /v1/conversations/{id}` returns metadata plus one page of messages. The first page is the **most recent** messages, so a refresh lands where they left off. Pass `cursor` to load older history. This call is free. A client token can only read its own conversation. History is saved when a turn **finishes** — a failed turn does not add an assistant message. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const page = await client.conversations.retrieve({ conversation_id: conversationId, limit: 50, // cursor: page.response.next_cursor, }); // page.data — this page of messages, most recent first // page.response.has_more, page.response.next_cursor — older messages ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} page = client.conversations.retrieve(conversation_id, limit=50) # page.items — this page of messages, most recent first # page.has_next, page.response.next_cursor — older messages ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.trychannel3.com/v1/conversations/conv_123?limit=50" \ -H "Authorization: Bearer c3_ct_..." ``` `items` are the same user and assistant messages you streamed — text and tool parts with product cards. See [messages and parts](/conversations/messages-and-parts) for how to render them, including `image_search` results grouped by `output.things`. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "id": "conv_123", "created_at": 1730000000, "items": [ { "role": "user", "parts": [ { "type": "text", "text": "I need waterproof hiking boots for day hikes, under $200" } ] }, { "role": "assistant", "parts": [ { "type": "text", "text": "Let me pull up some options." }, { "type": "tool", "tool_call_id": "call_1", "tool_name": "search_products", "input": { "query": "waterproof hiking boots for day hikes under $200" }, "output": { "products": [] } }, { "type": "text", "text": "Here are a few solid day-hiking picks under $200." } ] } ], "has_more": false, "next_cursor": null } ``` # Run a Turn Source: https://docs.trychannel3.com/conversations/run-a-turn A **turn** is one shopper message in, one assistant reply out. `POST /v1/conversations` runs that turn. Omit `conversation_id` on the first message and Channel3 opens a conversation. Pass it on later turns to continue. Stream with `createTurnStream` (the [quickstart](/conversations/quickstart) path). Use `createTurn` when you want one JSON body instead of a stream — for example a backend job. **One turn at a time per conversation.** A second `POST` while a turn is still running returns `409`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const stream = await client.conversations.createTurnStream({ message: { role: "user", parts: [ { type: "text", text: "I need waterproof hiking boots for day hikes, under $200", }, ], }, conversation_id: conversationId, // omit on the first message }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} stream = client.conversations.create_turn_stream( message={ "role": "user", "parts": [ { "type": "text", "text": "I need waterproof hiking boots for day hikes, under $200", } ], }, conversation_id=conversation_id, # omit on the first message ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -N -X POST https://api.trychannel3.com/v1/conversations \ -H "Authorization: Bearer c3_ct_..." \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{ "type": "text", "text": "I need waterproof hiking boots for day hikes, under $200" }] }, "conversation_id": "conv_REPLACE_ME" }' ``` | Field | Rule | | ---------------------- | ------------------------------------------------------------------------------------------------------ | | `message` | Required. Text and/or [image parts](/conversations/messages-and-parts). | | `conversation_id` | Omit to create the conversation. Send to continue it. | | `filters` | Optional. Pinned catalog filters for **this turn only**. | | `config.collection_id` | Optional. Bind this turn's searches to a [collection](/guides/collections). Pass it on **every turn**. | | `context` | Optional. Shopper and app notes. **Only on the first turn** (no `conversation_id`). | | `stream` | Default `true`. Set `false` for one JSON response. | The SDK sends your user id with `x-user-id` when you set it, so clicks and sales attach to your user. See [context](/conversations/context), [filters](/conversations/filters), and [streaming events](/conversations/streaming-events). ## One JSON reply Set `stream: false`, or call `createTurn`. You get `message` (the full assistant reply) and `usage` (`credits_charged`, `searches_run`). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const result = await client.conversations.createTurn({ message: { role: "user", parts: [{ type: "text", text: "Gift ideas for a new dad" }], }, }); // result.message — full assistant message // result.usage.credits_charged, result.usage.searches_run ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} result = client.conversations.create_turn( message={ "role": "user", "parts": [{"type": "text", "text": "Gift ideas for a new dad"}], }, ) # result.message, result.usage ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/conversations \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "stream": false, "message": { "role": "user", "parts": [{"type": "text", "text": "Gift ideas for a new dad"}] } }' ``` # Streaming Events Source: https://docs.trychannel3.com/conversations/streaming-events A streamed turn is a `text/event-stream`. The SDK turns each frame into a typed `TurnEvent`. The stream ends after a terminal event and `[DONE]`. For a shopping reply you usually need three events: save the conversation id, append text as it arrives, then take the finished message (including product cards). | Event | When | What to do | | ---------------- | --------------------------- | ------------------------------------------------------ | | `turn.started` | First, once | Save `conversation_id`, `turn_id`, `message_id`. | | `part.started` | A text or tool block begins | Add a block at `part_index`. | | `part.delta` | Text is generating | Append `delta` to the text at `part_index`. | | `part.completed` | A block is done | Replace the block at `part_index` with the final part. | | `turn.completed` | Success, once | Full `message` plus `usage`. | | `error` | The turn failed | Read `code`, `message`, `retryable`. | `part_index` is the position in the final message. Order inside one reply is stable. See [messages and parts](/conversations/messages-and-parts) for what to render — `search_products` uses `output.products`; `image_search` uses `output.things`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} for await (const event of stream) { switch (event.type) { case "turn.started": conversationId = event.conversation_id; break; case "part.started": case "part.completed": parts[event.part_index] = event.part; break; case "part.delta": text += event.delta; break; case "turn.completed": done(event.message); break; case "error": if (event.retryable) retry(); else showError(event.message); break; } } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} for event in stream: if event.type == "turn.started": conversation_id = event.conversation_id elif event.type in ("part.started", "part.completed"): parts[event.part_index] = event.part elif event.type == "part.delta": text += event.delta elif event.type == "turn.completed": done(event.message) elif event.type == "error": if event.retryable: retry() else: show_error(event.message) ``` ```text SSE theme={"theme":{"light":"github-light","dark":"github-dark"}} event: turn.started data: {"type":"turn.started","conversation_id":"conv_123","turn_id":"turn_…","message_id":"msg_…"} event: part.delta data: {"type":"part.delta","part_index":0,"delta":"Let me pull up some options."} event: part.completed data: {"type":"part.completed","part_index":1,"part":{"type":"tool","tool_name":"search_products",…}} event: turn.completed data: {"type":"turn.completed","message":{"parts":[ … ]},"usage":{"credits_charged":2,"searches_run":1}} ``` The stream sends a heartbeat about every 15 seconds — silence between tokens is not a dead connection. A turn that fails **before** it starts returns a normal HTTP error, not an `error` event. See [errors](/conversations/errors). # Billing & Credits Source: https://docs.trychannel3.com/faq/billing-and-credits ### What counts as a credit? See [pricing](/pricing) for the per-endpoint breakdown. ### Where can I see my usage? Usage and remaining credits are shown on the [Channel3 dashboard](https://trychannel3.com/dashboard). ### What are the rate limits? See [rate limits](/faq/rate-limits) for plan tiers (`default`, `high`, `no-limit`), burst, and daily quotas. # Catalog & Coverage Source: https://docs.trychannel3.com/faq/catalog-and-coverage ### Which countries and currencies are supported? Channel3 supports locale-aware search across the U.S., UK, Canada, Australia, and much of Europe. Set a default locale on the SDK client or pass `country`, `language`, and `currency` per call — see [Localization](/guides/localization). ### Countries | Code | Market | | ---- | ---------------------------------------------------------------------------------- | | `US` | United States | | `CA` | Canada | | `GB` | United Kingdom | | `IE` | Ireland | | `AU` | Australia | | `DE` | Germany | | `AT` | Austria | | `FR` | France | | `BE` | Belgium | | `IT` | Italy | | `ES` | Spain | | `NL` | Netherlands | | `SE` | Sweden | | `FI` | Finland | | `PT` | Portugal | | `CZ` | Czech Republic | | `GR` | Greece | | `RO` | Romania | | `EU` | Europe (pan-region; use with `currency: EUR` when you don't need a single country) | ### Currencies `USD`, `CAD`, `AUD`, `GBP`, `EUR`, `SEK`, `CZK`, `RON` When you set `country` only, the API infers `currency` (for example `GB` → `GBP`). When all three locale fields are unset, defaults are `en` / `US` / `USD`. ### Languages `en`, `de`, `fr`, `it`, `es`, `nl`, `sv`, `fi`, `pt`, `cs`, `el`, `ro` Coverage outside the U.S. is still growing. If you need a market we don't support yet, contact us or ask in [Discord](https://discord.gg/6J6MjctXrH). ### Can I request a brand or retailer be added? Yes — email us with the brand or retailer and we'll prioritize it. # Commissions Source: https://docs.trychannel3.com/faq/commissions ### How do commissions work? We've partnered with thousands of brands who pay commissions on AI-driven sales. After your app or agent recommends a product, we track if the shopper buys it (within a retailer-set timeframe). We pay you for those purchases. For the latest rates, see [Brands](https://trychannel3.com/dashboard/brands) and [Websites](https://trychannel3.com/dashboard/websites) in the dashboard. Rates also appear on offers in search responses (`max_commission_rate`). ### Why do rates show "maximum"? Commissions can be adjusted or canceled by retailers for reasons such as returns, order cancellations, suspected fraud, attribution to another channel, or network quality concerns. ### When and how are commissions paid out? Earnings appear on the [Channel3 dashboard](https://trychannel3.com/dashboard) once attributed sales clear the retailer's return window. Earnings are paid out securely via Stripe. You can also pull clicks and transactions via the [Reporting API](/guides/reporting). # Rate Limits Source: https://docs.trychannel3.com/faq/rate-limits ### What are the rate limits? Rate limits keep the API fast for everyone. Limits apply per API key. | Plan | Rate (req/s) | Burst | Daily quota | | ---------- | ------------ | ----- | ----------- | | `default` | 50 | 100 | 10,000 | | `high` | 200 | 500 | 100,000 | | `no-limit` | 1000 | 2000 | none | Need a higher plan? Contact us. If you exceed a limit, the API returns `429 Too Many Requests`. Back off and retry. Client tokens used by the [Conversational Agent](/conversations/rate-limits) have their own per-token caps. # Support Source: https://docs.trychannel3.com/faq/support ### How do I get help or report a bug? Email us at [support@trychannel3.com](mailto:support@trychannel3.com) or join our [Discord](https://discord.gg/6J6MjctXrH). We're happy to help with integration questions, brand requests, billing, or anything else. # Agentic Search Source: https://docs.trychannel3.com/guides/agentic-search For complicated queries, like "Knicks gear from Nike or Adidas, for kids, in blue," we recommend agentic search. Toggle to agentic mode, and an LLM plans the search: it extracts deterministic filters, may split the request into OR/AND sub-searches, runs them, and merges the results into one ranked list. Latency is higher than `default` or `keyword` (it's LLM-bound), so use it for shopping and assistant flows where result quality matters more than milliseconds. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); // reads CHANNEL3_API_KEY from env const results = await client.products.search({ query: "Nike or Adidas hoodies in black or grey for men, size large", config: { mode: "agentic" }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() # reads CHANNEL3_API_KEY from env results = client.products.search( query="Nike or Adidas hoodies in black or grey for men, size large", config={"mode": "agentic"}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/search \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "Nike or Adidas hoodies in black or grey for men, size large", "config": {"mode": "agentic"}}' ``` ## When to use Prefer agentic when the request has several moving parts that should become real filters: * **Multi-constraint shopping language** — brand, retailer, category, size, price, and attributes in one sentence * **Alternatives and branches** — `"Nike or Adidas…"`, `"green 4-wheel or black 2-wheel…"` * **Conversational / vibe phrasing** — descriptive language stays in the semantic query while constraints become structured filters * **Pasted identifiers** — SKU, MPN, GTIN, UPC, or EAN that should pin and boost an exact match If you already know exact attribute handles and values, build them yourself with [attribute filters](/guides/advanced-search) — agentic is for when the caller has natural language, not a pre-built filter tree. ## What it extracts Natural language is turned into structured `SearchFilters` (and related ranking signals), including: | Extracted as | Examples in the query | | --------------------------- | ------------------------------------------------------------------------------------------------------ | | **Product type → category** | `"TV"` → television (taxonomy-backed, not only a keyword boost) | | **Brand + retailer** | `"Levi's"`, `"from Best Buy"` → catalog brand / website IDs | | **Gender + age** | `"men"`, `"boys"`, and similar combined cues | | **Condition** | `new` / `used` / `refurbished`, including `"vintage"` / `"pre-owned"` | | **Price min / max** | `"under $400"`, `"above $100 or below $90"` (as separate ranges when disjunctive) | | **Colors** | Named shades → product-realistic hex matching (not string equality on `"navy"`) | | **Category attributes** | `"waterproof"`, `"wide feet"`, `"size 10"`, `"55 inch"`, `"queen size"` against that category's schema | | **Physical dimensions** | `"30 inches wide and 72 inches tall"` → structured L/W/H/weight filters when axes are named | | **Exact product IDs** | SKU / MPN / GTIN / UPC / EAN → deterministic boost and pin | | **Stock preference** | In-stock by default; widens when the user says `"even if sold out"` | Query shapes it plans for (not just single-filter extraction): * **Alternatives / DNF** — `"Nike or Adidas hoodies in black or grey"` → multiple sub-searches, then merge * **Correlated constraints** — `"green 4-wheel or black 2-wheel suitcase"` keeps color tied to the other trait per branch * **AND within one product** — `"brown and yellow sneakers"` stays one search with both colors * **Descriptive residue** — `"cozy oversized cable knit sweater for winter"` keeps vibe language in the semantic query while filters stay clean Attribute coverage depends on the category taxonomy. Agentic does not replace hand-built attribute filters when you already know the exact handles and values. The filters you provide override the filters set by the agent. ## Same API surface Request and response shapes are identical to `default` mode. Explicit `filters` combine with what the planner extracts, and pagination via `next_page_token` works as usual. Text search, filters, pagination, and the other search modes. Build structured attribute filters yourself for full control. # Attributes Source: https://docs.trychannel3.com/guides/attribute-filters Attributes are a powerful way to find exactly what you're looking for. Here's how it works — every product belongs to a category. Every category defines an attribute schema: keys and allowed values. You can search against those values via `filters.attributes`. For example, products in **Digital Cameras** can be labeled with attributes like: * `connectivity-technology`: Wi-Fi, Bluetooth, USB, … * `camera-sensor-type`: CMOS, BSI CMOS, CCD, … * `camera-features`: Image stabilization, Waterproof, Touchscreen, … This lets you find "all waterproof cameras with Wi-Fi or Bluetooh connectivity and a CCD sensor" — and get back only products that match. Search for a category, or retrieve one directly by slug, to see available handles and values. [Category search](/api-reference/v1/search-categories) is free. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); const { categories } = await client.categories.search({ query: "cameras" }); const slug = categories[0].slug; // e.g. "digital-cameras" const category = await client.categories.retrieve(slug); // category.attributes → [{ slug, name, values }, ...] for (const attr of category.attributes) { console.log(`${attr.slug}: ${attr.values.slice(0, 5).join(", ")}`); } // connectivity-technology: Wi-Fi, Bluetooth, USB, ... // camera-sensor-type: CMOS, BSI CMOS, CCD, ... // camera-features: Image stabilization, Waterproof, ... ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() category_results = client.categories.search(query="cameras") slug = category_results.categories[0].slug # e.g. "digital-cameras" category = client.categories.retrieve(slug) # category.attributes → [{ slug, name, values }, ...] for attr in category.attributes: print(f"{attr.slug}: {', '.join(attr.values[:5])}") # connectivity-technology: Wi-Fi, Bluetooth, USB, ... # camera-sensor-type: CMOS, BSI CMOS, CCD, ... ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.trychannel3.com/v1/categories/search?query=cameras" \ -H "x-api-key: $CHANNEL3_API_KEY" curl "https://api.trychannel3.com/v1/categories/digital-cameras" \ -H "x-api-key: $CHANNEL3_API_KEY" ``` Pass attribute handles and canonical values to `filters.attributes`. Pair with `category_ids` using the same category slug. Multiple values for the same handle match **any** of them (OR); multiple handles are combined with **AND**. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const results = await client.products.search({ query: "mirrorless camera", filters: { category_ids: [slug], attributes: { "connectivity-technology": ["Wi-Fi", "Bluetooth"], "camera-features": ["Image stabilization", "Waterproof"], "camera-sensor-type": ["CMOS"], }, }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} results = client.products.search( query="mirrorless camera", filters={ "category_ids": [slug], "attributes": { "connectivity-technology": ["Wi-Fi", "Bluetooth"], "camera-features": ["Image stabilization", "Waterproof"], "camera-sensor-type": ["CMOS"], }, }, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/search \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mirrorless camera", "filters": { "category_ids": ["digital-cameras"], "attributes": { "connectivity-technology": ["Wi-Fi", "Bluetooth"], "camera-features": ["Image stabilization", "Waterproof"], "camera-sensor-type": ["CMOS"] } } }' ``` # Brand Source: https://docs.trychannel3.com/guides/brand A **brand** is the company or label behind a product — Nike, Dyson, Sony. Use brands when shoppers care about *who makes it*, not *which store sells it*. ## How to use brands 1. Find a brand, and its stable ID, with [brand search](/guides/brand-search) 2. Pass `brand_ids` in search filters to narrow product results 3. Read `product.brand` on any product response for display ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} const { brands } = await client.brands.search({ query: "nike" }); const nikeId = brands[0].id; const results = await client.products.search({ query: "running shoes", filters: { brand_ids: [nikeId] }, }); ``` ## Brand ≠ website Nike products appear on many websites. Filtering by brand keeps all those retailers; filtering by website keeps only that store's offers. # Brand Source: https://docs.trychannel3.com/guides/brand-filters Restrict search results to products from specific brands. Pass one or more brand IDs to `filters.brand_ids` on `POST /v1/search`. Resolve IDs with `GET /v1/brands/search`. A **[brand](/concepts/brand)** is the company or label behind a product. A **[website](/concepts/website)** is the online store selling it. Search for a brand by name to get its stable ID. [Brand search](/api-reference/v1/search-brands) is free. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); const brands = await client.brands.search({ query: "Nike" }); const nikeId = brands.brands[0].id; console.log(nikeId); // e.g. "brand_nike_abc123" ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() brands = client.brands.search(query="Nike") nike_id = brands.brands[0].id print(nike_id) # e.g. "brand_nike_abc123" ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.trychannel3.com/v1/brands/search?query=Nike" \ -H "x-api-key: $CHANNEL3_API_KEY" ``` Pass the ID (or IDs) to `filters.brand_ids`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const results = await client.products.search({ query: "running shoes", filters: { brand_ids: [nikeId], }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} results = client.products.search( query="running shoes", filters={"brand_ids": [nike_id]}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/search \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "running shoes", "filters": { "brand_ids": ["brand_nike_abc123"] } }' ``` Full overview of the search endpoint and all available filters. Full API reference for the brands endpoints. # Caching Best Practices Source: https://docs.trychannel3.com/guides/caching The recommended path for most integrations is **real-time product discovery** via the [`/search`](/v1/search) endpoint. The guidance below is for use cases where product IDs are stored and revisited later, such as: * **Persisted carts or "saved for later" collections** — users bookmark products and return to them hours or days later * **Hand-curated shopping experiences** — developers select specific products for display, typically originally discovered via [`/search`](/v1/search) or [`/lookup`](/v1/lookup) ## Cache IDs, not data Product IDs and category slugs are stable identifiers — cache them freely and for as long as you like. Everything else on a product object (prices, availability, images, descriptions, offers, variants) should be treated as **unstable**. These fields can change at any time as merchants update their catalogs. Retrieve fresh product snapshots at load time with the **free** [`GET /v1/products/{product_id}`](/v1/products/\{product_id}). **Do:** * Store `product.id` from search results and reuse it later **Don't:** * Cache prices, availability, or offer URLs and serve them to users without refreshing * Assume image URLs or product descriptions are permanent * Cache offer URLs — these are short-lived and must be fetched fresh before presenting to users ## Refresh at load time When you need to display a product to a user, fetch the latest data with [`GET /v1/products/{product_id}`](/v1/products/\{product_id}) rather than relying on a cached snapshot. This ensures prices, stock status, and buy links are current. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // At search time — cache the IDs const results = await client.products.search({ query: "running shoes" }); const productIds = results.products.map((p) => p.id); // store productIds in your database or session // At display time — fetch fresh data const product = await client.products.retrieve(productId); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} # At search time — cache the IDs results = client.products.search(query="running shoes") product_ids = [p.id for p in results.products] # store product_ids in your database or session # At display time — fetch fresh data product = client.products.retrieve(product_id) ``` ## Short-lived caches are fine Caches with a short TTL (a few minutes to a few hours) on full product objects for presentational data (images, title) is reasonable for use cases that require it. Just avoid caching product data for many hours or days — pricing and availability can shift quickly, and we often update our data model to include new features and info. | Data | Cache strategy | | -------------------------------------------- | -------------------------------------- | | Product IDs, category slugs | Cache indefinitely | | Product details (title, description, images) | Short TTL or refresh on display | | Prices, availability, offers | Always refresh before showing to users | Full overview of the search endpoint, filters, and pagination. Set country, language, and currency for market-specific results. # Category Source: https://docs.trychannel3.com/guides/category A **category** is a node in Channel3's product taxonomy — for example `running-shoes`, `sofas`, or `laptops`. Every product belongs to a category. ## How to find and filter by category ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} const { categories } = await client.categories.search({ query: "sofas" }); const slug = categories[0].slug; // e.g. "sofas" const results = await client.products.search({ query: "sectional", filters: { category: slug }, }); ``` Or read `product.category.slug` from any product you already have. ## Attributes Each category defines a shared **attribute vocabulary** — the structured feature keys that apply to every product in that node. Every camera, for example, is labeled with the same handles (`sensor_type`, `megapixels`, `lens_mount`, and so on), so you can compare products side by side on the same axes. [Category detail](/api-reference/v1/category-detail) returns that vocabulary: each attribute has a `handle` (the key you'll use in filters) and a list of known `values`. ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} const category = await client.categories.retrieve("digital-cameras"); for (const attr of category.attributes) { console.log(`${attr.handle}: ${attr.values.slice(0, 5).join(", ")}`); } // sensor_type: CMOS, CCD, ... // megapixels: 20, 24, 45, ... // lens_mount: Sony E, Canon RF, ... ``` Pair those handles with the `attributes` search filter for precise queries — e.g. sofas with USB charging, or cameras with a specific sensor type. Full walkthrough: [Attribute search](/guides/advanced-search). On product detail, each product's filled-in values appear as `structured_attributes` — same keys as the category vocabulary, with that product's values. # Collections Source: https://docs.trychannel3.com/guides/collections A **collection** is a catalog you curate yourself — brands, websites, categories, and specific products you pick. Create it once, then search, browse, and chat inside it by passing `config.collection_id`. Creating and managing collections is **free**. ## What's in a collection Each collection is made of one or more **clauses**. Inside a clause, fill in any of: * **Any of these brands** (`brand_ids`) * **Any of these websites** (`website_ids`) * **Any of these categories** (`category_ids`) * **Any of these products** (`product_ids`) Let's walk through an example. You want Nike running shoes. That's one clause: Nike under brands, running shoes under categories. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import { Channel3 } from "@channel3/sdk"; const client = new Channel3(); const brands = await client.brands.search({ query: "Nike" }); const nikeId = brands.brands[0].id; const collection = await client.collections.create({ name: "Nike running", clauses: [ { brand_ids: [nikeId], category_ids: ["running-shoes"], }, ], }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() brands = client.brands.search(query="Nike") nike_id = brands.brands[0].id collection = client.collections.create( name="Nike running", clauses=[ { "brand_ids": [nike_id], "category_ids": ["running-shoes"], } ], ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/collections \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Nike running", "clauses": [ { "brand_ids": ["brand_nike_abc123"], "category_ids": ["running-shoes"] } ] }' ``` Search inside that collection and you'll only get Nike products in running shoes — not all Nike, and not running shoes from other brands. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const results = await client.products.search({ query: "trail runners", config: { collection_id: collection.collection_id }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} results = client.products.search( query="trail runners", config={"collection_id": collection.collection_id}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/search \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "trail runners", "config": { "collection_id": "COLLECTION_ID" } }' ``` Want REI too? Adding a clause and adding to a clause give different results. Nike running shoes **or** anything from REI. ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} add_clauses: [ { website_ids: ["rei.com"] }, ] ``` Only Nike running shoes, and only from REI. ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} replace_clauses: [{ clause_id, brand_ids: [nikeId], category_ids: ["running-shoes"], website_ids: ["rei.com"], }] ``` Filters on a later search can only **narrow** what's already in the collection — they can't widen it. In the Nike running shoes collection: ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Narrows: Nike running shoes under $150 await client.products.search({ query: "trail runners", filters: { price: { max_price: 150 } }, config: { collection_id: collection.collection_id }, }); // Zero results: this asks for Nike (the collection) AND Adidas (the filter) await client.products.search({ query: "trail runners", filters: { brand_ids: [adidasId] }, config: { collection_id: collection.collection_id }, }); ``` A collection can have up to 100 clauses, 100 IDs in each list per clause, and 500 `product_ids` in total. For websites, you can pass a merchant ID or a domain like `nike.com`. You can create an empty collection (`clauses: []`) and add clauses later. Optional `user_id` ties the collection to one of your users. If a later search uses this collection and you don't send `x-user-id`, Channel3 attributes clicks to that user. ## Search, browse, image search, and similar Pass the ID on `config.collection_id`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const results = await client.products.search({ query: "trail runners", config: { collection_id: collection.collection_id }, }); const grid = await client.products.browse({ config: { collection_id: collection.collection_id }, }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} results = client.products.search( query="trail runners", config={"collection_id": collection.collection_id}, ) grid = client.products.browse( config={"collection_id": collection.collection_id}, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.trychannel3.com/v1/search \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "trail runners", "config": { "collection_id": "COLLECTION_ID" } }' ``` The same `config.collection_id` field works on [`POST /v1/image-search`](/api-reference/v1/image-search), [`POST /v1/similar`](/api-reference/v1/similar-products), and [`POST /experimental/find-products-in-image`](/api-reference/v1/find-products-in-image). A 404 means that ID doesn't exist. `/v1/lookup` doesn't take a collection. ## List collections List is paginated. Pass `user_id` to see one user's collections. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const page = await client.collections.list({ limit: 50 }); for (const item of page.items) { console.log(item.collection_id, item.name); } if (page.next_page_token) { const next = await client.collections.list({ limit: 50, page_token: page.next_page_token, }); } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} page = client.collections.list(limit=50) for item in page.items: print(item.collection_id, item.name) if page.next_page_token: next_page = client.collections.list( limit=50, page_token=page.next_page_token, ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.trychannel3.com/v1/collections?limit=50" \ -H "x-api-key: $CHANNEL3_API_KEY" ``` ## Get a collection Fetch one collection by ID. The response includes name, description, `user_id`, and every clause. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const collection = await client.collections.retrieve(collectionId); console.log(collection.collection_id, collection.name); console.log(collection.clauses); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} collection = client.collections.retrieve(collection_id) print(collection.collection_id, collection.name) print(collection.clauses) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://api.trychannel3.com/v1/collections/COLLECTION_ID \ -H "x-api-key: $CHANNEL3_API_KEY" ``` A 404 means that ID doesn't exist. ## Update a collection PATCH can replace a clause in place by `clause_id`, remove one, or add another clause. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const updated = await client.collections.update(collection.collection_id, { add_clauses: [{ brand_ids: ["brand_adidas_abc"] }], }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} updated = client.collections.update( collection.collection_id, add_clauses=[{"brand_ids": ["brand_adidas_abc"]}], ) ``` ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH https://api.trychannel3.com/v1/collections/COLLECTION_ID \ -H "x-api-key: $CHANNEL3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "add_clauses": [{ "brand_ids": ["brand_adidas_abc"] }] }' ``` Filters on a search can only narrow what the collection returns. Bind a shopping chat to a collection. # Color Source: https://docs.trychannel3.com/guides/color-filters Find products that match one or more colors. This is useful for building color-matched shopping experiences — for example, finding brown couches or blue Dodger's gear. Color filtering is currently in **Beta**. The API surface may change. ``` KitDen chat widget returning two on-budget product picks with an AI rationale Teach your coding agent the Channel3 API for faster integration. The React component catalog and shadcn registry. Credits, free tier, and commission split. Questions or a brand request? We're happy to help. Building with Cursor or Claude Code alongside Lovable? Install the Channel3 API skill in your terminal: `npx skills add channel3-ai/skills --skill channel3-api`. See [Channel3 API skill](/skills/channel3-api). (Terminal agents only — not the Lovable chat box.) # MCP Source: https://docs.trychannel3.com/mcp-overview The Channel3 MCP lets any MCP-compatible agent search the product catalog in real time — prices, images, descriptions, and availability. Your agent calls the catalog tools for you, so you can ask in natural language. ## Start here Connect Cursor, VS Code, Claude, ChatGPT, Codex, or OpenCode. Start without an API key. Product search, including by image, and full product details. Prompts for search, comparison, lookup, and recommendations. ## Server URL ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} https://mcp.trychannel3.com/ ``` No API key is needed for the [free tier](/mcp/free-tier). ## Quick example ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find running shoes similar to the Nike Pegasus, compare prices across retailers, and recommend the lowest in-stock option. ``` See [usage examples](/mcp/usage) for more prompts, or [available tools](/mcp/tools) for how to phrase them and what each tool returns. # Free Tier Source: https://docs.trychannel3.com/mcp/free-tier The Channel3 MCP free tier is the quickest way to let an MCP-compatible agent search the catalog. Use this URL: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} https://mcp.trychannel3.com/ ``` No API key is required, and limits are generous and sized for personal use. Connect it with [MCP setup](/mcp/setup), then try a prompt: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find in-stock merino wool sweaters under $150 and include buy links. ``` ## When to add an API key Use an API key for unlimited usage, pay-as-you-go, and attributed buy links. Create an account, then copy a key from the [Dashboard](https://trychannel3.com/dashboard/api). Send it as a header with every request: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} X-API-Key: ``` See [Pricing](/pricing) for API costs and [Commissions](/faq/commissions) for monetization. # Setup Source: https://docs.trychannel3.com/mcp/setup Free tier URL (no key): ``` https://mcp.trychannel3.com/ ``` Have a key? Send it as an `X-API-Key` header. See [free tier](/mcp/free-tier) for the difference. Install in Cursor Or add to `~/.cursor/mcp.json`: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "mcpServers": { "Channel3": { "url": "https://mcp.trychannel3.com/" } } } ``` Install in VS Code Or add to `.vscode/mcp.json`: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "servers": { "Channel3": { "type": "http", "url": "https://mcp.trychannel3.com/" } } } ``` ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} claude mcp add --transport http --scope user Channel3 https://mcp.trychannel3.com/ ``` In Claude on web, desktop, or mobile, go to **Settings → Connectors → Add custom connector** and paste: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} https://mcp.trychannel3.com/ ``` Claude connects from its own servers, so one connector covers every Claude surface. Turn on developer mode under **Settings → Security and login**, then create an app under **Settings → Plugins** with this URL and authentication set to none: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} https://mcp.trychannel3.com/ ``` ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} codex mcp add Channel3 --url https://mcp.trychannel3.com/ ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "mcp": { "Channel3": { "type": "remote", "url": "https://mcp.trychannel3.com/", "enabled": true } } } ``` That's it. To confirm the tools are available, ask for something like “find in-stock merino wool sweaters under \$150 with buy links.” See [usage examples](/mcp/usage) for more. # Available Tools Source: https://docs.trychannel3.com/mcp/tools Your agent normally chooses these tools automatically. The list is useful when writing an agent prompt, testing a connection, or deciding which data Channel3 can provide. | Tool | What it does | | ----------------- | ------------------------------------------------------------------------------------------------------------------------ | | `search_products` | Find products from a plain-language description, an image URL, or both | | `get_products` | Fetch full details — offers, variants, images, attributes — for up to 40 products at once, by product ID or retailer URL | ## How tools map to user requests | User says… | Your agent typically uses… | | ----------------------------------------------- | ---------------------------------- | | “Find black running shoes under \$100” | `search_products` | | “Red leather jacket under \$200 from Zara” | `search_products` | | “Find something that looks like this photo” | `search_products` with `image_url` | | “Tell me about the first result” | `get_products` | | “What stores sell this URL, and at what price?” | `get_products` with the URL | | “Compare these three” | `get_products` with all three IDs | Search is semantic and takes no filter parameters, so constraints belong inside the description. A bare category like “good birthday gifts” dilutes the match; keep one concrete idea per call and run several searches for several ideas. `get_products` returns the canonical product plus every retailer [offer](/guides/offer), so price comparison happens there rather than in another search. ## Interactive results On hosts that support [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview), such as Claude and ChatGPT, results also render as a browsable storefront with live prices, similar products, and price history. For full end-user prompts, see [usage examples](/mcp/usage). # Usage Examples Source: https://docs.trychannel3.com/mcp/usage After [connecting the server](/mcp/setup), ask your agent in plain language. It picks the Channel3 tools on its own and turns your request into a catalog search. ## Search with constraints ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find in-stock merino wool sweaters under $150. Show the retailer, price, and buy link for each result. ``` ## Compare retailer offers ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find a Dyson vacuum under $400. Compare every available retailer offer and recommend the lowest in-stock price. ``` ## Look up a store URL ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Look up this product URL. Show its canonical product, variants, and every store selling it with price and availability: https://www.nike.com/t/air-force-1-07-mens-shoes-5QFp5Z/CW2288-111 ``` ## Find similar products ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find products similar to the Nike Pegasus. Keep only in-stock options below $180. ``` ## Search by attributes ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Find sofas with USB charging ports. Prefer in-stock offers and explain which product attributes matched. ``` ## Visual search ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} I have a photo of a jacket. Find visually similar products online, then show prices and buy links. ``` Ask the agent to distinguish the **product** from its **offers** when you need price comparison. See [Offer](/guides/offer). # MPP (Pay Per Call) Source: https://docs.trychannel3.com/mpp Pay per API call with **no signup and no API key**. Agents use [MPP](https://mpp.dev) over **Tempo** (USDC.e on mainnet) at `https://mpp.trychannel3.com`. | | **MPP** | **API key** | | -------- | -------------------------------------- | ----------------------------------------------------------- | | Signup | None | [Free account](https://trychannel3.com/sign-up/developer) | | Billing | **\$0.007 USDC.e** per call (on-chain) | 1000 free credits/mo, then [same rate](/pricing) via Stripe | | Best for | Agents, scripts, one-off automation | Dashboard, commissions, higher volume | Send `x-api-key` **or** satisfy the MPP payment challenge — same endpoints either way. ## Supported endpoints MPP works on these **POST** routes only: | Endpoint | Docs | | ----------------------- | -------------------------------------------- | | `POST /v1/search` | [Search](/guides/search) | | `POST /v1/image-search` | [Image search](/guides/image-search) | | `POST /v1/similar` | [Similar products](/guides/similar-products) | | `POST /v1/lookup` | [URL lookup](/guides/url-lookup) | Everything else (`/v1/browse`, product detail, brands, etc.) requires an API key. ## Quick start **1. Set up a Tempo wallet** with USDC.e: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -fsSL https://tempo.xyz/install | bash tempo wallet login tempo wallet fund ``` **2. Make a paid request** — the CLI handles 402 → pay → retry: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} tempo request -X POST \ --json '{"query":"running shoes"}' \ https://mpp.trychannel3.com/v1/search ``` Also works with [mppx](https://mpp.dev/tools/wallet) (`npx mppx@latest ... --network mainnet`). Plain `curl` returns **402** but cannot complete payment. ## How it works ```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}} sequenceDiagram participant Agent participant API as mpp.trychannel3.com participant Tempo as Tempo chain Agent->>API: POST /v1/search (no auth) API-->>Agent: 402 + WWW-Authenticate (Payment challenge) Agent->>Tempo: Settle $0.007 USDC.e Agent->>API: POST /v1/search + Authorization: Payment ... API-->>Agent: 200 + products + Payment-Receipt ``` 1. Call without auth → **402** with `WWW-Authenticate: Payment ...` 2. Wallet settles **\$0.007** USDC.e on Tempo (7000 base units, chain `4217`) 3. Retry with `Authorization: Payment ` → **200** + `Payment-Receipt` Each payment credential is **single-use**. Pagination (`page_token`) is a new paid call. Failed responses after payment are refunded automatically. ## Errors | Code | Meaning | | ------- | --------------------------------------------- | | **402** | Payment required (or credential already used) | | **403** | Payer address blocked | | **422** | Invalid request body | | **503** | Payment infra unavailable — retry | See [MPP](https://mpp.dev) and the [Tempo wallet CLI](https://tempo.xyz/developers/docs/wallet) for protocol details. # Webhooks Source: https://docs.trychannel3.com/price-tracking-webhooks When you subscribe to price tracking for a product, you can receive webhook notifications whenever the price changes. Use webhooks to alert your users about deals, update your UI, or trigger other actions in real-time. Configure your webhook endpoint in the [Channel3 dashboard](https://trychannel3.com/dashboard). For payload details and event types, see our [webhook event catalog](https://webhooks.trychannel3.com/). # Pricing Source: https://docs.trychannel3.com/pricing Our goal is for you to make much more money on Channel3 than you spend on it, and we've designed our pricing to make that happen. **You get 1000 free credits every month, and extra credits are billed as they're used, at just \$0.007 each.** No account? Agents can pay via [MPP (Machine Payments Protocol)](/mpp) — no API key or signup required. Looking for bulk pricing? Contact us. | Method | Cost | | ------------------------------------ | ---------------------------------------------------- | | /search | 1 credit | | /browse | 1 credit | | /image-search | 1 credit | | /experimental/find-products-in-image | 1 credit + 1 credit per detected thing | | /similar | 1 credit | | /lookup | 1 credit | | /conversations | 1 credit per turn + search credits used by the agent | | /conversations/ | Free | | /collections | Free | | /brands | Free | | /websites | Free | | /products | Free | | /categories | Free | | /price-tracking | 1 credit/product/day | # Product Source: https://docs.trychannel3.com/product-model # SDK Source: https://docs.trychannel3.com/sdk Install the Channel3 SDK, authenticate, and make async calls. ## Installation ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // npm npm install @channel3/sdk // yarn yarn add @channel3/sdk // pnpm pnpm add @channel3/sdk ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} # pip pip install channel3-sdk # uv uv add channel3-sdk ``` ## Authentication Provide your API key via the `CHANNEL3_API_KEY` environment variable, or pass it directly to the client. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; // Initialize with environment variable const client = new Channel3(); // Or, initialize with API key directly const clientWithKey = new Channel3({ apiKey: "your_api_key_here", }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 # Initialize with environment variable client = Channel3() # Or, initialize with API key directly client_with_key = Channel3(api_key="your_api_key_here") ``` ## Async usage ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.search({ query: "organic cotton t-shirt", }); console.log(response); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import asyncio from channel3_sdk import AsyncChannel3 client = AsyncChannel3() async def main(): response = await client.products.search( query="organic cotton t-shirt" ) print(response) if __name__ == "__main__": asyncio.run(main()) ``` ## Explore the SDK Keyword, image, similar-product, filtered, and paginated search. Find a brand, browse the catalog, and use brand filters. List retailers and limit search to their offers. Find categories and apply a category filter. Resolve a retailer URL to a canonical product. Handle SDK request and API errors. # Search with a Base64 Image Source: https://docs.trychannel3.com/sdk/base64-image Encode a local image as base64 and pass `base64_image`. Useful for camera flows or screenshots. See also [Image search](/guides/image-search). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import fs from "node:fs"; import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const b64 = fs.readFileSync("shoe.jpg").toString("base64"); const response = await client.products.search({ base64_image: b64, }); console.log(response.products[0]?.title); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import base64 from channel3_sdk import Channel3 client = Channel3() def main(): with open("shoe.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") response = client.products.search( base64_image=b64, ) print(response.products[0].title) if __name__ == "__main__": main() ``` For a publicly hosted image, prefer [Search with an image URL](/sdk/image-url). For a scene with several products, use [Find products in image](/sdk/find-products-in-image). # Browse the Brand Catalog Source: https://docs.trychannel3.com/sdk/browse-brands Pass `next_cursor` from the response back as `cursor` to fetch the next page. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const page1 = await client.brands.list({ limit: 50 }); console.log( "Page 1:", page1.items.map((b) => b.name), ); if (page1.next_cursor) { const page2 = await client.brands.list({ limit: 50, cursor: page1.next_cursor, }); console.log( "Page 2:", page2.items.map((b) => b.name), ); } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): page1 = client.brands.list(limit=50) print("Page 1:", [b.name for b in page1.items]) if page1.next_cursor: page2 = client.brands.list(limit=50, cursor=page1.next_cursor) print("Page 2:", [b.name for b in page2.items]) if __name__ == "__main__": main() ``` For name lookup, use [Find brands by name](/sdk/find-brands). # Configure Search Behavior Source: https://docs.trychannel3.com/sdk/configure-search Pass a `config` object to set search `mode` and locale. `mode: "keyword"` skips semantic ranking and uses lexical matching only — lowest latency, best for exact product names or SKUs. | Mode | Behavior | | --------- | ------------------------------------------------------------------------------------------- | | `default` | Lexical + semantic. Recommended for most searches. | | `keyword` | Lexical only. Use for real-time, low-latency needs. | | `agentic` | LLM-planned sub-searches for complex queries. See [Agentic search](/guides/agentic-search). | ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.search({ query: "linen shirt", config: { mode: "keyword", }, }); console.log(response.products[0]?.title); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.products.search( query="linen shirt", config={"mode": "keyword"}, ) print(response.products[0].title) if __name__ == "__main__": main() ``` Locale overrides (`country`, `currency`, `language`) also go on `config` (or on the client default). See [Localization](/guides/localization) and [Locale](/concepts/locale). `keyword_search_only` is ignored. Use `mode: "keyword"` instead. # Error Handling Source: https://docs.trychannel3.com/sdk/error-handling When the library cannot connect to the API, or the API returns a non-success status code (4xx or 5xx), an error is thrown. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { try { await client.products.search({ query: "running shoes" }); } catch (err) { if (err instanceof Channel3.APIError) { console.log(err.status); // e.g. 400 console.log(err.name); // e.g. BadRequestError console.log(err.headers); // { server: 'nginx', ... } } else { throw err; } } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 from channel3_sdk.errors import APIError client = Channel3() def main(): try: client.products.search(query="running shoes") except APIError as e: print(f"Status code: {e.status_code}") print(f"Error name: {e.request.body.get('error', {}).get('type')}") print(f"Headers: {e.headers}") if __name__ == "__main__": main() ``` Error types are exposed via each SDK's error module. # Find Brands by Name Source: https://docs.trychannel3.com/sdk/find-brands Search brands ordered by relevance. Use returned IDs with `filters.brand_ids` on [Search with filters](/sdk/search-with-filters). See also [Brand](/concepts/brand) and [Brand search](/guides/brand-search). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.brands.search({ query: "lululemon" }); console.log(response.brands.map((b) => ({ id: b.id, name: b.name }))); const brandId = response.brands[0]?.id; if (brandId) { const products = await client.products.search({ query: "leggings", filters: { brand_ids: [brandId] }, }); console.log(products.products[0]?.title); } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.brands.search(query="lululemon") print([(b.id, b.name) for b in response.brands]) brand_id = response.brands[0].id products = client.products.search( query="leggings", filters={"brand_ids": [brand_id]}, ) print(products.products[0].title) if __name__ == "__main__": main() ``` To page through all brands, see [Browse the brand catalog](/sdk/browse-brands). # Find Categories Source: https://docs.trychannel3.com/sdk/find-categories Search categories by free-text query. Use returned `slug` values with `filters.category` on product search, or pass one to `categories.retrieve` for full details and attributes. See also [Category](/concepts/category) and [Attribute search](/guides/advanced-search). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.categories.search({ query: "sofas" }); console.log(response.categories.map((c) => c.slug)); const slug = response.categories[0]?.slug; if (slug) { const products = await client.products.search({ query: "sectional", filters: { category: slug }, }); console.log(products.products[0]?.title); } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.categories.search(query="sofas") print([c.slug for c in response.categories]) slug = response.categories[0].slug products = client.products.search( query="sectional", filters={"category": slug}, ) print(products.products[0].title) if __name__ == "__main__": main() ``` For full taxonomy traversal, `client.categories.list({ page, page_size, roots_only })` returns a page-numbered listing, and `client.categories.retrieve(slug)` returns one category with its children, attributes, and full path. # Find Products in Image Source: https://docs.trychannel3.com/sdk/find-products-in-image Send a scene photo to `experimental.findProductsInImage` and get back a list of matches, per product in the scene. Use this when the picture contains **more than one product** — a room, an outfit, a desk setup. For a photo of a single product, use [Search with an image URL](/sdk/image-url). See also [Find products in an image](/guides/image-search#find-products-in-an-image). Alpha. The request shape and billing may change. `filters` apply to every per-thing search. `limit` is per thing, not across the whole response. Billing is **1 credit for the call plus 1 credit per detected thing**. ## From an image URL ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const result = await client.experimental.findProductsInImage({ image_url: "https://example.com/images/living-room.jpg", }); for (const item of result.things) { console.log(item.thing, item.matches?.[0]?.title); } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): result = client.experimental.find_products_in_image( image_url="https://example.com/images/living-room.jpg", ) for item in result.things: title = item.matches[0].title if item.matches else None print(item.thing, title) if __name__ == "__main__": main() ``` ## From a base64 image Provide exactly one of `image_url` or `base64_image`. In Python the upload field is `base64image`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import fs from "node:fs"; import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const b64 = fs.readFileSync("living-room.jpg").toString("base64"); const result = await client.experimental.findProductsInImage({ base64_image: b64, }); console.log(result.things.map((item) => item.thing)); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import base64 from channel3_sdk import Channel3 client = Channel3() def main(): with open("living-room.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") result = client.experimental.find_products_in_image(base64image=b64) print([item.thing for item in result.things]) if __name__ == "__main__": main() ``` # Search with an Image URL Source: https://docs.trychannel3.com/sdk/image-url Pass `image_url` instead of (or alongside) a text `query`. See also [Image search](/guides/image-search). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.search({ image_url: "https://example.com/images/shoe.jpg", }); console.log(response.products[0]?.title); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.products.search( image_url="https://example.com/images/shoe.jpg", ) print(response.products[0].title) if __name__ == "__main__": main() ``` For uploads without a public URL, use [Search with a base64 image](/sdk/base64-image). For a scene with several products, use [Find products in image](/sdk/find-products-in-image). # Keyword Search Source: https://docs.trychannel3.com/sdk/keyword-search Pass a text `query` to `products.search`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); const results = await client.products.search({ query: "organic cotton t-shirt", limit: 20, }); for (const product of results.products) { console.log(product.title, product.offers[0]?.price); } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() results = client.products.search( query="organic cotton t-shirt", limit=20, ) for product in results.products: print(product.title, product.offers[0].price if product.offers else None) ``` Add [filters](/sdk/search-with-filters), enable keyword-only behavior with [search configuration](/sdk/configure-search), or try [image search](/sdk/image-url). # Look Up a Product by URL Source: https://docs.trychannel3.com/sdk/lookup-by-url Pass a store URL to `products.lookup` and get back the canonical product (with all merchant [offers](/concepts/offer)). The returned `product.id` works with `products.retrieve` afterward. See also [URL lookup](/guides/url-lookup). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.lookup({ url: "https://brand.com/products/linen-shirt", }); console.log(response.product?.id); console.log(response.product?.offers?.[0]?.price); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.products.lookup( url="https://brand.com/products/linen-shirt" ) print(response.product.id) print(response.product.offers[0].price) if __name__ == "__main__": main() ``` # Search with Pagination Source: https://docs.trychannel3.com/sdk/pagination Use `next_page_token` from the response to fetch the next page of results. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const page1 = await client.products.search({ query: "running shoes", }); console.log("Page 1:", page1.products.length, "products"); if (page1.next_page_token) { const page2 = await client.products.search({ query: "running shoes", page_token: page1.next_page_token, }); console.log("Page 2:", page2.products.length, "products"); } } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): page1 = client.products.search( query="running shoes", ) print(f"Page 1: {len(page1.products)} products") if page1.next_page_token: page2 = client.products.search( query="running shoes", page_token=page1.next_page_token, ) print(f"Page 2: {len(page2.products)} products") if __name__ == "__main__": main() ``` Pass the same `query` and `filters` on every page request — only `page_token` changes. # Search with Filters Source: https://docs.trychannel3.com/sdk/search-with-filters Pass a `filters` object to `products.search`. Every field is optional — combine as many as you need. | Filter | SDK page | | ---------- | ------------------------------------------- | | Price | [Price filter](/sdk/filters/price) | | Color | [Color filter](/sdk/filters/color) | | Dimensions | [Dimension filter](/sdk/filters/dimensions) | | Brand | [Brand filter](/sdk/filters/brand) | | Website | [Website filter](/sdk/filters/website) | | Attributes | [Attribute filter](/sdk/filters/attributes) | ## Combined example ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.search({ query: "running shoes", filters: { brand_ids: ["brand_id_1", "brand_id_2"], gender: "unisex", price: { min_price: 50.0, max_price: 150.0 }, availability: ["InStock", "LimitedAvailability"], }, limit: 20, }); console.log(response.products[0]?.offers[0]?.price); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.products.search( query="running shoes", filters={ "availability": ["InStock"], "price": {"min_price": 10, "max_price": 50}, "gender": "male", }, limit=20, ) print(response.products[0].offers[0].price) if __name__ == "__main__": main() ``` Price and buy links are on **offers** — see [Offer](/concepts/offer). # Find Similar Products Source: https://docs.trychannel3.com/sdk/similar-products Given a `product_id`, `products.find_similar` returns nearest neighbors in the catalog. Add `filters` to narrow by gender, brand, category, or price. See also [Similar products](/guides/similar-products). ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); async function main() { const response = await client.products.find_similar({ product_id: "2yh8WH5", filters: { gender: "female", price: { max_price: 200 }, }, limit: 10, }); console.log(response.products.map((p) => p.title)); } main(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() def main(): response = client.products.find_similar( product_id="2yh8WH5", filters={ "gender": "female", "price": {"max_price": 200}, }, limit=10, ) print([p.title for p in response.products]) if __name__ == "__main__": main() ``` # Websites Source: https://docs.trychannel3.com/sdk/websites List supported retailers, then pass their IDs to `filters.website_ids`. ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import Channel3 from "@channel3/sdk"; const client = new Channel3(); const { websites } = await client.websites.list(); const bestBuy = websites.find((site) => site.domain === "bestbuy.com"); const results = await client.products.search({ query: "coffee maker", filters: { website_ids: [bestBuy.id], }, }); console.log(results.products[0]?.offers); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} from channel3_sdk import Channel3 client = Channel3() websites = client.websites.list().websites best_buy = next(site for site in websites if site.domain == "bestbuy.com") results = client.products.search( query="coffee maker", filters={"website_ids": [best_buy.id]}, ) print(results.products[0].offers) ``` Website filters restrict the retailers whose offers can match. They are different from brands; see [Website](/concepts/website). # Channel3 API Skill Source: https://docs.trychannel3.com/skills/channel3-api The Channel3 API skill is for anyone building with Channel3 — whether you're wiring up a backend, shipping a Lovable app, or adding search to an existing codebase. It gives your coding agent (Claude Code, Cursor, Codex, and others) the API reference, examples, and integration patterns it needs to add Channel3 to your project. ## Install ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill channel3-api ``` ## Use it Once installed, paste this prompt into your agent to get started: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Help me integrate Channel3 into my project using the Channel3 API skill. ``` Ask your agent to use the Channel3 API skill whenever you need to integrate Channel3 — search, product detail, variants, localization, and more. Building on [Lovable](/lovable)? Install this skill in your terminal (Cursor, Claude Code, etc.) alongside Lovable — your terminal agent can wire up Channel3 APIs and edge functions while Lovable handles the UI. The skill does not run inside the Lovable chat box. ## Source View the skill source, open issues, or contribute on GitHub. # Product Discovery Skill Source: https://docs.trychannel3.com/skills/product-discovery The product discovery skill is for an existing agent, workflow, or harness that needs to search products, compare prices, or check availability directly during a conversation. It is not for integrating Channel3 APIs into a service — use the [Channel3 API skill](/skills/channel3-api) or [MCP server](/mcp-overview) for that. ## Install ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add channel3-ai/skills --skill product-discovery ``` ## Use it Once installed, your agent can search the Channel3 catalog on demand. Try a prompt like: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Use the product discovery skill to find running shoes under $150 and compare the top options. ``` ## Source View the skill source, open issues, or contribute on GitHub. # About the Catalog Source: https://docs.trychannel3.com/ucp/about-catalog **`ucp.trychannel3.com`** is one catalog across many online stores. It uses the same product data as the Channel3 REST API. ## Three tools | Tool | Use it when you want to... | | ---------------- | ------------------------------------------------------- | | `search_catalog` | Find products from a text query | | `lookup_catalog` | Load products you already know by id or store URL | | `get_product` | Load one product by id, with optional variant selection | ## How to shape a request Every tool takes the same outer shape: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "meta": null, "catalog": { } } ``` * **`catalog`** — your query (`query`, `ids`, `urls`, `id`, `selected`, `preferences`, `pagination`, filters, `context` for currency or country) * **`meta`** — pass `null` on Channel3 today ([details](/ucp/about-profiles#what-is-meta)) The answer lives in **`structuredContent`**: `products`, `pagination`, and sometimes `messages`. ## Channel3 extras Base UCP gives you title, price, variants, and store domain. For filters by store, richer seller info, and URL lookup, use extension **`com.trychannel3.catalog.global`**. Details: [Extension reference](/ucp/catalog-global-extension). ## Same as REST | Tool | REST equivalent | | ---------------------------- | -------------------------------------------------------------- | | `search_catalog` | `POST /v1/search` | | `lookup_catalog` with `ids` | `POST /v1/lookup` | | `lookup_catalog` with `urls` | `POST /v1/lookup` (URL) | | `get_product` | `GET /v1/products/{id}` (`option_=