Home· Features· Developer API
⌨️ Developer API

Plain REST. Three scopes. v1 stable.

Bearer-token auth, JSON in and out, stable SK-* error codes with incident IDs, dual response shape (Skryx-native + legacy data-wrapper). No SDK lock-in — use whatever HTTP client your team already has.

Starter Growth Scale Enterprise
// Search in 3 lines
const r = await fetch("https://api.skryx.io/v1/indexes/products/query", {
  method: "POST",
  headers: { Authorization: "Bearer sk_live_…" },
  body: JSON.stringify({ q: "sneakers" })
});
const { hits, found, search_mode } = await r.json();
Auth model

Three scopes. No more.

Every API key carries one of three scopes. Keys are stored as SHA-256 hashes (full plaintext shown once at create time); a 12-char prefix is kept for display so you can tell keys apart in the UI. Rotate any key without breaking the others.

🔐 The three scopes

Read · write · admin.
Admin implies the rest.

index:read covers search + autocomplete only — safe to ship in the browser. index:write adds document mutations and data-source syncs (ingestion services). index:admin is the umbrella for schema / synonyms / ranking rules / curator / lifecycle endpoints.

  • Key formats: sk_live_{32} for search/write, sk_admin_{32} for admin
  • Scope checked by CheckApiScope middleware on every protected route
  • 403 with insufficient_scope and a message listing the required scope
// API key shapes
sk_live_a1b2c3d4e5f6g7h8…   // search · write
sk_admin_x9y8z7w6v5u4t3s2…  // admin

// Required scopes per resource
GET    /v1/indexes/:n/query     none
POST   /v1/indexes/:n/documents index:write
POST   /v1/indexes/:n/synonyms  index:admin
DELETE /v1/indexes/:n           index:admin
The surface

Eleven resources. Standard verbs.

🔍 Search & autocomplete

Two paths, same body shape.

POST /v1/indexes/:name/query (or the /search alias). Accepts both Skryx-native input (q + filters{} + facets[] + sort) and the legacy engine-style shape (query_by, filter_by, etc.) — keeps existing integrations working without translation.

  • Optional search_mode: auto / keyword / semantic / hybrid
  • Per-query num_typos, prefix, fields / exclude_fields
  • POST /v1/indexes/:name/suggest for autocomplete (1–20 results)
POST /v1/indexes/products/query
{
  "q": "wireless headphones",
  "filters": { "brand": ["Bose", "Sony"] },
  "facets": ["brand", "category"],
  "sort": "price:asc",
  "per_page": 20,
  "search_mode": "auto"
}
📄 Documents

Single & batch upsert. Partial PATCH.

POST single or /batch (no documented batch size cap — validation requires min 1, in practice we recommend batches of 500). Partial updates via PATCH. Successful batch returns 200; mixed results return 207 with per-row errors so you know exactly which documents failed and why.

  • POST /v1/indexes/:n/documents · single
  • POST /v1/indexes/:n/documents/batch · array, 200 / 207
  • PATCH /v1/indexes/:n/documents/:id · merge fields, re-embed if needed
  • DELETE /v1/indexes/:n/documents/:id
POST /v1/indexes/products/documents/batch
{ "documents": [/* … */] }

// 207 partial response
{
  "data": {
    "imported": 498,
    "failed":   2,
    "errors": [
      { "id": "sku-x",
        "reason": "missing field: title" }
    ],
    "records_count": 26509
  }
}
🪡 Zero-downtime reindex

Build a new collection. Swap atomically.

Index Lifecycle exposes two endpoints. copy-settings-from clones synonyms, ranking rules, and search settings from one index to another (each set is opt-in). swap-with performs an atomic alias swap so traffic moves to the new collection instantly, with the option to drop the old one.

  • POST /v1/indexes/:n/copy-settings-from · clone synonyms / rules / settings
  • POST /v1/indexes/:n/swap-with · atomic alias swap, optional drop
  • Zero downtime, no inconsistent reads, no warming step needed
POST /v1/indexes/products_v2/copy-settings-from
{
  "source_index": "products",
  "copy": ["synonyms", "ranking_rules"]
}

POST /v1/indexes/products/swap-with
{
  "target_index": "products_v2",
  "drop_source_after": true
}
🎨 Visual Curator

Pin · replace · hide.
With A/B + scheduling.

Full CRUD on curated-result rules: triggers (exact / contains / contains_all / regex), modes (pin / replace / hide), schedule windows, A/B split percentage. Plus duplicate, toggle, per-rule analytics, and a preview endpoint that returns organic vs curated hits before you save anything.

  • Multiple triggers per rule (exact / contains / contains_all / regex)
  • A/B split percentage between curated and organic variants
  • Per-rule analytics: impressions, clicks, CTR, last fired
POST /v1/indexes/products/curated-results
{
  "name": "Holiday push",
  "mode": "pin",
  "pinned_product_ids": ["sku-1", "sku-2"],
  "triggers": [
    { "type": "contains", "value": "sneakers" }
  ],
  "scheduled_start": "2026-12-01",
  "scheduled_end":   "2026-12-31",
  "ab_test_enabled": true,
  "ab_test_split":   0.5
}
🔁 Semantic lifecycle

Enable, status, reindex, backfill.

