channel3-api

Installation
SKILL.md

Channel3 API Integration Guide

Channel3 is a universal product catalog API. Pick the endpoint(s) that match the developer's input.

Developer has... Use
Free-text query, image URL, or both POST /v1/search
Channel3 product_id and wants similar items POST /v1/similar
Channel3 product_id and wants full details GET /v1/products/{id}
A merchant URL and wants the canonical product POST /v1/lookup
A product_id and wants price-change alerts POST /v0/price-tracking/start
A free-text term and wants matching category slugs GET /v1/categories/search

Base URL: https://api.trychannel3.com
Auth: x-api-key header
Full docs: docs.trychannel3.com (SDK setup, per-endpoint refs, error handling, retries, async usage)
Offline quick reference: references/api-reference.md

Anti-patterns (read first)

  • Don't use /v1/similar with a free-text query or an image. Similar takes a Channel3 product_id, not a query. If the user describes a product or has an image, that's /v1/search (query and/or image_url).
  • Don't reach for /v1/lookup to seed /v1/similar when a /v1/search by title would do. Lookup takes seconds and can fail on uncatalogued URLs. The intended flow is search → grab product.idsimilar. Lookup-then-similar can be used if needed but is not an optimal flow for the majority of cases.
  • Locale (country / currency) constrains which merchant offers come back. Pan-region storefronts can omit country and just set currency: "EUR". Default is en / US / USD.

Quick start

npm install @channel3/sdk           # TypeScript
pip install channel3_sdk            # Python

Set CHANNEL3_API_KEY in the environment (free key at trychannel3.com). Then the minimum end-to-end call:

import { Channel3 } from '@channel3/sdk';

const client = new Channel3({ apiKey: process.env.CHANNEL3_API_KEY });
const { products } = await client.products.search({ query: 'running shoes', limit: 5 });

Locale defaults can be set client-wide via constructor (new Channel3({ country: 'GB', currency: 'GBP' })) or CHANNEL3_LANGUAGE / CHANNEL3_COUNTRY / CHANNEL3_CURRENCY env vars; per-call config.country / config.currency / config.language always wins. For async clients, error classes, retries, timeouts, and logging, see docs.trychannel3.com/sdk.

Endpoints

Search — POST /v1/search

Text, image, or text+image search. Returns a SearchResponse with paginated ProductDetail[] and a next_page_token.

const response = await client.products.search({
  query: 'running shoes under $100',
  filters: { price: { max_price: 100 }, gender: 'male' },
  limit: 10,
});
response = client.products.search(
    query="running shoes under $100",
    filters={"price": {"max_price": 100}, "gender": "male"},
    limit=10,
)

Per-call locale override: config: { country: 'GB', currency: 'GBP' }. Pure keyword matching (skip semantic search): config: { keyword_search_only: true } — incompatible with image input. Full filter shape in references/api-reference.md; full per-endpoint schema at docs.trychannel3.com/api-reference.

For raw HTTP / non-SDK callers:

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":{"price":{"max_price":100}},"limit":10}'

Similar — POST /v1/similar

"More like this" from a Channel3 product_id you already have. Almost always seeded from a previous /v1/search response. Returns the same SearchResponse shape; the source product is excluded.

const search = await client.products.search({ query: 'red leather jacket', limit: 5 });
const seedId = search.products[0].id;

const similar = await client.products.find_similar({
  product_id: seedId,
  filters: { gender: 'female', price: { max_price: 200 } },
  limit: 10,
});
search = client.products.search(query="red leather jacket", limit=5)
seed_id = search.products[0].id

similar = client.products.find_similar(
    product_id=seed_id,
    filters={"gender": "female", "price": {"max_price": 200}},
    limit=10,
)

Filters are recommended to keep results in the same slice (gender, brand, category, price). Returns 404 if the product isn't in the catalog yet — fall back to /v1/search by title.

Lookup — POST /v1/lookup

Resolve a merchant URL to the canonical Channel3 Product. Use this only when the developer's only handle on a product is a URL (e.g. a user-pasted link).

const { product } = await client.products.lookup({
  url: 'https://merchant.com/products/red-jacket',
});
result = client.products.lookup(url="https://merchant.com/products/red-jacket")
product = result.product

Latency: typically 2–10 seconds for uncached URLs (real-time extraction), sub-second for cached. Returns 422 for non-product pages (category listings, search results, homepages) and 504 on timeout. max_staleness_hours (default 3) bounds cache freshness. Once you have product.id, use it with client.products.retrieve() or client.products.find_similar().

