Skip to content
SearchJet
Updated October 1, 2026

Index Management API

The Index Management API keeps your SearchJet index in sync with your content. Every endpoint on this page needs your private key, so only call them from server-side code. If you run WordPress or WooCommerce, the SearchJet Instant Search plugin handles indexing for you.

Authentication

Authorization: Bearer YOUR_PRIVATE_KEY
Accept: application/json
Content-Type: application/json

The private key is 32 characters long. You create it in the dashboard under Sites → API Keys, and it's shown only once. If you send the public search key here, you get 401 {"message": "Invalid or expired token"}. Always send Accept: application/json: without it, a validation error comes back as a redirect instead of a 422 JSON response. See API Key Setup.

Add, replace or update documents

POST  https://app.searchjetengine.com/api/v1/index/bulk
PATCH https://app.searchjetengine.com/api/v1/index/bulk
  • POST adds documents. A document with an existing id is replaced entirely.
  • PATCH merges the fields you send into existing documents. Use it for partial updates, such as changing only price.

Body

Field Required Description
documents Yes Array of documents. Up to 1,000 per request on Free and 5,000 on paid plans.
full_reindex No true clears your existing documents before adding these. Limited per day by plan: Free 1, Pro 4, Growth and Enterprise unlimited.

Each document:

  • needs an id, plus at least one other non-empty field. Documents with only an id are dropped.
  • must be at most 1 MB as JSON. Larger documents get 413 payload_too_large.
  • should use only letters, digits, - and _ in its id. Other characters make the asynchronous task fail.

Useful fields are title, content, url, excerpt, type (default page), author, date, image, thumbnail, price, sale_price, regular_price, currency, sku, stock_status, and categories and tags (as arrays of strings). You can add custom fields too. Before storing documents, SearchJet trims strings, removes empty values, and converts prices to numbers.

Example

curl -X POST "https://app.searchjetengine.com/api/v1/index/bulk" \
  -H "Authorization: Bearer $SEARCHJET_PRIVATE_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {
        "id": "post-1",
        "title": "Getting Started with SearchJet",
        "content": "SearchJet is a hosted site search service...",
        "url": "https://example.com/blog/getting-started",
        "type": "post",
        "date": "2026-01-15"
      }
    ]
  }'
import os
import httpx

HEADERS = {
    "Authorization": f"Bearer {os.environ['SEARCHJET_PRIVATE_KEY']}",
    "Accept": "application/json",
}

response = httpx.patch(  # merge: update only the price
    "https://app.searchjetengine.com/api/v1/index/bulk",
    headers=HEADERS,
    json={"documents": [{"id": "product-42", "price": 19.99}]},
)
response.raise_for_status()
task_uid = response.json()["taskUid"]
use Illuminate\Support\Facades\Http;

$task = Http::withToken(config('services.searchjet.private_key'))
    ->acceptJson()
    ->post('https://app.searchjetengine.com/api/v1/index/bulk', [
        'documents' => [[
            'id' => 'post-1',
            'title' => 'Getting Started with SearchJet',
            'url' => 'https://example.com/blog/getting-started',
        ]],
    ])
    ->throw()
    ->json();

Response

Indexing is asynchronous. A successful request returns the queued task:

{
  "taskUid": 1234,
  "indexUid": "your-index",
  "status": "enqueued",
  "type": "documentAdditionOrUpdate",
  "enqueuedAt": "2026-10-01T09:00:00Z",
  "message": "Indexed 1 documents. Total indexed: 120.",
  "was_cleared": false,
  "task_uid": 1234
}

There's no success field. Use taskUid to check the task's status.

Check a task

GET https://app.searchjetengine.com/api/v1/index/tasks/{taskUid}

The response is {"success": true, "task": {"uid", "status", "type", "details", "error", "duration", "enqueued_at", "started_at", "finished_at"}}. If status is failed, look at error. A typical cause is an invalid id.

Delete documents

DELETE https://app.searchjetengine.com/api/v1/index/delete

Send a JSON body with either {"id": "post-1"} or {"ids": ["post-1", "post-2"]} (at most 500 ids). The response is {success, task_uid, status}. If you send neither field, you get 400 missing_parameters.

curl -X DELETE "https://app.searchjetengine.com/api/v1/index/delete" \
  -H "Authorization: Bearer $SEARCHJET_PRIVATE_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["post-1", "post-2"]}'
POST https://app.searchjetengine.com/api/v1/index/delete-batch

Send either {"ids": [...]} or {"filter": "type = \"draft\""}. The response is {success, taskUid, deletedCount, message}, and deletedCount is -1 when you delete by filter. An invalid filter returns 400 invalid_filter.

Read your index

  • GET /index/count returns {success, total_indexed_documents}. The value is cached for up to 5 minutes.
  • GET /index/{id} returns {success, document} with all stored fields, or 404 not_found.

Limits to plan around

  • Page quota: requests that would take you past your plan's indexed-page limit (Free 1,000, Pro 25,000, Growth 100,000) get 429 quota_exceeded. Incoming documents are counted as new even when their ids already exist, so near the limit, re-sending existing documents can be rejected.
  • Abuse detection: on Free and Pro, indexing more than 1,000 documents per minute adds an X-Abuse-Warning header. After repeated violations within an hour, the key is temporarily suspended (429 "Abuse detected"), and retrying keeps the block going. Keep batches to 1,000 documents or fewer, and wait at least a minute between large batches.
  • Request limits: private-key requests are limited per plan (Free 10 per minute, Pro 60). See Rate Limits & Quotas.

Best practices

  1. Use stable ids, such as post-42, so that re-indexing replaces documents instead of duplicating them.
  2. Use PATCH when only a few fields have changed.
  3. Index in the background with a queue, and retry 429 and 5xx responses with backoff. See Error Handling.
  4. Delete when you unpublish. Step-by-Step Installation & Configuration shows a Laravel observer that does this.

Was this article helpful?

Ready to add search to your project?

Start free. No credit card required. Deploy in under 10 minutes.