Hawksearch Analytics Developer Portal
Overview

Hawksearch Analytics API

A REST API that gives you direct access to your Hawksearch analytics data — search events, clicks, sales, and recommendation interactions. Build custom dashboards, feed data into your BI tools, or power site-level reporting with a single integration.

Interactive reference — Use the API Reference to explore every endpoint and try requests directly from your browser.

Endpoints

GET / Full field schema — types, operators, valid values
POST / Ad-hoc query — define dimensions, measures, and filters
POST /daily-searches Daily search volume by engine
POST /top-queries Top search keywords by volume
POST /poor-results Keywords returning few results
POST /spelling-suggestion Daily spelling correction activity
POST /daily-sales Daily sales funnel metrics
POST /sales-by-product Revenue broken down by product and keyword
POST /recommendations Widget impressions, clicks, add-to-carts, orders, and revenue per day

Authentication

API Keys

Every request must include your API key in the x-api-key header. Your key is scoped to your account — it automatically restricts results to your site's data only.

HTTP
POST / HTTP/1.1
Host: api.analytics.hawksearch.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Contact your account manager to obtain an API key or to rotate an existing one. Keys cannot be created through the API.

Overview

Base URL

URL
https://api.analytics.hawksearch.com

All endpoints are relative to this base URL. The API is HTTPS-only.


Data

Data Model

All data lives in a single summary table. Each row has an event_type that determines which fields are populated. Always filter on event_type when querying fields specific to one row type.

event_type Represents Key fields
search A search request. Includes click details if the search led to a click. keyword, no_of_results, visit flags
cart An item added to cart, linked to the originating search. item_id, item_price, item_quantity
sale A purchased item, linked to the originating search. item_total, order_number, order_total
rec_click A click on a recommended item. Carries sale fields when the click led to a purchase. widget_id, widget_name, item_total
Visit-level flags on search rows (had_click, had_cart, had_sale, had_rec_click) reflect what happened across the entire session — not just the individual search row.
Data

Field Reference

Use GET / to retrieve the live schema. The tables below document the most commonly used fields.

Search fields

FieldTypeDescription
event_datedateDate of the search
keywordstringSearch term entered by the user
tracking_idstringUnique ID for a single search request
visit_idstringSession ID — shared across searches in one visit
visitor_idstringBrowser/device identifier
no_of_resultsnumberResults returned for this search
engine_id / engine_namestringSearch engine identifier and display name
event_year / event_monthnumberPartition keys — include in filters for best performance

Visit-level flags (on search rows)

FieldDescription
had_clickThis search had at least one click
had_cartThis visit included an add-to-cart
had_saleThis visit included a purchase
had_rec_impressionThis visit had a recommendation impression
had_rec_clickThis visit had a recommendation click

Item fields (cart, sale, rec_click rows)

FieldTypeDescription
item_id / item_namestringItem identifier and title
item_quantitynumberQuantity
item_pricenumberUnit price
item_totalnumberLine total. Populated on sale rows and attributed rec_click rows.
order_numberstringOrder identifier
order_totalnumberTotal order value

Recommendation fields (rec_click rows)

FieldTypeDescription
widget_idstringRecommendation widget identifier
widget_namestringRecommendation widget display name

Querying

Ad-hoc Queries

Send a POST / with a structured query to pull any combination of fields from your data. Specify dimensions (what to group by), measures (what to aggregate), and filters (what to include).

