Search
Query published content across your site for search-as-you-type and search results pages
The search endpoints run a full-text query across the published content on your site and return ranked matches. Use them to build a search-as-you-type box, a dedicated results page, or any widget that finds content from the browser.
Matching is both exact and fuzzy, so a query that is close but not exact still returns useful results. Basker applies the boosts and synonyms that editors configure.
Two endpoints are available. /api/search returns one ranked set of up to 200 results. /api/search/v2 pages through results and adds a date-aware filter for events. Use /api/search/v2 for a results page that needs paging or tab counts.
Search
GET {your-domain}/api/search?q={query}Unlike the content endpoints, search does not accept the .json suffix. Always call the bare path.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | None | The search query. Required. A missing query returns a 400. |
collection | string | (all types) | Restricts results to one or more content types. Separate multiple values with commas. Valid values are pages, posts, events, and people. Omit to search all types. An unrecognized value returns a 400. |
locale | string | (site default) | Language to search and return, for example cy. An unknown or unavailable code returns a 400. |
Response
A docs array of ranked matches plus list metadata. Each item carries a collectionType field naming the content type that matched, so you can group or filter results by type on the client.
{
"docs": [
{
"id": "000000000000000000000002",
"collectionType": "events",
"title": "Moonclock",
"slug": "moonclock",
"url": "/events/moonclock"
},
{
"id": "000000000000000000000004",
"collectionType": "posts",
"title": "How Moonclock was made",
"slug": "making-moonclock",
"url": "/blogs/remarkable-theatre-news/posts/making-moonclock"
}
],
"totalDocs": 2,
"limit": 200,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}This endpoint returns the full ranked set, up to 200 results, in one response. It does not page. Every result includes an id, a collectionType, a title, a slug, and a url you can render. The other fields depend on the content type:
collectionType | Fields |
|---|---|
pages | description, image, meta |
posts | description, image, meta, data.publishDate, data.authors (each with id and title) |
events | description, image, meta, data.startDate, data.endDate, data.startDate_tz, data.endDate_tz, data.venue (with id and title) |
people | image. The person's name is in title. |
Dates sit under data. There is no top-level startDate, date, or publishDate. Results carry no excerpt, no event instances, and no template settings.
Examples
Search as the visitor types:
async function search(query) {
if (!query) return [];
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const { docs } = await res.json();
return docs;
}Group results by content type for a tabbed results page:
const { docs } = await (await fetch(`/api/search?q=${encodeURIComponent(query)}`)).json();
const byType = docs.reduce((groups, result) => {
(groups[result.collectionType] ??= []).push(result);
return groups;
}, {});
// byType.events, byType.posts, byType.people, …Paged search
GET {your-domain}/api/search/v2?q={query}This endpoint returns 10 results per page and adds a filter parameter that separates upcoming events from past events. Use it for a full results page. Use /api/search for a type-ahead box, where one ranked set is enough.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | None | The search query. Required. It must be 1 to 256 characters. Anything else returns a 400. |
filter | string | ALL | One of ALL, EVENTS, PAST_EVENTS, NEWS, PAGES, or PEOPLE. EVENTS returns events that have not finished. PAST_EVENTS returns events that have. An unrecognized value returns a 400. |
page | integer | 1 | Page number to return. A page above the last page returns the last page. A value that is not a positive integer returns a 400. |
collection | string | (all types) | Restricts results to one or more content types before the filter applies. Valid values are pages, posts, events, and people. |
locale | string | (site default) | Language to search and return, for example cy. An unknown or unavailable code returns a 400. |
Response
The same paginated envelope the content endpoints use, with limit set to 10. Read totalPages and hasNextPage to build the pager.
{
"docs": [
{
"id": "000000000000000000000002",
"collectionType": "events",
"title": "Moonclock",
"slug": "moonclock",
"url": "/events/moonclock",
"excerpt": "...",
"image": { "url": "https://..." }
}
],
"totalDocs": 42,
"limit": 10,
"totalPages": 5,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": true,
"prevPage": null,
"nextPage": 2
}Result counts per tab
GET {your-domain}/api/search/v2/counts?q={query}Returns the number of matches for each filter, plus an ALL total. Call it once alongside the first page to label the tabs on a results page.
{
"EVENTS": 12,
"PAST_EVENTS": 24,
"NEWS": 4,
"PAGES": 2,
"PEOPLE": 0,
"ALL": 42
}Example
Render one page of results and the tab counts together:
const query = encodeURIComponent(term);
const [page, counts] = await Promise.all([
fetch(`/api/search/v2?q=${query}&filter=EVENTS&page=1`).then(res => res.json()),
fetch(`/api/search/v2/counts?q=${query}`).then(res => res.json()),
]);
// page.docs, page.hasNextPage, counts.EVENTSCaching
Search responses are cacheable at the edge. Popular queries benefit from the cache, and long-tail queries fall through to a fresh response.
Date-aware responses hold for a shorter time, because the boundary between upcoming and past events moves. The EVENTS and PAST_EVENTS filters and the counts endpoint carry s-maxage=300. Other search responses carry s-maxage=3600.
Avoid query parameters that vary per visitor, such as cache-busters and timestamps, unless you need to bypass the cache.
See also
- Event instances: list and fetch performances, filtered by date, venue, and category.
- Content endpoints: fetch one content type directly when you already know what you want.