Google My Business performance and call history for Kajaria dealers. Look up stores by dealer SAP code or employee code, then pull day wise GMB performance for any store over any date range.
Reports are fetched in two steps. First find which stores you are dealing with, then pull the detail for one store at a time.
storelookup from step 1 back, with the same or a narrower date range, to get day wise GMB performance or the call log.
Step 1 returns storelookup rather than the raw internal store ID. It is an AES-encrypted, signed value — send it back exactly as received, without URL-encoding or trimming it. An edited value is rejected rather than resolved to another store.
The same store always produces the same storelookup, so you can safely store it, cache it, or compare two responses to see whether they refer to the same store.
from_date and to_date are required on every endpoint, in YYYY-MM-DD format.2026-07-01 to 2026-07-31 covers the whole of July."message": "No record found".https://dashboard.kajariaceramics.com/api/gmb/ and accept POST with a JSON body.The same key system as the Product Filter API. One key identifies one integration; requests without a valid key are rejected before any database work happens.
X-API-KEY: kj_live_xxxxxxxxxxxxxxxx
Where custom headers are not possible, send "api_key" in the JSON body instead. It works the same way, but the key ends up in request logs more often.
A SAP code may cover more than one store. This returns all of them, including stores that have no GMB data yet.
| Field | Type | Required | Description |
|---|---|---|---|
| sap_code | string | Required | Dealer SAP code |
| from_date | string | Required | Start of range, YYYY-MM-DD |
| to_date | string | Required | End of range, inclusive |
{
"sap_code": "1000234",
"from_date": "2026-07-01",
"to_date": "2026-07-31"
}
{
"code": 200,
"status": true,
"user": "dealer_portal",
"sap_code": "1000234",
"from_date": "2026-07-01",
"to_date": "2026-07-31",
"returned": 2,
"data": [
{
"storelookup": "Zk9tR2h4...",
"dealership_name": "Kajaria World Rohini",
"city": "New Delhi",
"state": "Delhi",
"has_data": true,
"message": "",
"days_with_data": 31,
"totals": {
"total_search": 18420,
"website_clicks": 342,
"call_clicks": 210,
"direction_requests": 985,
"business_bookings": 0
}
},
{
"storelookup": "Qm5wT2p2...",
"dealership_name": "Kajaria World Pitampura",
"city": "New Delhi",
"state": "Delhi",
"has_data": false,
"message": "No record found",
"days_with_data": 0,
"totals": {
"total_search": 0,
"website_clicks": 0,
"call_clicks": 0,
"direction_requests": 0,
"business_bookings": 0
}
}
]
}
| Field | Type | Description |
|---|---|---|
| storelookup | string | Encrypted store reference. Pass this to the day wise and call endpoints. |
| has_data | boolean | False when the store has no GMB rows in this period |
| message | string | Empty, or No record found when has_data is false |
| days_with_data | integer | Number of days in the range that have figures |
| totals | object | Metrics summed across the whole range |
data comes back as an empty array with "message": "No record found" at the top level. That is a 200, not an error.Same shape as the dealer lookup, resolved through the employee-to-store mapping. Adds the employee's details and a grand total across all their stores.
| Field | Type | Required | Description |
|---|---|---|---|
| emp_code | string | Required | Employee code |
| from_date | string | Required | Start of range, YYYY-MM-DD |
| to_date | string | Required | End of range, inclusive |
Each entry in data carries the same fields as the dealer lookup, plus sap_code. Two extra blocks appear at the top level:
{
"code": 200,
"status": true,
"employee": {
"emp_code": "E12345",
"name": "Employee Name",
"sales_off": "Delhi",
"role": "Area Sales Manager"
},
"from_date": "2026-07-01",
"to_date": "2026-07-31",
"returned": 14,
"message": "",
"grand_totals": {
"total_search": 204118,
"website_clicks": 3912,
"call_clicks": 2440,
"direction_requests": 11002,
"business_bookings": 0
},
"data": [ /* one entry per store */ ]
}
emp_code that is not in the employee table returns 404 with "Employee not found". This differs from the dealer lookup, which returns 200 with an empty list — an unknown employee is a mistake worth surfacing, whereas a dealer with no stores is a normal result.One row per day for a single store. Returns the period totals alongside the rows, so a summary panel needs no second call.
| Field | Type | Required | Description |
|---|---|---|---|
| storelookup | string | Required | Store reference from step 1, unmodified |
| from_date | string | Required | Start of range, YYYY-MM-DD |
| to_date | string | Required | End of range, inclusive |
| page | integer | Optional | 1-based. Defaults to 1. |
| limit | integer | Optional | Rows per page. Defaults to 31, capped at 366. |
{
"storelookup": "Zk9tR2h4...",
"from_date": "2026-07-01",
"to_date": "2026-07-31",
"page": 1,
"limit": 31
}
{
"code": 200,
"status": true,
"dealer": {
"storelookup": "Zk9tR2h4...",
"sap_code": "1000234",
"dealership_name": "Kajaria World Rohini",
"city": "New Delhi",
"state": "Delhi"
},
"page": 1,
"limit": 31,
"returned": 31,
"total": 31,
"total_pages": 1,
"has_more": false,
"totals": {
"total_search": 18420,
"website_clicks": 342,
"call_clicks": 210,
"direction_requests": 985,
"business_bookings": 0
},
"data": [
{
"performance_date": "2026-07-01",
"total_search": 612,
"website_clicks": 11,
"call_clicks": 7,
"direction_requests": 33,
"business_bookings": 0,
"desktop_search_impressions": 88,
"mobile_search_impressions": 402,
"desktop_maps_impressions": 24,
"mobile_maps_impressions": 98
}
]
}
totals always covers the full date range, not just the current page. total_search is the sum of the four impression columns, matching the figure shown on the dealer dashboard.One row per call for a single store. This is where large result sets appear — a busy store over a long range can run to thousands of rows, so page through it.
| Field | Type | Required | Description |
|---|---|---|---|
| storelookup | string | Required | Store reference from step 1, unmodified |
| from_date | string | Required | Start of range, YYYY-MM-DD |
| to_date | string | Required | End of range, inclusive of the whole day |
| substatus | string | Optional | Filter by call result, e.g. Answered, Missed |
| page | integer | Optional | 1-based. Defaults to 1. |
| limit | integer | Optional | Rows per page. Defaults to 50, capped at 500. |
{
"code": 200,
"status": true,
"dealer": { /* same block as day wise */ },
"page": 1,
"limit": 50,
"returned": 50,
"total": 1284,
"total_pages": 26,
"has_more": true,
"summary": {
"total_calls": 1284,
"connected_calls": 903,
"missed_calls": 381,
"total_minutes": 2140.75
},
"data": [
{
"call_log_id": "CL889231",
"caller_number": "+919812345678",
"call_start_time": "2026-07-31 17:42:11",
"call_duration_minutes": 2.35,
"did_number": "+911140001234",
"call_event_type": "Inbound",
"substatus": "Answered",
"total_calls_from_caller": 3,
"prev_calls_count": 2,
"recording_url": "https://dashboard.kajariaceramics.com/gmb/audio/2050_CL889231.mp3"
}
]
}
summary covers the whole range and respects the substatus filter if you sent one.connected_calls counts Answered; missed_calls counts Missed and Offline together, matching the dashboard.recording_url is an empty string when no recording was captured or the download has not run yet.Errors return the same JSON shape with status: false and a message.
| Code | Message | What it means |
|---|---|---|
| 400 | Invalid JSON | Body missing or malformed |
| 400 | from_date and to_date are required | One or both dates missing |
| 400 | Dates must be in YYYY-MM-DD format | Includes real-looking but invalid dates such as 2026-02-30 |
| 400 | from_date cannot be later than to_date | Range is the wrong way round |
| 400 | sap_code is required | Dealer lookup called without a SAP code |
| 400 | emp_code is required | Employee lookup called without an employee code |
| 400 | storelookup is required | Step 2 called without a store reference |
| 400 | Invalid storelookup | Reference was edited, truncated, or issued under a different key |
| 401 | API key missing | No key in the header or the body |
| 401 | Invalid API key | Key not recognised or revoked |
| 404 | Employee not found | No employee with that code |
| 405 | Only POST allowed | Request used GET or another method |
| 500 | Internal Server Error | Failure on the server side |
200 with "message": "No record found", zeroed totals and an empty data array. Check total or has_data rather than treating an empty list as a failure.Two calls, end to end: find a dealer's stores for July, then pull the day wise GMB performance for the first one.
curl -X POST https://dashboard.kajariaceramics.com/api/gmb/dealer \ -H "Content-Type: application/json" \ -H "X-API-KEY: kj_live_xxxxxxxxxxxxxxxx" \ -d '{"sap_code":"1000234","from_date":"2026-07-01","to_date":"2026-07-31"}'
# storelookup copied from the step 1 response curl -X POST https://dashboard.kajariaceramics.com/api/gmb/daywise \ -H "Content-Type: application/json" \ -H "X-API-KEY: kj_live_xxxxxxxxxxxxxxxx" \ -d '{"storelookup":"Zk9tR2h4...","from_date":"2026-07-01","to_date":"2026-07-31"}'
let page = 1, all = []; while (true) { const res = await fetch("https://dashboard.kajariaceramics.com/api/gmb/calls", { method: "POST", headers: { "Content-Type": "application/json", "X-API-KEY": process.env.KAJARIA_API_KEY }, body: JSON.stringify({ storelookup: storeRef, from_date: "2026-07-01", to_date: "2026-07-31", page: page, limit: 500 }) }); const json = await res.json(); if (!json.status) throw new Error(json.message); all.push(...json.data); if (!json.has_more) break; page++; }