Blog

Give a Vercel AI SDK Agent Live Web Search

By Kartey R

Founder of Serpex

Updated 7 min read
Technology

Your AI SDK agent knows everything up to its training cutoff and nothing after that. The fix is one tool. Wire the Serpex TypeScript SDK into a tool() definition, hand it to generateText or streamText, and the model can pull live web search results into its own reasoning whenever it decides it needs them. Serpex is a web search API built for AI agents: one call returns up to 10 results and, on request, the top pages as Markdown. New accounts get 200 free credits with no card.

This walks through the whole thing: install, define the tool, wire it up with multi-step calling so the agent can act on what it finds, and a variant that hands back full page content instead of just a snippet.

What do I need first?

A Serpex API key. Sign up at app.serpex.dev and you get 200 free credits with no card required. Grab the key from your dashboard and put it in an environment variable, never in the code itself.

bash
# .env.local
SERPEX_API_KEY=sk_your_api_key

You'll also need the AI SDK already set up with a model provider (we'll use @ai-sdk/openai in the examples below, but any provider works the same way).

How do I install the SDK?

bash
npm install serpex

That's the whole install. No config step, no separate types package. SerpexClient and all its types ship from the same import.

How do I define the search tool?

This is the part that matters. A tool for the AI SDK is a zod schema (what the model is allowed to pass in) plus an execute function (what actually runs). We use inputSchema, not parameters, since that's the current AI SDK naming.

typescript
// lib/tools/web-search.ts
import { z } from "zod";
import { tool } from "ai";
import { SerpexClient } from "serpex";
const serpex = new SerpexClient(process.env.SERPEX_API_KEY!);
export const webSearch = tool({
description:
"Search the live web for current information. Use this for anything recent, time sensitive, or outside your training data: news, prices, current events, docs for a library, current versions.",
inputSchema: z.object({
query: z.string().describe("The search query"),
}),
execute: async ({ query }) => {
const { results } = await serpex.search({ q: query });
return results.map((r) => ({
title: r.title,
url: r.url,
snippet: r.snippet,
}));
},
});

We keep the returned object small on purpose. The model gets titles, URLs, and snippets, which is usually all it needs to answer or to decide which page to read next. client.search() also returns metadata.credits_used if you want to log spend per call, but there's no need to pass that back to the model.

One thing that trips people up the first time: SerpexClient throws a SerpApiException on a non-2xx response (bad key, no credits, rate limit). If you don't want a bad search to kill the whole agent turn, catch it inside execute and return something the model can react to instead of letting it bubble up.

typescript
execute: async ({ query }) => {
try {
const { results } = await serpex.search({ q: query });
return results.map((r) => ({ title: r.title, url: r.url, snippet: r.snippet }));
} catch (error) {
return { error: "Search failed, try a different query." };
}
},

How do I wire it into generateText?

Pass the tool in the tools object, keyed by whatever name you want the model to see:

typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { webSearch } from "./lib/tools/web-search";
const { text } = await generateText({
model: openai("gpt-4o"),
tools: { webSearch },
prompt: "What's the current weather like in Lisbon, and what should I wear?",
});
console.log(text);

Run this as is and the model can call webSearch once. Most real questions need more than that: search, notice the first result doesn't fully answer it, search again with a narrower query, then write the answer. For that you need multi-step tool calls, which brings us to the part that's easy to get wrong.

How do I let the agent search more than once?

In the current AI SDK, a single generateText call only goes back to the model with tool results, and lets it keep calling tools, when you set stopWhen. Without it, the agent stops after the first round of tool calls whether or not it actually has an answer.

