Public catalogue API
The machine-readable interface to KEW’s published catalogue. Intended for agents and integrators; the same public data the KEW catalogue renders.
Read-only OpenAPI contract · Product sitemap · Agent discovery notes
You can search products and prepare a list for a quote without an account or a paid integration. This does not create a quotation or submit an enquiry. The customer completes the quote basket in their browser, or contacts KEW.
No authentication. No rate-limit headers are published yet — treat the service as politely rate-limited and do not poll aggressively.
Base URL: https://kew-erp-api-dxxg3763qa-ew.a.run.app
What is visible, and what is not
A product appears here only when KEW has explicitly published it. Publication is a deliberate staff decision recorded against the SKU; it is not inferred from stock levels, from legacy import flags, or from the product simply existing.
Consequences worth understanding before you build against this:
- Absence is not evidence KEW cannot supply the line. An unpublished SKU may still be stocked. Ask for a quote rather than concluding it is unavailable.
- The catalogue can be empty. It legitimately returns
total: 0when nothing has been published yet. That is not an outage.
The following are never returned by any public endpoint, by policy: cost, sell price, supplier identity, exact stock quantities, and warehouse bin locations.
GET /catalog/search
Search or browse published products.
Parameters
| Name | Type | Default | Notes |
|---|---|---|---|
q |
string, ≤ 120 chars | "" |
Free text. Empty browses everything published. Separate words may match across SKU, name, description, brand, manufacturer, MPN, GTIN, category and public search keywords. Quote a phrase to require it together. |
category_code |
string, ≤ 64 chars | — | Restrict to one category. Codes come from GET /catalog/categories. |
brand |
string, ≤ 64 chars | — | Restrict to one published brand using a normalized exact match (trimmed, collapsed whitespace, case-insensitive). This is not a free-text description search; a product that merely mentions the brand in its copy is excluded. |
selling_unit |
Published selling unit; preserve it exactly when preparing a quote-basket line. Do not infer pack, reel or metre conversions. | ||
stock_status |
in_stock | low_stock | order_in |
— | Restrict to a coarse public stock band. Applied before total, limit and offset; products whose publication omits stock_status remain excluded from this filtered view. |
sort |
name | sku | code |
sku |
|
limit |
integer, 1–100 | 10 |
|
offset |
integer, ≥ 0 | 0 |
For example, 3 core finds a published product labelled 3-core because both
words match; "3 core" requires that exact phrase. With very few exact results,
multiword searches also try a conservative spelling match on public product
text. Category and stock filters still apply, and the endpoint never searches
unpublished products or staff-only supplier, cost, or location fields. Queries
with more than 12 separate words return 422.
Response
{
"total": 0,
"limit": 10,
"offset": 0,
"items": []
}
total is the full count matching the query, not the page size — use it to
paginate with offset.
Each entry in items carries the public-safe field set. Search omits unset fields;
detail and category responses may return optional fields as null. Treat every
field except sku as optional and handle both missing and null values:
| Field | Notes |
|---|---|
sku |
KEW’s product code. Always present. Stable identifier. |
name, description |
Human-readable copy. |
brand, manufacturer, model, mpn, gtin |
Product identity for cross-referencing. |
image_url, datasheet_url |
May be absolute or site-relative. Resolve relative values against https://kew.ie/, not the API server. |
category, category_code |
Human-readable name and the code for filtering. |
technical_description, bullet_features, layperson_description |
|
selling_unit |
Published selling unit; preserve it exactly when preparing a quote-basket line. Do not infer pack, reel or metre conversions. |
stock_status |
in_stock | low_stock | order_in. See below. |
model is an optional field in the response schema but is not currently selectable
through the standard publication workflow. Do not rely on it being populated;
use a verified mpn and the published description when identifying a product.
Stock bands
Availability is a band, never a count:
| Value | Meaning |
|---|---|
in_stock |
More than 5 units on hand |
low_stock |
1–5 units on hand |
order_in |
None on hand; KEW orders it in |
The band reflects the last stocktake for that line, not a real-time reservation. It is not a guarantee of allocation — confirm on a quote.
Examples
Search for a product (read-only):
curl --get 'https://kew-erp-api-dxxg3763qa-ew.a.run.app/catalog/search' \
--data-urlencode 'q=LED high bay' \
--data-urlencode 'stock_status=in_stock' \
--data-urlencode 'limit=10'
Look up a specific SKU:
curl --get 'https://kew-erp-api-dxxg3763qa-ew.a.run.app/catalog/product' \
--data-urlencode 'sku=UFO200W'
Build customer-facing links and resolve media safely:
const productPage = new URL('/p', 'https://kew.ie');
productPage.searchParams.set('sku', product.sku);
const imageUrl = product.image_url
? new URL(product.image_url, 'https://kew.ie/').href
: undefined;
Keep the SKU exactly as returned, including punctuation and slashes. Do not construct a different SKU from the manufacturer part number. Check the product description and ask KEW about pack sizes or ordering units if they are unclear.
GET /catalog/categories
Published categories with product counts. Only categories containing at least one published product appear.
{ "total": 0, "items": [] }
Each item has code, name and product_count. Use code with the
category_code parameter on search.
GET /catalog/p/{sku} and GET /catalog/product?sku=...
Detail for a single published SKU. Returns the same public-safe field set as a search result.
Use the query-parameter form for SKUs containing / — several KEW codes do
(for example PFE/10/100), and percent-encoding them into the path is fragile
across clients.
Returns 404 when the SKU is unknown or not published. The two are
deliberately indistinguishable: whether an unpublished SKU exists is not public
information.
GET /catalog/c/{category_code}
Published products in one category. Supports limit and offset with the same
bounds as search.
POST /catalog/quote-requests
Quote lines submit SKU, quantity and an optional note. Do not add a client-supplied selling_unit field to this request: unknown fields are rejected. The server reads the current ERP unit for each published product and snapshots it into the saved enquiry, staff context and email when that unit is selected for public publication. Missing or unselected units remain missing; no pack, reel or metre conversion is inferred.
This is the existing browser quote-basket submission endpoint. It requires Cloudflare Turnstile verification and is not an unattended agent tool. Publishing these documents does not remove that requirement or grant write access.
An agent should prepare a list containing the exact SKU, product link, requested quantity and any customer notes, then hand it to the customer. The customer can open the catalogue, add the products to their quote basket and submit it, or contact KEW. Opening a product link does not automatically add the item to a basket or submit it.
The browser endpoint validates published SKUs, records the enquiry and attempts
to send an acknowledgement email. Its response distinguishes email_status: "sent" from "failed". A saved request with a failed email is still saved: do
not blindly resubmit and create duplicates. The customer should retain the
request reference and contact KEW if confirmation is unclear.
Every quote request is reviewed by a person before a price is issued. Expect a human-in-the-loop reply, not an instant automated price.
Errors
| Status | Meaning |
|---|---|
404 |
Unknown or unpublished SKU/category — or the public catalogue is disabled. |
422 |
Invalid parameters, or a quote request naming a SKU that is not published. |
429 |
Request frequency exceeded a service limit. Honour Retry-After when present and back off. |
5xx |
Temporary service failure. Retry reads with bounded backoff; never treat an error as an empty catalogue. |
The catalogue sits behind a feature flag and is fail-closed: when disabled,
catalogue routes return 404. If all reads return 404, the catalogue may be
disabled; check the base URL and contact KEW rather than concluding every
product is unavailable.
Guidance for agents
- Do not infer a price from any field. There is none, by design.
- Prefer this API over scraping HTML. It is stable and returns the same data the pages render.
- Do not treat absence as unavailability — see the visibility note above.
- Paginate with
totalandoffset. Do not assumelimitbounds the result set; it bounds the page. - Use
sort=skufor a full crawl. Request at most 100 items per page, deduplicate by SKU and advance the offset by the page size. The catalogue can change during pagination; refresh later if the total changes. Stop and investigate repeated empty pages before reaching the reported total. - Be a considerate crawler. Fetch sequentially, cache successful reads and use bounded retries with backoff. Do not repeatedly crawl the entire catalogue for a single customer enquiry.
- Separate facts from suggestions. Do not invent specifications, compatibility, certifications, pack units, delivery promises or prices when fields are missing.
- Keep the customer in control. Ask them to confirm the item list and quantities. A prepared list is not a submitted enquiry, a priced quote, an order or a stock reservation.
- Protect contact details. Do not place names, email addresses, phone numbers or account details in public URLs or search queries.
Product pages render a selected layperson_description as the lead and a distinct
description as longer selection guidance, including in server-rendered HTML.
Without selected short copy, the description remains the lead and the empty or
duplicate second paragraph is hidden. Public-field selection remains authoritative.
Release the API renderer before the updated landing template; the renderer also
accepts the previous template.