REST API
Search Console
Read organic Google Search keyword stats — clicks (as visitors), impressions, CTR, average position, and estimated revenue — for a site's connected Google Search Console property. A site is addressed by {siteKey} (UUID or domain). Requires Authorization: Bearer {token}. See Conventions for the base URL, headers, date-range, and filter conventions shared by every endpoint.
There is no API endpoint to connect Search Console — that OAuth flow is web-only (the team connects a property from the dashboard). This endpoint only reads keyword stats for a property that is already connected.
Search keywords
GET /api/sites/{siteKey}/stats/search-keywords
Route name: api.sites.stats.search-keywords.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
siteKey |
string | Yes | Site UUID or domain. |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
range |
string | No | Preset date range (see Date ranges). Default today. |
from |
string | No | Custom range start, Y-m-d. Requires to. |
to |
string | No | Custom range end, Y-m-d. Requires from. |
limit |
int | No | Rows per Search Console fetch, 1–500. Default 10. |
page |
int | No | Zero-based page offset into the Search Console result set, 0–100. Default 0. |
search |
string | No | Case-insensitive substring match against the keyword label, applied after the fetch. Max 255 chars. |
Plus the shared dashboard filters as query parameters — see Filters.
Response — a flat object:
| Field | Type | Description |
|---|---|---|
rows |
array | Keyword rows (see below). Empty when the site has no data or is not connected. |
error |
string | Present only when rows is empty for a reason other than "no data" — see below. |
Each row in rows:
| Field | Type | Description |
|---|---|---|
label |
string | The search query/keyword. |
visitors |
int | Clicks from this keyword (Search Console's clicks metric). |
impressions |
int | Impressions. |
ctr |
float | Click-through rate, as a percentage (e.g. 4.2 for 4.2%). |
position |
float | Average result position. |
revenue |
int | Estimated revenue attributed to the keyword, in minor units (cents), derived from the site's organic-Google revenue per visitor. |
Status: 200 OK — always, even when Search Console is not connected. This endpoint never returns a 500 for a disconnected or misconfigured property; it returns the clean shape below instead.
error value |
Meaning |
|---|---|
not_connected |
The site has no Google Search Console connection. |
property_not_configured |
The connection exists but no property has been selected. |
period_too_recent |
Search Console returned no rows and the range starts within the last 72 hours (GSC data lags). |
| (client/auth error message) | The Search Console API call failed (token refresh or transport error) — the raw error message is surfaced, rows stays []. |
Rate limit — this endpoint has its own throttle bucket, throttle:60,1,search-keywords (60 requests/minute, isolated from the general 60,1 bucket shared by the rest of the authenticated API), so Search Console reads and Google's own API quota can't be starved by other API traffic.
curl "https://clickbase.so/api/sites/example.com/stats/search-keywords?range=last_30_days&limit=25" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
const params = new URLSearchParams({ range: 'last_30_days', limit: '25' });
await fetch(`https://clickbase.so/api/sites/example.com/stats/search-keywords?${params}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' } })
requests.get(
'https://clickbase.so/api/sites/example.com/stats/search-keywords',
params={'range': 'last_30_days', 'limit': 25},
headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
)
{
"rows": [
{
"label": "web analytics tool",
"visitors": 142,
"impressions": 3210,
"ctr": 4.4,
"position": 8.2,
"revenue": 4200
}
]
}
Not-connected response:
{
"rows": [],
"error": "not_connected"
}