Search API Deep Dive
The Search API is the core of SearchJet. A single GET endpoint runs full-text search, powered by Meilisearch, across your site's index. It can also do hybrid semantic search and generate AI answers. This page goes deeper than the REST API Reference, with usage notes and plan differences.
Endpoint and authentication
GET https://app.searchjetengine.com/api/v1/search
The path must start with /api: requests to https://app.searchjetengine.com/v1/search return 404.
Send your public search key, which is shown on the dashboard's Sites list as "Public Key". It's read-only, and CORS is enabled, so browsers can call the endpoint directly. The private key doesn't work here: it returns 401 INVALID_API_KEY.
Authorization: Bearer YOUR_PUBLIC_KEY
Accept: application/json
Example request
curl -G "https://app.searchjetengine.com/api/v1/search" \
-H "Authorization: Bearer $SEARCHJET_PUBLIC_KEY" \
-H "Accept: application/json" \
--data-urlencode "q=installation guide" \
--data-urlencode "limit=10"
const params = new URLSearchParams({ q: 'installation guide', limit: '10' });
const res = await fetch(`https://app.searchjetengine.com/api/v1/search?${params}`, {
headers: { Authorization: 'Bearer YOUR_PUBLIC_KEY', Accept: 'application/json' },
});
const data = await res.json();
data.results.forEach((hit) => console.log(hit.title, hit.url));
import os
import httpx
response = httpx.get(
"https://app.searchjetengine.com/api/v1/search",
params={"q": "installation guide", "limit": 10},
headers={
"Authorization": f"Bearer {os.environ['SEARCHJET_PUBLIC_KEY']}",
"Accept": "application/json",
},
)
response.raise_for_status()
for hit in response.json()["results"]:
print(hit["title"], hit["url"])
Parameters
| Parameter | Notes |
|---|---|
q |
Required. If it's missing, you get 400 MISSING_QUERY. |
limit |
1 to 100, default 20 |
page / offset |
page defaults to 1. If you send offset, it overrides page. |
filters |
Meilisearch filter string, at most 1,000 characters |
sort |
Comma-separated attr:asc or attr:desc, up to 5 |
attributes |
Comma-separated fields to return, within your plan's field list |
semantic, semanticRatio |
Hybrid semantic search (Pro and above) |
ai_answer |
true adds an AI-generated answer |
On Pro, Growth and Enterprise you can also use facets, attributesToHighlight, attributesToCrop, cropLength, highlightPreTag, highlightPostTag, showMatchesPosition and matchingStrategy. On Free these are silently ignored.
Response
{
"results": [
{
"id": "doc-123",
"title": "Installation Guide",
"url": "https://example.com/docs/installation",
"type": "page",
"excerpt": "A step-by-step guide to installing SearchJet..."
}
],
"remaining_quota": 48766,
"total": 12,
"limit": 10,
"offset": 0,
"page": 1,
"semantic_search_used": false,
"facet_distribution": [],
"facet_stats": [],
"processing_time_ms": 3
}
resultsis the array of hits. It isn't wrapped in another object.totalis an estimate of the number of matches.remaining_quotashows the searches left in your site's monthly quota. It'snullon unlimited plans, and-1if metering is briefly unavailable.- Optional keys:
answer(withai_answer=true),semantic_ratio(on semantic searches), andmeta(for example, warnings).
Which fields each hit contains
Hits contain only the fields your plan returns:
- Free:
id,title,url,type,thumbnail,price,currency,sku. There's nocontentorexcerpt, so design your result template around the title. - Pro: adds
excerpt,content,author,date,image,categories,tags,regular_price,sale_priceandstock_status. - Growth and Enterprise: every stored field.
On Free, ids come back as site_. To recover your own id, strip the prefix. Hits have no highlighting by default. On paid plans, set attributesToHighlight (or attributesToCrop) to get a _formatted object on each hit.
Filters
type = "post"
price < 50
type = "product" AND price >= 10
type = "post" OR type = "page"
NOT type = "archive"
- On Free, you can filter only on
id,title,type,price,currencyandsku. Any other field returns400 INVALID_FILTER("Field 'x' is not available in your plan"). - On paid plans, you can filter on any attribute marked filterable in Index Settings – General Attributes. Anything else returns
INVALID_FILTERwith a hint to add it tofilterableAttributes. - Unbalanced quotes or parentheses also return
INVALID_FILTER.
Comparison operators work on numbers, so to filter by date, index a numeric timestamp field. For more patterns, see Building Search Queries.
Sorting
sort=date:desc or sort=price:asc,title:asc (up to 5 fields). Free can sort on one field from id, title or price. Paid plans can sort on any sortable attribute. An unsupported sort returns 400 INVALID_SORT.
Semantic search
Send semantic=true, and optionally semanticRatio (0 = keyword only, 1 = semantic only, default 0.5). It requires:
- Pro or higher. Otherwise you get
403 SEMANTIC_PLAN_REQUIRED. - Semantic search enabled in your site settings. Otherwise you get
403 SEMANTIC_NOT_ENABLED.
When it ran, the response has semantic_search_used: true and semantic_ratio.
AI answers
Add ai_answer=true to get a short generated answer based on the top results. This works on all plans, within the monthly AI-answer quota: Free 50, Pro 500, Growth 5,000, Enterprise 100,000. Some things to know:
- An answer is generated only when the search has results.
- If you're over quota, the search still succeeds, but there's no
answerandmeta.warningscontains"AI answer quota exceeded." answercan benullif generation failed.- On Free, hits carry no excerpt or content, so answers are based on titles only.
Quota and caching
Every authenticated search counts toward your site's monthly search quota. That includes cached responses and many error responses. When the quota is used up, you get 429 QUOTA_EXCEEDED until the next month or until you upgrade. See Rate Limits & Quotas.
Results are cached for a short time per plan: 10 minutes on Free, 3 on Pro, 1 on Growth, and no caching on Enterprise. Semantic searches aren't cached. Indexing or deleting documents clears the cache.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | MISSING_QUERY |
q is missing |
| 400 | INVALID_FILTER / INVALID_SORT |
See above |
| 400 | INDEX_NOT_CONFIGURED |
The site has no search index yet |
| 401 | MISSING_API_KEY / INVALID_API_KEY |
No key, a wrong key, or the private key |
| 403 | SEMANTIC_PLAN_REQUIRED / SEMANTIC_NOT_ENABLED |
Semantic search isn't available |
| 429 | QUOTA_EXCEEDED |
Monthly search quota used up |
| 429 | – | Too many requests from your IP (30 per minute) |
| 500 | MEILISEARCH_ERROR / UNEXPECTED_SEARCH_ERROR |
Engine error. For example, facets requested on an attribute that isn't facetable. |
Retry code is in Error Handling.
Best practices
- Call search from the browser with the public key. The 30-per-minute limit applies per client IP, so routing every visitor's search through one server means they all share a single budget.
- Debounce search-as-you-type, and paginate rather than requesting 100 results.
- Watch
remaining_quota. - Send
Accept: application/json, so errors always come back as JSON.