typescript
import { generateText, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { webSearch } from "./lib/tools/web-search";
const { text, steps } = await generateText({
model: openai("gpt-4o"),
tools: { webSearch },
stopWhen: stepCountIs(5),
prompt: "What's the latest stable version of Next.js, and what changed in it?",
});
console.log(text);
console.log(`Took ${steps.length} step(s)`);

stepCountIs(5) caps the run at five model steps, so a confused model can't run up your bill searching forever. It's also the easiest setting to miss. Leave it out and the agent stops after one search, mid-thought, with no error and no warning. It just quietly returns whatever it had. If your agent seems to ignore search results or cut answers short, check stopWhen before anything else.

streamText takes the exact same stopWhen option and streams the whole multi-step run, tool calls included:

typescript
import { streamText, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { webSearch } from "./lib/tools/web-search";
const result = streamText({
model: openai("gpt-4o"),
tools: { webSearch },
stopWhen: stepCountIs(5),
prompt: "What's the latest stable version of Next.js, and what changed in it?",
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}

How do I get full page content instead of just snippets?

Sometimes a snippet isn't enough: the model needs to actually read the page to answer well. Set include_content: true on the search call and Serpex fetches each top result's page and returns it as markdown, in the same request, no separate crawl call needed.

typescript
export const webSearchWithContent = tool({
description:
"Search the live web and return the full page content (as markdown) for each top result, not just a snippet. Use this when a summary won't be enough and the model needs to actually read the page.",
inputSchema: z.object({
query: z.string().describe("The search query"),
}),
execute: async ({ query }) => {
const { results, metadata } = await serpex.search({
q: query,
include_content: true,
content_results: 5,
});
return {
delivered: `${metadata.content_delivered}/${metadata.content_requested}`,
pages: results.map((r) => ({
title: r.title,
url: r.url,
content: r.content ?? null,
content_error: r.content_error ?? null,
})),
};
},
});

Page content is best-effort: some sites block fetching or disallow it in robots.txt, and those results come back with content_error instead of content. Serpex documents this at roughly 70% of requested results actually delivering content, so always check for content_error and let the model fall back to the snippet in your prompt or tool description.

It costs more than a plain search, and the billing is based on what actually came back, not what you asked for: 3 credits when 1 to 5 pages are delivered, 6 credits when 6 to 10 are delivered, and just the plain search rate of 1 credit if none deliver. A normal search without include_content is 1 credit. Repeat the same query within about 5 minutes and it's free either way.

FAQ

Does this work with streamText the same way as generateText? Yes. Both take the same tools and stopWhen options, and both return a steps array once the run finishes.

Why isn't my agent calling the tool a second time? Almost always a missing stopWhen. Without it, generateText and streamText stop after the first batch of tool calls even if the model still has more to do.

Can I use this in a Next.js API route or server action? Yes, that's the normal setup. Keep SERPEX_API_KEY server side only. Never ship it to client-side JavaScript.

What happens if the search fails? SerpexClient throws a SerpApiException with a statusCode and details. Catch it inside execute so a failed search becomes a message the model can react to, not a crash.

Try Serpex

Sign up for 200 free credits, no card required, and try the tool against your own agent today. The docs cover the TypeScript SDK and the quickstart, and pricing lists the pay-as-you-go rates, with credits that never expire.

Serpex Blog

Related Articles

More content you might be interested in

Oct 24, 2026 · 8 min read

Vibe Coding Web Search: Add a Search API With Cursor, Claude Code or Lovable

Add web search to an app you build with AI, no coding needed: get a key, give your agent the docs, keep the key server-side. Cursor, Claude Code, Lovable.

Technology
Read More
Oct 24, 2026 · 14 min read

Search APIs for Enterprise AI (2026): Security, Rate Limits and Cost at Scale

Questions to ask before signing a search API contract for enterprise AI: security, retention, SLAs, rate limits, billing, and cost at 1M-10M searches.

Technology
Read More
Oct 23, 2026 · 15 min read

PHP2Shell vs the Knowledge Cutoff: CVE-2026-109618, Tested Oct 2026

Only 3 of 36 top-3 search results for PHP2Shell came from the PHP project. We built a 91-line agent that checks CVE-2026-109618 against primary sources.

Technology
Read More
Oct 19, 2026 · 19 min read

Which Search API Is Cheapest at 100k, 1M and 10M Searches a Month? (Prices Checked October 2026)

Search API costs at 100k, 1M and 10M searches a month, checked Oct 23, 2026. Lowest list: $0.20/1k. Serpex: $0.50-$0.80, from $0.20 at 10M+.

Technology
Read More
Oct 19, 2026 · 8 min read

How to Add Web Search to an AI Workflow or RAG Pipeline (Python, 2026)

How to add web search to an AI workflow or RAG pipeline in Python (2026): one call returns pages as Markdown to chunk, rank and cite, with real monthly costs.

Technology
Read More
Oct 19, 2026 · 8 min read

Tavily Pricing in 2026: What 10k, 100k and 1M Searches Cost

Tavily pricing in 2026: $8 per 1,000 credits pay as you go, $5 on Growth. Monthly cost at 10k, 100k and 1M searches, advanced depth, Extract and alternatives.

Technology
Read More
Oct 19, 2026 · 8 min read

Serper vs Tavily (2026): Pricing, Output and Which to Use

Serper vs Tavily in 2026: Serper costs $0.30 to $1.00 per 1,000 searches, Tavily $5 to $8. Free tiers, output, page content, LangChain support and which to use.

Technology
Read More
Oct 19, 2026 · 8 min read

Google Custom Search API Is Closing: What to Use by January 1, 2027

The Google Custom Search JSON API is closed to new users and existing ones must move by January 1, 2027. Replacements and 2026 costs at 10k and 100k searches.

Technology
Read More
Oct 3, 2026 · 7 min read

Web Search in n8n: HTTP Request, AI Agent or Community Node

Add web search to an n8n workflow with the HTTP Request node, use it as an AI Agent tool with $fromAI(), or install the n8n-nodes-serpex community node.

Technology
Read More