Familiar query format — The ad-hoc query schema is inspired by Cube.js, a widely adopted open-source analytics framework. If you've worked with Cube.js, Cube Cloud, or any BI tool built on top of it (Metabase, Superset, etc.), the dimensions / measures / filters pattern will feel immediately familiar. This is standard practice in the analytics engineering community for abstracting SQL behind a portable, declarative query layer.
JSON — Revenue by keyword
{
  "dimensions": ["keyword"],
  "measures": [
    { "field": "item_total",   "fn": "sum",            "alias": "revenue" },
    { "field": "order_number", "fn": "count_distinct", "alias": "orders" },
    { "field": "item_quantity","fn": "sum",            "alias": "units_sold" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals",     "values": ["sale"] },
    { "member": "event_date", "operator": "inDateRange", "values": ["last_month"] }
  ],
  "order": [{ "field": "revenue", "dir": "desc" }],
  "limit": 25
}

Request fields

FieldTypeDefaultDescription
querystringNatural language question. When set, structured fields are ignored.
dimensionsstring[]Fields to select and group by
measuresobject[]Aggregations to compute
filtersobject[]Row-level conditions (WHERE). ANDed together.
havingobject[]Post-aggregation conditions (HAVING). Member must be a measure alias.
orderobject[]Sort order
pagenumber1Page number (1-based)
limitnumber100Rows per page (max 1000)
dryRunbooleanfalseReturn generated SQL without executing
queryExecutionIdstringRe-fetch a prior cached result (valid 24 h)
Querying

Natural Language

Set query to a plain English question instead of building the structured fields manually. The API interprets it and runs the appropriate query.

JSON
{ "query": "Top 10 keywords by revenue last month" }
If the natural language query fails (HTTP 502), rephrase the question or switch to structured mode. Structured queries are always more reliable for production integrations.
Querying

Filters

Each filter object has member (the field), operator, and values. Multiple filters are combined with AND.

equalsExact match
notEqualsNot equal (does not exclude NULLs)
gt / gteGreater than / or equal
lt / lteLess than / or equal
containsSubstring match
startsWithPrefix match
inMatch any value in list
setField is not null
notSetField is null
inDateRangeDate range or shortcut

Date range shortcuts

today this_week this_month last_month last_30_days this_year last_year
JSON — Date range examples
// Shortcut
{ "member": "event_date", "operator": "inDateRange", "values": ["last_30_days"] }

// Explicit range
{ "member": "event_date", "operator": "inDateRange", "values": ["2026-06-01", "2026-06-30"] }

// Null check
{ "member": "widget_id", "operator": "set" }
Querying

Measures

Each measure object defines one aggregation. When measures are present, dimensions automatically become the GROUP BY.

PropertyRequiredDescription
fieldYesField to aggregate. Use "*" with count for a row count.
fnYescount, count_distinct, sum, avg, min, max
aliasNoColumn name in the response. Auto-generated if omitted.
JSON
[
  { "field": "item_total",   "fn": "sum",            "alias": "revenue" },
  { "field": "order_number", "fn": "count_distinct", "alias": "orders" },
  { "field": "*",            "fn": "count",          "alias": "row_count" }
]
Querying

Pre-built Reports

Report endpoints run fixed, pre-optimised queries — faster than ad-hoc queries and require no field knowledge. Send a date range and receive paginated results.

JSON — Report request
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30",
  "page": 1,
  "limit": 100
}
EndpointWhat it returns
/daily-searchesDaily search count by engine and group
/top-queriesTop keywords by search volume
/poor-resultsKeywords returning 10 or fewer results
/spelling-suggestionDaily spelling correction counts
/daily-salesDaily funnel — searches, clicks, carts, orders, revenue
/sales-by-productRevenue and orders by product and keyword
/recommendationsWidget impressions, clicks, add-to-carts, orders, and revenue per day

Advanced

Pagination

All responses include totalRecords, hasMore, page, and limit. Increment page while hasMore is true to fetch subsequent pages.

Each response also includes a queryExecutionId. Pass it back in a follow-up request to re-fetch the same result without re-running the query — results are cached for 24 hours.

JSON — Re-fetch cached result
{
  "queryExecutionId": "a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "page": 1,
  "limit": 25
}
page and limit must match the original request — the cached result contains only the rows for that page.
Advanced

Dry Run

Add "dryRun": true to preview the generated SQL without executing it. The response will contain a sql field and no data.

JSON
{
  "dimensions": ["keyword"],
  "measures": [{ "field": "item_total", "fn": "sum", "alias": "revenue" }],
  "filters": [
    { "member": "event_type", "operator": "equals", "values": ["sale"] },
    { "member": "event_year", "operator": "equals", "values": ["2026"] }
  ],
  "dryRun": true
}
Advanced

Performance Tips

  • Always include event_year and/or event_month in filters when targeting a specific period. These are partition keys and dramatically reduce data scanned.
  • Use date shortcuts (last_30_days, last_month, etc.) for relative windows — they resolve server-side and keep your query always current.
  • Prefer pre-built report endpoints for common metrics — they query pre-aggregated tables and are faster than raw summary queries.
  • Set limit to the smallest value you need. Use hasMore and totalRecords to decide whether to page further.
  • Re-use queryExecutionId for retries or paginated UIs — avoids re-running expensive queries.

Reference

Response Format

All endpoints return the same JSON structure regardless of query type.

JSON
{
  "columns": [
    { "name": "keyword", "type": "string" },
    { "name": "revenue", "type": "number" }
  ],
  "data": [
    { "keyword": "running shoes", "revenue": "4521.50" },
    { "keyword": "blue jeans",    "revenue": "3890.00" }
  ],
  "totalRecords": 842,
  "page": 1,
  "limit": 25,
  "hasMore": true,
  "queryExecutionId": "a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
FieldDescription
columnsColumn names and inferred types (string, number, date)
dataArray of rows. Each row is a flat key-value map; all values are strings. Null fields are omitted.
totalRecordsTotal matching rows across all pages
hasMoretrue if additional pages are available
queryExecutionIdPass back to re-fetch this result without re-running the query
sqlGenerated SQL. Only present when dryRun: true
Reference

Errors

StatusMeaning
400Invalid request — unknown field, unsupported operator, or missing dimensions/measures
401API key missing or invalid
404Account not configured for analytics
429Rate limit exceeded
500Query execution failed
502Natural language translation failed — rephrase or use structured mode
JSON — Error response
{
  "error": "Bad Request",
  "message": "Unknown dimension: 'sale_item_id'."
}
Reference

Sample Queries

Pre-built Reports

Daily search volume

POST /daily-searches
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30"
}

