Returns active products matching any combination of colour, finish, series, size, surface, range and texture. Results are paginated. Every request needs an API key.
Send a POST request with a JSON body. An empty body {} is valid and returns the first page unfiltered.
POST https://dashboard.kajariaceramics.com/api/products/filter Content-Type: application/json X-API-KEY: kj_live_xxxxxxxxxxxxxxxx
The key identifies which system is calling. Requests without a valid key are rejected before any database work happens.
X-API-KEY: kj_live_xxxxxxxxxxxxxxxx
Use this only where you cannot set custom headers. It works the same way, but the key ends up in request logs more often.
{
"api_key": "kj_live_xxxxxxxxxxxxxxxx",
"color": "Grey"
}
All filter fields are optional. When several are supplied, a product must match every one of them.
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| color | string | Optional | Product colour name | "Grey" |
| finish | string | Optional | Finish name | "Matt" |
| series | string | Optional | Series name | "Paris" |
| size | string | Optional | Size code | "600x600" |
| surface | string | Optional | Surface type | "Glossy" |
| range | string | Optional | Product range | "Luxury" |
| texture | string | Optional | Texture / look and feel | "Stone" |
| page | integer | Optional | Page number, 1-based. Defaults to 1. | 2 |
| limit | integer | Optional | Products per page. Defaults to 20. | 50 |
| api_key | string | Conditional | Only if the key is not sent as a header | "kj_live_…" |
Values are compared against the master tables as written — "Matt" matches, "matt " with a trailing space does not. Pull the options from your existing master lists rather than typing them by hand, and an unknown value returns an empty data array rather than an error.
page and limit are forced to a minimum of 1, so zero and negative values are silently corrected.total is the full match count for your filters. Use it to draw page numbers or a result count.total_pages saves you the division. Stop when has_more is false.page beyond the last one returns an empty data array, not an error.total. It is cheap at current catalogue size, but avoid calling the API in a tight loop just to read the count.Always Content-Type: application/json. The transport status is 200 on success; check the code and status fields in the body for the API's own result.
{
"code": 200,
"status": true,
"user": "mobile_app",
"page": 1,
"limit": 20,
"returned": 20,
"total": 143,
"total_pages": 8,
"has_more": true,
"data": [
{
"id": 123,
"sku": "SKU001",
"name": "Product Name",
"description": "Product description",
"status": 1,
"images": [
"https://www.kajariaceramics.com/storage/image1.jpg",
"https://www.kajariaceramics.com/storage/image2.jpg"
]
}
]
}
| Field | Type | Description |
|---|---|---|
| code | integer | Result code, mirrors HTTP conventions |
| status | boolean | True when the call succeeded |
| user | string | Name of the integration the key belongs to. Useful when debugging which key a request used. |
| page | integer | Page that was served |
| limit | integer | Page size that was applied |
| returned | integer | Number of products in data on this page |
| total | integer | All active products matching the filters, ignoring pagination |
| total_pages | integer | total divided by limit, rounded up |
| has_more | boolean | True when at least one further page exists |
| data | array | Product objects |
| Field | Type | Description |
|---|---|---|
| id | integer | Internal product ID |
| sku | string | SKU code |
| name | string | Product name |
| description | string | Product description |
| status | integer | Always 1 — only active products are returned |
| images | array | Absolute image URLs in display order. Empty array when a product has no images. |
Errors return the same JSON shape with status: false and a message.
| Code | Message | What it means | How to fix it |
|---|---|---|---|
| 400 | Invalid JSON | Body missing or malformed | Send valid JSON, and {} rather than an empty body |
| 401 | API key missing | No key in the header or the body | Add the X-API-KEY header |
| 401 | Invalid API key | Key not recognised or revoked | Check for stray whitespace, then request a fresh key |
| 405 | Only POST allowed | Request used GET or another method | Switch to POST |
| 500 | Internal Server Error | Failure on the server side | Report it with the timestamp and the request body |
// 401 response { "code": 401, "status": false, "message": "Invalid API key" }
/api/filter-products.php directly to confirm the server is reachable — a 405 Only POST allowed from a browser is the expected healthy response there.Same request in four clients. Replace the placeholder with the key issued to your integration.
curl -X POST https://dashboard.kajariaceramics.com/api/products/filter \ -H "Content-Type: application/json" \ -H "X-API-KEY: kj_live_xxxxxxxxxxxxxxxx" \ -d '{"color":"Grey","finish":"Matt","page":1,"limit":20}'
const res = await fetch("https://dashboard.kajariaceramics.com/api/products/filter", { method: "POST", headers: { "Content-Type": "application/json", "X-API-KEY": process.env.KAJARIA_API_KEY }, body: JSON.stringify({ color: "Grey", page: 1, limit: 20 }) }); const json = await res.json(); if (!json.status) throw new Error(json.message); console.log(json.data);
$ch = curl_init("https://dashboard.kajariaceramics.com/api/products/filter"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "X-API-KEY: " . getenv("KAJARIA_API_KEY") ], CURLOPT_POSTFIELDS => json_encode(["color" => "Grey", "limit" => 20]) ]); $response = json_decode(curl_exec($ch), true); curl_close($ch);
import os, requests r = requests.post( "https://dashboard.kajariaceramics.com/api/products/filter", headers={"X-API-KEY": os.environ["KAJARIA_API_KEY"]}, json={"series": "Paris", "page": 1, "limit": 50}, timeout=15, ) payload = r.json()
// first 20 products, no filters {} // one filter { "color": "White" } // two filters combined { "color": "Grey", "finish": "Matt" } // page 2, fifty per page { "page": 2, "limit": 50 }