Six endpoints to manage embeddings end-to-end. Pick a Skryx embedding model at enable time, poll /status for live progress, re-trigger a full pass with /reindex, or backfill missing vectors after a schema bump with /embeddings/backfill.

  • GET / POST semantic-search/{status, enable, disable, reindex}
  • GET embeddings/status · coverage % + counts
  • POST embeddings/backfill · idempotent full pass
GET /v1/indexes/products/embeddings/status

{ "data": {
  "enabled":                  true,
  "model":                    "voyage-3-lite",
  "dimensions":               512,
  "total_documents":          26011,
  "documents_with_embeddings": 26011,
  "documents_pending":        0,
  "documents_failed":         0,
  "coverage_percentage":      100
}}
⚡ Auto-Sync Feeds

Full CRUD + test-connection + sync-now.

Manage data sources entirely through the API. Test a feed before saving it. Trigger an out-of-cycle sync. Walk the last 50 runs with per-run detail (products fetched / added / updated / deleted / failed, duration, error trace).

  • Full CRUD on /v1/indexes/:n/data-sources
  • POST /data-sources/:id/sync-now · 202 + run ID
  • GET /data-sources/:id/runs · last 50 with per-run drilldown
POST /v1/indexes/products/data-sources/
     test-connection
{
  "url":    "https://shop…/feed.xml",
  "format": "auto"
}

// → format_detected, total_products,
//   preview (3 docs), default_mapping
Response shape

One envelope, two doors.

Search responses ship in two shapes simultaneously: the Skryx-native shape at the top level (the documented one for new integrations) and a legacy data wrapper underneath that mirrors the older engine-style payload. Existing integrations keep reading data.hits; new ones use the cleaner top-level fields.

{
  // Native shape — preferred for new code
  "hits":              [ /* … */ ],
  "found":             142,
  "page":              1,
  "per_page":          20,
  "total_pages":       8,
  "facets":            { "brand": { "Sony": 42, "Bose": 38 } },
  "search_time_ms":    18,
  "search_mode":       "hybrid",
  "ai_enhanced":       true,
  "ai_context": {
    "original_query":  "laptop for vid edting",
    "rewritten_query": "laptop for video editing",
    "intent":          "compare",
    "alternative_queries": ["4k editing laptop", "creator laptop"]
  },
  "suggestions":       ["video editing laptop"],
  "related_hits":      [ /* secondary tier · similarity 0.50–0.65 */ ],
  "curated_applied":   false,

  // Legacy data wrapper — keeps older integrations working
  "data": { "hits": [], "out_of": 26011, "facet_counts": [], "embed_time_ms": 3 }
}
Errors

Stable codes. Incident IDs.

🆔 SK-* error codes

Same code = same root cause. Forever.

Every error response carries a stable SK-* code so your error-handling code can match on it without parsing English. Each one also includes an incident_id you can quote when emailing support so we look up the exact request in our logs.

  • SK-AI-{503, 429, 422, 504} — AI / embedding pipeline
  • SK-SE-{503, 400, 504, 404} — search engine
  • SK-DS-{503, 422, 504, 401} — data sources
  • SK-SYS-{500, 503, 429} — platform
{
  "error":       "SK-SE-400",
  "message":     "Invalid filter_by expression",
  "incident_id": "INC-2026-05-25-a8f2",
  "details": {
    "field":  "filter_by",
    "reason": "unbalanced parentheses"
  }
}
🛡️ Rate limits

Two layers. Both observable from headers.

Per-IP burst (60 req / sec, soft cap) returns 429 with X-RateLimit-Limit / Remaining / Retry-After. Per-tenant monthly quota returns 429 with a structured body — current_usage, quota, reset_at, upgrade_url — or, on overage-enabled plans, bills instead of failing.

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 0

{
  "error":         "quota_exceeded",
  "current_usage": 100242,
  "quota":         100000,
  "reset_at":      "2026-06-01T00:00:00Z",
  "upgrade_url":   "https://app.skryx.io/billing"
}
In every language

If it speaks HTTP, it speaks Skryx.

There are no published SDKs yet — language-specific clients for JavaScript, Python, PHP, Go, Ruby are on the roadmap. In the meantime, the contract is plain JSON over HTTPS: every example in the docs is a fetch / requests / Http / net/http snippet that runs without any dependency install.

// JavaScript / TypeScript
const r = await fetch(API + "/v1/indexes/products/query", {
  method: "POST",
  headers: { Authorization: "Bearer " + key },
  body:    JSON.stringify({ q })
});
# Python
import requests
r = requests.post(
    f"{API}/v1/indexes/products/query",
    headers={"Authorization": f"Bearer {key}"},
    json={"q": q},
).json()
// PHP / Laravel
$hits = Http::withToken($key)
    ->post("$API/v1/indexes/products/query",
        ['q' => $q])
    ->json('hits');
// Go
body, _ := json.Marshal(map[string]any{
    "q": q,
})
req, _ := http.NewRequest("POST",
    API+"/v1/indexes/products/query",
    bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+key)
Stability promises

v1. Honest about what's missing.

v1 stable
Routes mounted at api.skryx.io/v1 · response shape carries native + legacy doors
~3 min
From key created to first successful search call · documented end-to-end
Soon
Published SDKs and outbound webhooks · both on the roadmap, not yet shipped
Keep exploring

Other things Skryx does

Try it on your own catalog.

Free tier, no credit card. EU-hosted from day one.