Search namespace

Reference for federated keyword suggestions across companies, markets, taxonomy terms, blog posts, and tags.

Use board.search for the search-dropdown typeahead. It returns one interleaved, server-ranked list of companies, markets, taxonomy terms, blog posts, and tags. These reads are public. Routing is the caller's decision — the API never returns an href.

Shared behavior

Every method returns the Board API wire body unchanged and throws BoardApiError for a non-2xx response. The final FetchOptions argument passes abort signals, headers, and framework cache directives through to fetch.

Order is server-ranked. Render the list as returned; do not re-sort or re-filter client-side except to hide kinds your UI does not handle.

board.search.suggest

Suggest companies, markets, categories, skills, blog posts, and tags for a keyword.

ts
board.search.suggest(
query?: SearchSuggestQuery,
options?: FetchOptions,
): Promise<SuggestResult>
FieldTypeBehavior
qstringKeyword matched against company names, market names, taxonomy terms, blog posts, and tags. Queries under two characters return an empty items array.
limitnumberMax interleaved suggestions after type filtering (1–25; default 25).
types('company' | 'category' | 'skill' | 'market' | 'post' | 'tag')[]Include only these kinds. Omit to return every kind. Serializes as repeated query keys.

limit applies after types. { types: ['skill'], limit: 10 } returns up to ten skills — not ten items drawn from a mixed pool that happens to include skills.

ts
const { items, query } = await board.search.suggest({
q: 'acme',
limit: 10,
types: ['company', 'skill'],
});
for (const item of items) {
if (item.type === 'company') {
console.log(item.name, item.slug, item.jobCount);
} else if (item.type === 'market') {
console.log(item.name, item.slug, item.companyCount);
} else if (item.type === 'post') {
console.log(item.title, item.slug);
} else if (item.type === 'tag') {
console.log(item.name, item.slug);
} else {
console.log(item.termType, item.displayName, item.canonicalSlug);
}
}

Result shape

ts
type SuggestResult = {
object: 'suggest_result';
query: string;
items: SuggestionItem[];
};
type SuggestionItem =
| CompanySuggestion
| MarketSuggestion
| TermSuggestion
| PostSuggestion
| TagSuggestion;
KindDiscriminatorUseful fields
Companytype: 'company'id, slug, name, logoUrl, jobCount
Markettype: 'market'id, slug, name, companyCount
Termtype: 'term'termType: 'category' | 'skill', sourceSlug, canonicalSlug, displayName
Posttype: 'post'id, slug, title
Tagtype: 'tag'id, slug, name

Use canonicalSlug for URL paths and sourceSlug for job-list filters. Company slug is the public company URL identity and the value to pass as companySlug on board.jobs.list.

A missing or private board returns 404.

Routing is application-side

The same CompanySuggestion means two different things:

ScopeSelecting a company means
companiesnavigate to /companies/<slug>
jobsapply companySlug as a filter on the listing

The endpoint does not know which scope the user is searching in, so it cannot resolve a destination. Use suggestionPath from @cavuno/board/paths:

ts
import { suggestionPath } from '@cavuno/board/paths';
const path = suggestionPath(item, {
scope: 'jobs',
location: activePlaceSlug,
});
if (path === null && item.type === 'company') {
// apply companySlug filter — do not invent /jobs/companies/<slug>
} else if (path) {
navigate(path);
}

Related methods: board.taxonomy.places.list for location autocomplete, board.jobs.list for applying filters, and the headless controller at @cavuno/board/suggest.