Top search keywords

POST /top-queries
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30",
  "limit": 25
}

Keywords returning few results

POST /poor-results
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30",
  "limit": 50
}

Spelling correction activity

POST /spelling-suggestion
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30"
}

Daily sales funnel

POST /daily-sales
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30"
}

Revenue by product and keyword

POST /sales-by-product
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30",
  "limit": 100
}

Recommendation widget performance

POST /recommendations
{
  "startDate": "2026-06-01",
  "endDate": "2026-06-30",
  "limit": 50
}

Natural Language

Pass a plain English question using the query field. The API interprets it and runs the appropriate query automatically.

Search volume

POST /
{ "query": "How many searches did we have last month?" }

Top keywords by revenue

POST /
{ "query": "Top 10 keywords by revenue in June 2026" }

Poor-performing search terms

POST /
{ "query": "Which keywords returned no results last 30 days?" }

Recommendation performance

POST /
{ "query": "Which recommendation widgets drove the most revenue this month?" }

Sales trend

POST /
{ "query": "Show me monthly revenue for 2026" }

Ad-hoc Queries

Click-through rate by keyword

POST /
{
  "dimensions": ["keyword"],
  "measures": [
    { "field": "tracking_id",   "fn": "count_distinct", "alias": "searches" },
    { "field": "click_item_id", "fn": "count_distinct", "alias": "clicks" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals",      "values": ["search"] },
    { "member": "event_date", "operator": "inDateRange",  "values": ["last_30_days"] }
  ],
  "order": [{ "field": "searches", "dir": "desc" }],
  "limit": 50
}

Cart abandonment — added to cart but did not purchase

POST /
{
  "dimensions": ["keyword"],
  "measures": [{ "field": "visit_id", "fn": "count_distinct", "alias": "abandoned_visits" }],
  "filters": [
    { "member": "event_type", "operator": "equals",      "values": ["search"] },
    { "member": "had_cart",   "operator": "equals",      "values": ["true"] },
    { "member": "had_sale",   "operator": "equals",      "values": ["false"] },
    { "member": "event_date", "operator": "inDateRange",  "values": ["last_month"] }
  ],
  "order": [{ "field": "abandoned_visits", "dir": "desc" }]
}

Widget revenue (recommendation-attributed)

POST /
{
  "dimensions": ["widget_id", "widget_name"],
  "measures": [
    { "field": "item_total",    "fn": "sum",            "alias": "revenue" },
    { "field": "order_number",  "fn": "count_distinct", "alias": "orders" },
    { "field": "item_quantity", "fn": "sum",            "alias": "units_sold" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals",      "values": ["rec_click"] },
    { "member": "item_total",  "operator": "set" },
    { "member": "event_date",  "operator": "inDateRange",  "values": ["last_30_days"] }
  ],
  "order": [{ "field": "revenue", "dir": "desc" }]
}

Top recommended items by widget

POST /
{
  "dimensions": ["widget_id", "widget_name", "item_id", "item_name"],
  "measures": [
    { "field": "*",            "fn": "count",          "alias": "clicks" },
    { "field": "item_total",   "fn": "sum",            "alias": "revenue" },
    { "field": "order_number", "fn": "count_distinct", "alias": "orders" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals",      "values": ["rec_click"] },
    { "member": "event_date", "operator": "inDateRange",  "values": ["last_30_days"] }
  ],
  "order": [{ "field": "clicks", "dir": "desc" }],
  "limit": 50
}

Monthly revenue trend

POST /
{
  "dimensions": ["event_year", "event_month"],
  "measures": [
    { "field": "item_total",    "fn": "sum",            "alias": "revenue" },
    { "field": "order_number",  "fn": "count_distinct", "alias": "orders" },
    { "field": "item_quantity", "fn": "sum",            "alias": "units_sold" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals", "values": ["sale"] },
    { "member": "event_year", "operator": "equals", "values": ["2026"] }
  ],
  "order": [{ "field": "event_month", "dir": "asc" }]
}

High-revenue keywords — using having

POST /
{
  "dimensions": ["keyword"],
  "measures": [
    { "field": "item_total",   "fn": "sum",            "alias": "revenue" },
    { "field": "order_number", "fn": "count_distinct", "alias": "orders" }
  ],
  "filters": [
    { "member": "event_type", "operator": "equals",      "values": ["sale"] },
    { "member": "event_date", "operator": "inDateRange",  "values": ["last_month"] }
  ],
  "having": [
    { "member": "revenue", "operator": "gte", "values": ["1000"] }
  ],
  "order": [{ "field": "revenue", "dir": "desc" }]
}