API Reference
CountertopData exposes the same data shown on the search page as a small set of read-only JSON endpoints. There is no authentication — every endpoint below is a public, unauthenticatedGET request. There are no write endpoints.
Conventions
Base URL:
https://countertopdata.com/api/v1| Convention | Detail |
|---|---|
| Format | All responses are JSON (Content-Type: application/json). |
| List envelope | List endpoints return { data, count }. Endpoints that paginate (/materials) also return limit and offset, andcount there is the number of items in this response (data.length) — not a total match count across all pages. |
| Single-resource envelope | { data: { ... } } — see /materials/{id} below. |
| Errors | A non-2xx status with a JSON body of the shape { error: string }. |
| Caching | Responses set Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=600 — safe to cache at a shared/CDN layer. |
| Timestamps | Timestamp fields (e.g. lastVerifiedAt, createdAt) serialize as ISO 8601 date-time strings, or null when unset. |
GET /api/v1/materials
Search and list materials, each annotated with crystalline silica content, silica compliance tier, and per-market availability.
| Parameter | Type | Notes |
|---|---|---|
material_type | string | One of Natural Stone, Quartz, Sintered Stone/Porcelain, Mineral Surface. |
silica_compliance_tier | string | One of Zero-Silica, Low-Silica, Traditional. |
brand_id | string (UUID) | Exact match on the owning brand's id. |
q | string | Case-insensitive substring match against material name, brand name, and manufacturer code. |
lifecycle_status | string | One of Active, Discontinued. Filters the material's own rollup status directly. |
market | string | One of GB, IE, US, CA, AU. Restricts results to materials that have an availability row recorded for this market. |
market_status | string | One of Active, Discontinued. Only valid together withmarket — supplying market_status without marketreturns HTTP 400 with a JSON error body. |
limit | integer | Default 50, maximum 100. Values above 100 return HTTP 400 rather than being silently capped. |
offset | integer | Default 0. |
400 Bad Request — market_status supplied without market:
{"error":"market_status query param requires market to also be specified"}400 Bad Request — limit above 100:
{"error":"limit 500 exceeds the maximum of 100."}Each item in data:
| Field | Notes |
|---|---|
id | Opaque UUID string. |
name | Material name. |
slug | URL-safe slug, unique per brand. |
materialType | One of Natural Stone, Quartz, Sintered Stone/Porcelain, Mineral Surface. |
colorGroup | Free-text colour family; nullable. |
crystallineSilicaPctMin | Minimum crystalline silica content, percent (0–100); nullable. |
crystallineSilicaPctMax | Maximum crystalline silica content, percent (0–100); nullable. |
silicaComplianceTier | One of Zero-Silica, Low-Silica, Traditional; nullable — not every material has been tier-classified. |
manufacturerCode | Manufacturer's own SKU/code; nullable. |
sdsDocumentUrl | Link to the safety data sheet; nullable. |
originCountry | Country of origin; nullable. |
dataSource | One of Manufacturer SDS, Brand Submission, Manual Entry, Third-Party Lab, Manufacturer Price List, Distributor Price List. |
lastVerifiedAt | ISO 8601 timestamp of last verification; nullable. |
brandId | UUID of the owning brand. |
brandName | Denormalised brand name, joined in. |
brandSlug | Denormalised brand slug, joined in. |
markets | Array of per-market availability rows — see below. |
The markets array is the single most important field on this endpoint to read correctly. An empty markets array means no availability data has been recorded for that material — it is never a claim that the material is unavailable or discontinued. Coverage across GB/IE/US/CA/AU is, and will remain, incomplete. Absence of a market row is always "unknown," not "no." Any consumer of this API — human or automated — must not read a missing or empty markets entry as a negative signal.
Each entry in markets:
| Field | Notes |
|---|---|
market | One of GB, IE, US, CA, AU. |
status | Active or Discontinued, for that specific market only. |
releasedAt | Nullable string: a bare year ("2024") or year-month ("2024-06"). Day-level precision is not represented. |
discontinuedAt | Same precision rules as releasedAt; nullable. |
Worked example
Request:
curl "https://countertopdata.com/api/v1/materials?q=5141"Response:
{
"data": [
{
"id": "...",
"name": "Frosty Carrina",
"slug": "frosty-carrina",
"materialType": "Mineral Surface",
"crystallineSilicaPctMin": null,
"crystallineSilicaPctMax": 1,
"silicaComplianceTier": "Zero-Silica",
"manufacturerCode": "5141",
"dataSource": "Manufacturer Price List",
"brandName": "Caesarstone",
"brandSlug": "caesarstone",
"markets": [
{ "market": "GB", "status": "Active", "releasedAt": null, "discontinuedAt": null }
]
}
],
"count": 1,
"limit": 50,
"offset": 0
}q=5141 matches on manufacturer code here — q also matches material name and brand name. id is an opaque UUID, elided above.
GET /api/v1/materials/{id}
Fetch a single material by id. Same fields and markets-array semantics as the list endpoint above, returned as a single object rather than an array.
| Parameter | Type | Notes |
|---|---|---|
id | string (UUID), path parameter | Required. |
Response: { data: { ...same fields as /materials, markets } }.
404 Not Found when no material matches the id:
{"error":"Not found"}GET /api/v1/brands
List and search brands (manufacturers).
| Parameter | Type | Notes |
|---|---|---|
verification_status | string | One of Verified, Unverified, Pending. |
q | string | Case-insensitive substring match against brand name. |
Response: { data, count }. Each item is the full brand record —id, name, slug, website(nullable), verificationStatus, createdAt, updatedAt.
GET /api/v1/slab-specifications
List slab specification records — thickness, dimensions, weight, finish, and miter-edge compatibility. These describe individual slab batches; the same material/color can ship in multiple finishes as distinct batches, which is why this is a separate resource rather than columns on a material.
| Parameter | Type | Notes |
|---|---|---|
material_id | string (UUID) | Optional. Omit to list slab specification rows across all materials, unfiltered. |
Response: { data, count }. Each item is the full record —id, materialId, thicknessMm, lengthMm,widthMm, weightKgSqm (all nullable numbers), finishType(nullable — one of Polished, Honed, Leathered,Matt, Concrete, Rough, Natural,Ultra-Rough, Stone, Silk, Antique,Soft Matt), miterEdgeCompatible(boolean), createdAt, updatedAt.
GET /api/v1/markets
The controlled list of market codes this API and dataset support. No query parameters.
Response: { data: [{ code, name }, ...] } — currentlyGB (United Kingdom), IE (Ireland), US (United States),CA (Canada), AU (Australia).
Terms and attribution
This API is free to use for any purpose. No key or account is required — everything above is open to anyone, right now.
Attribution ("data from countertopdata.com", linked back to this site) is appreciated but not required.
The rate limits described above exist to keep the service healthy for everyone, not to gate access. If you need sustained high volume, a bulk export, or help integrating this data into your own product, get in touch at api@countertopdata.com — that's also the address for corrections.
On provenance: every material in this registry carries its own dataSource andlastVerifiedAt, so you can always see where a figure came from and how current it is. If something looks wrong, the same contact address above is the way to flag it.