Product Details — GET /v1/products/{product_id}

Full ProductDetail for a known product_id.

const product = await client.products.retrieve('prod_abc123', {
  country: 'GB',
  currency: 'GBP',
});

Python is the same shape: client.products.retrieve("prod_abc123", country="GB", currency="GBP"). Optional query params: website_ids, language, country, currency. Returns 404 when the product has no merchant offer in the requested locale — seed product_id from a /v1/search call run under the same locale, or omit the locale to fall back to default.

Price Tracking — /v0/price-tracking/...

  • client.priceTracking.start({ canonical_product_id }) — start tracking
  • client.priceTracking.stop({ canonical_product_id }) — stop tracking
  • client.priceTracking.retrieveHistory(id, { days }) — up to 30 days; returns current_price, min/max/mean/std_dev, current_status (low / typical / high)
  • client.priceTracking.listSubscriptions() — cursor-paginated, supports for await iteration
await client.priceTracking.start({ canonical_product_id: 'prod_abc123' });
const history = await client.priceTracking.retrieveHistory('prod_abc123', { days: 30 });
console.log(history.statistics?.current_price, history.statistics?.current_status);

Brands and Websites — /v1/brands*, /v0/websites

Lookup helpers, mostly used to obtain IDs for search filters.

  • client.brands.search({ query, limit? }) — find brands by name; returns up to limit matches ordered by relevance (default 5, max 20)
  • client.brands.retrieve(brandId) — by ID
  • client.brands.list() — cursor-paginated, supports for await iteration. Iterating to exhaustion walks the entire brand catalog (thousands of brands, many pages of API calls); always break early or use client.brands.search when you just need one brand.
  • client.websites.retrieve({ query: 'nike.com' }) — find a retailer
// Find a brand ID for filtering
const { brands } = await client.brands.search({ query: 'Nike', limit: 5 });
const brandId = brands[0]?.id;  // top match — inspect `brands` to disambiguate when multiple match

Categories — /v1/categories*

Discover the category slugs you can pass to SearchFilters.category_ids / exclude_category_ids. Slugs are stable URL-friendly identifiers (e.g. shoes, sofas, handbags) — prefer them over internal IDs. The taxonomy doesn't have a leaf for every conceivable subcategory, and unknown slugs are silently dropped, so always discover real slugs with client.categories.search rather than guessing.

  • client.categories.search({ query, limit? }) — free-text → CategorySummary[] (limit 1–20, default 5)
  • client.categories.list({ roots_only?, page?, page_size? }) — paginated browse, roots first (page_size 1–100, default 20)
  • client.categories.retrieve(slug) — full Category with description, attributes, direct children, and root-to-self path
const { categories } = await client.categories.search({ query: 'running shoes', limit: 5 });
const slug = categories[0].slug;

const { products } = await client.products.search({
  query: 'lightweight trainers',
  filters: { category_ids: [slug] },
});
result = client.categories.search(query="running shoes", limit=5)
slug = result.categories[0].slug

response = client.products.search(
    query="lightweight trainers",
    filters={"category_ids": [slug]},
)

exclude_category_ids excludes the category and all its descendants.

Affiliate links

Every ProductOffer.url in a response is an affiliate-tracked link. Surface them as the buy buttons in any UI — sales driven through these URLs earn commission with no additional setup. Use offer.domain to identify the retailer and offer.max_commission_rate to compare earning potential across merchants.

Locale codes

  • Languages: en, de, fr, it, es, nl, sv, fi, pt, cs
  • Countries: US, GB, EU, AU, CA, IE, DE, AT, FR, BE, IT, ES, NL, SE, FI, PT, CZ
  • Currencies: USD, CAD, AUD, GBP, EUR, SEK, CZK

When country is set alone, the server infers currency (GB → GBP) and language (GB → en). When all three are unset, defaults are en / US / USD.

When to use the MCP instead

If the goal is to give an existing AI agent product superpowers without writing code, the Channel3 MCP (https://mcp.trychannel3.com/, free tier, append ?apiKey=<key> for pay-as-you-go) is lower friction — see docs.trychannel3.com/mcp-overview.

When stuck

Related skills

More from channel3-ai/skills

Installs
18
GitHub Stars
3
First Seen
Feb 24, 2026
Security Audits