Web Search
Implement the web-search capability with defineWebSearch — the request an extension receives, the recency window, and the published date each result can carry.
Web search is a toolkit capability, not a model family. An extension that can search the
web declares it on a toolkit with defineWebSearch(), and the platform routes every
action.search.web call — the Search block, the Agent's web-search tool, notebooks, and the
/api/platform/v1/action/search/web operation — to the organization's selected provider.
Define the capability
import {
defineToolkit,
defineWebSearch,
} from "@scrydon/sdk-authoring/extensions/define";
import {
WEB_SEARCH_RECENCY,
type WebSearchRecency,
} from "@scrydon/sdk-authoring/extensions/capabilities/web-search";
// Map the platform's recency window to your vendor's own knob.
const FRESHNESS: Record<WebSearchRecency, string> = {
[WEB_SEARCH_RECENCY.DAY]: "day",
[WEB_SEARCH_RECENCY.WEEK]: "week",
[WEB_SEARCH_RECENCY.MONTH]: "month",
[WEB_SEARCH_RECENCY.YEAR]: "year",
};
const webSearch = defineWebSearch({
fetchModels: async () => [{ id: "search-v1", name: "Search v1", capability: "webSearch" }],
async execute(request, ctx) {
const hits = await myVendor.search({
apiKey: ctx.apiKey,
q: request.query,
limit: request.maxResults,
...(request.recency ? { freshness: FRESHNESS[request.recency] } : {}),
});
return {
results: hits.map((hit) => ({
title: hit.title,
url: hit.url,
snippet: hit.summary,
// This vendor reports ISO 8601 with a zone already; convert yours if it does not.
...(hit.published_at ? { publishedDate: hit.published_at } : {}),
})),
vendor: "my-vendor",
};
},
});
export const searchToolkit = defineToolkit({
id: "search",
name: "My Search",
credentialRef: "apiKey",
webSearch,
});The request
| Field | Type | Meaning |
|---|---|---|
query | string | Free-text query. |
maxResults | number? | How many results to return. Clamp to your vendor's limit. |
page | number? | 1-indexed page. |
safe | "active" | "off"? | Safe-search mode. Omitted: the vendor's own default (Google's is off). |
language | string? | BCP-47 language tag, such as en. |
recency | "day" | "week" | "month" | "year"? | Only return results published within that window. Omitted: no date restriction. |
Use WEB_SEARCH_RECENCY instead of writing the four values out. If your vendor has no
freshness filter, ignore recency. The caller still gets results, just not filtered by date.
The response
Each result carries title, url, an optional snippet, and an optional publishedDate.
publishedDate is one of two ISO 8601 shapes, as precise as your vendor reports it:
- a calendar date, such as
2026-09-20; - a date-time that states its zone with
Zor±hh:mm, such as2026-09-20T08:15:00Z,2026-09-20T10:15+02:00or2026-09-20T10:15:30.250Z. Seconds and fractions are optional.
Set it only when the vendor gives you a real date, and convert any other vendor format to one
of these shapes yourself. Never guess. A date-time without a zone does not name an instant, and
a year alone or 09/20/2026 does not name a day, so leave publishedDate out for those.
API Platform checks publishedDate before the result leaves it. A value that is not one of the
two shapes above is dropped. It does not fail the search, and it is not converted.
Built-in providers
The built-in Google Custom Search provider maps recency to
dateRestrict (d1, w1, m1, y1); its block starts on Any time, which sends no
dateRestrict. It reads publishedDate from the page's metatags, checking these in order:
article:published_time, datepublished, og:published_time, pubdate, date, dc.date,
article:modified_time, og:updated_time. The first tag holding a date wins. A tag value in
another format is converted only when its day is unambiguous and, for a time, its zone is stated.
Sep 20, 2026 and 2026/09/20 become 2026-09-20; 09/20/2026, 2026, and a zone-less time
are skipped.