REST API Reference
SearchJet is a REST API that any HTTP client can call, with no client library to install. This page covers search and indexing in full and summarises the other endpoints. For a quick list, see the API Endpoints Reference.
Base URL and headers
https://app.searchjetengine.com/api/v1
| Header | When | Value |
|---|---|---|
Authorization |
Always | Bearer YOUR_PUBLIC_KEY or Bearer YOUR_PRIVATE_KEY (see below) |
Accept |
Always | application/json. Without it, some errors come back as an HTML page or a redirect instead of JSON. |
Content-Type |
Requests with a body | application/json |
X-Session-Id |
Optional, on search | Your own session identifier, stored with search logs on paid plans |
Keys go in the Authorization header and never in the query string.
Keys
| Key | Where to find it | Accepted by |
|---|---|---|
| Public search key | Dashboard, Sites list ("Public Key") | GET /search, MCP, GET /client-info |
| Private key | Dashboard, Sites → API Keys. It's 32 characters with no prefix, and it's shown only once. | Indexing, analytics, click tracking, AI generation, GET /client-info |
The wrong key type returns 401. Keep the private key on your server. See API Key Setup.
GET /search
Uses the public key. CORS is enabled, so browsers can call this endpoint directly.
Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Required. The search query. If it's missing, you get 400 MISSING_QUERY. |
limit |
integer | 1 to 100, default 20. |
page |
integer | Default 1. |
offset |
integer | Default (page - 1) × limit. If you send both offset and page, offset wins. |
filters |
string | Meilisearch filter expression, at most 1,000 characters. |
sort |
string | Comma-separated attribute:asc or attribute:desc pairs, up to 5. |
attributes |
string | Comma-separated fields to return, limited to the fields your plan returns. |
semantic |
boolean | Hybrid semantic search. Requires Pro or higher, and semantic search enabled in the site settings. |
semanticRatio |
float | 0 to 1, default 0.5. Only used when semantic=true. |
ai_answer |
boolean | Generates an AI answer from the top results (all plans, within your monthly AI-answer quota). |
On Pro, Growth and Enterprise you can also send facets, attributesToHighlight, attributesToCrop, cropLength, highlightPreTag, highlightPostTag, showMatchesPosition (adds _matchesPosition) and matchingStrategy. On Free they're ignored.
Free plan limits: you can filter only on id, title, type, price, currency and sku, and sort on one of id, title or price. On paid plans, you can filter and sort on any attribute marked filterable or sortable in Index Settings – General Attributes.
Response
| Field | Description |
|---|---|
results |
The array of hits, in ranked order |
remaining_quota |
Searches left this month (null if unlimited, -1 if metering is unavailable) |
total |
Estimated number of matches |
limit, offset, page |
Pagination applied (page = floor(offset / limit) + 1) |
semantic_search_used |
Whether hybrid semantic search ran |
facet_distribution, facet_stats |
Facet data (empty unless you requested facets) |
processing_time_ms |
Engine processing time |
answer |
With ai_answer=true and results: a string, or null if generation failed |
semantic_ratio |
Only on semantic searches |
meta |
Optional, e.g. {"warnings": ["AI answer quota exceeded."]} |
Hit fields depend on your plan:
| Plan | Fields returned |
|---|---|
| Free | id, title, url, type, thumbnail, price, currency, sku (no content or excerpt) |
| Pro | The Free fields plus excerpt, content, author, date, image, categories, tags, regular_price, sale_price, stock_status |
| Growth, Enterprise | All stored fields |
On Free, a hit's id is returned as site_. Hits include _formatted only when a paid-plan request sets attributesToHighlight or attributesToCrop.
Example
curl -G "https://app.searchjetengine.com/api/v1/search" \
-H "Authorization: Bearer $SEARCHJET_PUBLIC_KEY" \
-H "Accept: application/json" \
--data-urlencode "q=react tutorial" \
--data-urlencode "limit=10" \
--data-urlencode 'filters=type = "post"'
For a full response example, see the Search API Deep Dive.
POST /index/bulk and PATCH /index/bulk
Uses the private key, from server-side code only.
- POST adds documents, and replaces any existing document with the same
id. - PATCH merges the fields you send into existing documents.
| Body field | Description |
|---|---|
documents |
Required. Up to 1,000 documents per request on Free, or 5,000 on paid plans. Each document can be at most 1 MB. |
full_reindex |
Optional true: clears your existing documents before adding these. Counts against your daily reindex limit. |
Each document needs an id (letters, digits, - and _) plus at least one other non-empty field. Common fields are title, content, url, excerpt and type (default page). The Index Management API lists the rest.
Indexing is asynchronous. The response is the queued task:
{"taskUid": 1234, "indexUid": "...", "status": "enqueued", "type": "documentAdditionOrUpdate", "enqueuedAt": "...", "message": "Indexed 2 documents. Total indexed: 120.", "was_cleared": false, "task_uid": 1234}
To check whether the task finished, poll GET /index/tasks/{taskUid}. Index requests are rejected with 429 quota_exceeded if they would take you past your plan's page limit. Near that limit, re-sent ids are counted as new documents. On Free and Pro, sending more than 1,000 documents a minute triggers abuse detection, so keep batches to 1,000 or fewer and space them out.
Other endpoints
| Endpoint | Key | Body / params | Response |
|---|---|---|---|
DELETE /index/delete |
Private | {"id": "..."} or {"ids": [...]} (max 500) |
{success, task_uid, status} |
POST /index/delete-batch |
Private | {"ids": [...]} or {"filter": "..."} |
{success, taskUid, deletedCount, message} |
GET /index/tasks/{taskUid} |
Private | – | {success, task: {uid, status, type, details, error, duration, enqueued_at, started_at, finished_at}} |
GET /index/count |
Private | – | {success, total_indexed_documents} |
GET /index/{id} |
Private | – | {success, document}, or 404 not_found |
GET /search-analytics |
Private | days (1 to 90, default 30) |
{top_queries, zero_result_queries, total_searches, search_volume_by_day} |
POST /search-analytics |
Private | One log {query, results_count, ...} or {"logs": [...]} (max 100) |
{"status": "ok"} |
POST /analytics/click |
Private | document_id and position (both required), plus search_log_id, session_id, user_agent |
{"status": "ok"} |
POST /ai/generate |
Private | prompt (required, max 1,000 characters) |
{success, message, task_id, status: "pending"} |
GET /ai/status/{id} |
Private | – | {success, status: "pending" | "complete" | "failed", output} |
GET /client-info |
Public or private | – | {success, site_id, is_shared_index, plan, public_key, usage_limits} |
POST /mcp/{publicKey} |
Public key in the path | JSON-RPC 2.0 (Pro and above) | MCP search tool |
Analytics and click tracking need the private key. Call them from your own server, never from the browser.
Errors
Error bodies vary by endpoint. Search uses {"error", "code"}, the index endpoints use {"success": false, "error", "message"}, and the middleware uses {"message"} or {"error", "message"}. Error Handling lists every error, with retry code. Rate limits are covered in Rate Limits & Quotas.