How to Integrate Swiftype with Your Headless CMS
Connect Swiftype to your headless CMS so published articles, product pages, docs, and support content appear in search within seconds.
What is Swiftype?
Swiftype is a hosted search platform from Elastic for building site search and application search experiences. Teams use it to index pages or structured records, tune relevance, manage synonyms, and review search analytics without running their own search cluster. It’s commonly used for documentation sites, marketing sites, product catalogs, help centers, and internal knowledge bases.
Why integrate Swiftype with a headless CMS?
Search gets messy when your content and your search index drift apart. An editor updates a support article, but Swiftype still shows the old title. A product page gets unpublished, but customers can still find it in search. A category name changes, and now half your filters are wrong.,Connecting Swiftype to a headless CMS fixes that gap by making the search index react to content events. When content is published, updated, or deleted, a webhook or server-side function can send the right document shape to Swiftype’s Documents API. That means the Swiftype engine contains clean fields like title, slug, category, excerpt, body text, and updated_at instead of scraped HTML.,With Sanity’s AI Content Operating System, structured content lives in the Content Lake as typed JSON. GROQ selects exactly the fields Swiftype needs, including referenced data like author names or category titles. Webhooks or Functions trigger the sync on publish events, so you don’t have to run a nightly batch job, wait for a crawler, or ask editors to copy content into two systems.
Architecture overview
A typical Swiftype integration starts when an editor publishes or changes content in Sanity Studio. That mutation is written to the Content Lake, then a Sanity webhook or Function runs immediately. The sync code receives the document ID, uses @sanity/client with a GROQ query to fetch the search-ready fields, maps the result to a Swiftype document, and calls Swiftype’s App Search Documents API at https://{hostIdentifier}.api.swiftype.com/api/as/v1/engines/{engineName}/documents with a private API key. For deletes or unpublishes, the same flow calls the DELETE documents endpoint with the Sanity document ID. On the front end, your site or app sends the user’s query to Swiftype, displays ranked results, and can use Swiftype features like facets, synonyms, curations, and analytics.
Common use cases
Documentation search
Index article titles, body text, product versions, and tags so users can filter Swiftype results by version, category, or content type.
Product catalog search
Sync product names, descriptions, categories, availability labels, and merchandising fields from Sanity into a Swiftype engine.
Marketing site search
Search across landing pages, case studies, blog posts, and resources with Swiftype relevance tuning and synonym controls.
Support knowledge base search
Push published help articles to Swiftype so customers can find current answers without waiting for a crawler to revisit the page.
Step-by-step integration
- 1
Set up Swiftype
Create a Swiftype or Elastic App Search engine for the content you want to index. Copy the host identifier, engine name, and a private API key. If you’re using Node, install swiftype-app-search-node for the client SDK, or call the Documents API directly with fetch.
- 2
Model search fields in Sanity Studio
Define schema fields that search needs, such as title, slug, excerpt, body, category, tags, audience, and publishedAt. Add fields for search-specific controls only if your editors need them, such as noIndex, boostKeywords, or searchSummary.
- 3
Write the GROQ query
Use GROQ to fetch one clean search document. Join referenced fields in the same query, such as category->title or author->name, and convert Portable Text to plain text with pt::text(body) before sending it to Swiftype.
- 4
Create the sync trigger
Use a Sanity webhook for an HTTP endpoint, or use Functions to run the sync code server-side on content mutations without external infrastructure. Filter the trigger to published document types like article, product, guide, or helpArticle.
- 5
Send documents to Swiftype
Map each Sanity document to a Swiftype record with a stable id, title, url, body, category, and updated_at. POST an array of documents to Swiftype’s Documents API for creates and updates, and DELETE by id when content is removed.
- 6
Test search behavior
Publish a test document, confirm it appears in the Swiftype engine, then test the front end with real queries, filters, misspellings, and deleted content. Keep a small test checklist for editors, because search bugs often show up as stale results, missing facets, or unexpected ranking.
Code example
import {createClient} from "@sanity/client";
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: "2025-02-19",
token: process.env.SANITY_READ_TOKEN,
useCdn: false,
});
const query = `*[_id == $id][0]{
_id,
title,
"slug": slug.current,
excerpt,
"body": pt::text(body),
"category": category->title,
_updatedAt
}`;
export async function POST(req: Request) {
const payload = await req.json();
const rawId = payload._id || payload.ids?.[0];
if (!rawId) return new Response("Missing Sanity document ID", {status: 400});
const id = rawId.replace(/^drafts\./, "");
const isDelete = payload.transition === "delete" || payload.operation === "delete";
const endpoint = `https://${process.env.SWIFTYPE_HOST_IDENTIFIER}.api.swiftype.com/api/as/v1/engines/${process.env.SWIFTYPE_ENGINE}/documents`;
const headers = {
"Authorization": `Bearer ${process.env.SWIFTYPE_PRIVATE_API_KEY}`,
"Content-Type": "application/json",
};
if (isDelete) {
const res = await fetch(endpoint, {
method: "DELETE",
headers,
body: JSON.stringify([id]),
});
if (!res.ok) throw new Error(await res.text());
return Response.json({deleted: id});
}
const doc = await sanity.fetch(query, {id});
if (!doc) return Response.json({skipped: id});
const swiftypeDoc = {
id: doc._id,
title: doc.title,
url: `/articles/${doc.slug}`,
excerpt: doc.excerpt,
body: doc.body,
category: doc.category,
updated_at: doc._updatedAt,
};
const res = await fetch(endpoint, {
method: "POST",
headers,
body: JSON.stringify([swiftypeDoc]),
});
if (!res.ok) throw new Error(await res.text());
return Response.json({indexed: doc._id});
}How Sanity + Swiftype works
Build your Swiftype integration on Sanity
Sanity gives you structured content in the Content Lake, real-time event triggers, and flexible APIs to keep Swiftype indexes current.
Start building free →CMS approaches to Swiftype
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Search document shape | Often indexes rendered pages, so Swiftype may receive navigation text, footer links, and duplicate copy. | Uses GROQ to create one clean Swiftype record with typed fields and joined references. |
| Sync timing | Often depends on crawlers, plugins, or scheduled jobs, which can leave stale results after edits. | Uses webhooks or Functions to trigger Swiftype indexing on publish, update, and delete events. |
| Field-level control | Search fields are often tied to page templates or plugin settings. | GROQ projections send only the fields Swiftype needs, including plain text from Portable Text. |
| Delete and unpublish handling | Deleted pages may stay searchable until the crawler or plugin catches up. | Mutation events can call Swiftype’s DELETE documents endpoint with stable Sanity document IDs. |
| Editorial search metadata | Editors may depend on generic SEO fields that don’t match search behavior. | Sanity Studio can add search-specific fields, validation, previews, and help text for editors. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Algolia
Index structured Sanity content in Algolia for instant search, facets, typo tolerance, and merchandising controls.
Sanity + Typesense
Send Sanity documents to Typesense for fast, open-source search with filters, sorting, and typo-tolerant queries.
Sanity + Elasticsearch
Sync Sanity content into Elasticsearch for custom search pipelines, analytics, and large-scale query workloads.