Klatchit Public API
Read the marketplace catalog without an account, a key, or a signup — over plain JSON or over MCP. Everything exposed is exactly what an anonymous visitor sees on the site: publicly listed products, public prices, and purchasable offers.
Quickstart
No authentication. Send a descriptive User-Agent so we can tell your agent apart from a scraper.
curl "https://klatchit.com/api/public/v1/search?q=headphones&condition=new" \ -H "User-Agent: my-agent/1.0 (contact@example.com)"
Endpoints (v1)
Generated from the OpenAPI document, so this table is the API — not a copy of it.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/public/v1/search | None | Search the public catalog |
| GET | /api/public/v1/products/{slug} | None | Retrieve a product and its purchasable offers |
| GET | /api/public/v1/categories | None | Retrieve the active category tree |
| POST | /api/public/v1/cart-links | None | Create a cart link for a human to open |
Conventions
- Success is
{ data, meta }; errors are{ error: { type, code, message, param?, request_id } }. - All money is integer cents, USD (a field named
*_centsis never a decimal). - Search pages with
page/page_size; the effective page size is echoed inmeta.page_size(oversized values are clamped, not rejected). - A product is addressed by its slug — the same slug as its canonical page
/p/{slug}. An offer is addressed by itslisting_id, which identifies one seller's offer, not the product. - Inventory depth is never exposed; an offer reports a boolean
in_stock. - Reads are shared-cacheable (
Cache-Control: public, s-maxage=60, 300s for the category tree). Please reuse rather than re-fetch.
Rate limits
Limits are per client IP (first X-Forwarded-For hop; no key, so there is nothing else to key on). Each is a token bucket — a burst up to the capacity, then a steady refill:
| Bucket | Burst | Refill |
|---|---|---|
| Each catalog endpoint | 30 requests | 5 / second |
| POST /cart-links | 10 requests | 1 / second |
| MCP (/api/mcp/mcp) | 30 requests | 5 / second |
Buckets are counted per endpoint, so search and product lookups do not compete. Over the limit you get 429 with a Retry-After header in seconds — honour it and back off. These numbers are current operating values, not a contractual guarantee.
MCP server
The same catalog is available over the Model Context Protocol, so a model can call it directly instead of you writing HTTP glue.
- Endpoint:
https://klatchit.com/api/mcp/mcp(streamable HTTP,POSTonly). - Stateless — a fresh server instance per request, no session id to carry, no
GETstream to hold open. AGETis answered405on purpose. - No authentication, no OAuth, no cookie is read.
- Tools:
search_products,get_product,get_categories(all read-only) pluscreate_cart_link.
Connect from Claude
Settings → Connectors → Add custom connector, and paste the URL below. There is no auth step to complete.
https://klatchit.com/api/mcp/mcp
Connect from the MCP inspector or a client config
npx @modelcontextprotocol/inspector # transport: Streamable HTTP # URL: https://klatchit.com/api/mcp/mcp
{
"mcpServers": {
"klatchit": {
"type": "http",
"url": "https://klatchit.com/api/mcp/mcp"
}
}
}Cart links: the handoff to a human
There is no way to buy through this API, by design. An agent picks the offers; a person reviews and pays. The bridge between the two is a cart link.
- The agent finds offers (
GET /search→GET /products/{slug}) and collects each chosen offer'slisting_id. - It calls
POST /cart-links, which mints a signed link. No cart is created, no stock is held, no payment is taken, and no order exists. - It hands the
urlto its user, who opens it in their own browser. The items land in their cart, where they review, edit, and check out themselves. - The link expires after 7 days, and price and availability are re-checked when it is opened — so quote prices as current, never as guaranteed.
Worked example
curl -X POST "https://klatchit.com/api/public/v1/cart-links" \
-H "Content-Type: application/json" \
-H "User-Agent: my-agent/1.0 (contact@example.com)" \
-d '{
"items": [
{ "listing_id": "0198f2b1-4c7e-7a10-9d3e-5f6a7b8c9d01", "quantity": 1 },
{ "listing_id": "0198f2b1-9a2d-7bd4-8e11-2c3d4e5f6a72", "quantity": 2 }
]
}'{
"data": {
"url": "https://klatchit.com/cart/restore?t=<signed-token>",
"expires_at": "2026-08-04T17:05:11.000Z",
"accepted": 1,
"rejected": [
{ "listing_id": "0198f2b1-9a2d-7bd4-8e11-2c3d4e5f6a72", "reason": "unavailable" }
]
},
"meta": { "request_id": "req_..." }
}- 1–20 lines, quantity 1–99. A repeated
listing_idhas its quantities summed. - Lines that are not purchasable right now are dropped and reported in
rejectedwith a coarse reason (unavailableorinsufficient_stock) — a precise reason would leak non-public seller state. Checkacceptedbefore you claim success. - If nothing is purchasable you get a
400whose error object carries the samerejectedlist. - Responses are
no-store: a token is minted per request and must never be served from a shared cache. - Present the result as “here is a link to your cart” — never as a completed order, a reservation, or a hold.
What is not exposed
Seller-internal data has no public endpoint: inventory counts, seller contact details, orders, payouts, messages, and anything behind a login. Every response is projected through the same serializers the anonymous storefront uses, so an agent can see exactly what a logged-out browser can see and nothing more. To manage your own store programmatically, use the authenticated Seller API instead.