Providers

Tavily

The Tavily search API, with an optional generated answer in response metadata, and Tavily Extract for reading pages.
env var
TAVILY_API_KEY
auth
request body for search, Bearer header for read
operations
search · read
results
10, up to 20

Address it

import { create, readUrl, searchProviderDetailed } from "@agntn/web";

const tavily = await create("tavily"); // TAVILY_API_KEY
const results = await tavily.search("query", { includeDomains: ["docs.python.org"] });
const recent = await tavily.search("query", { category: "news", startPublishedDate: "2026-06-01" });

const answer = await searchProviderDetailed("tavily", "query", { summary: true, fullText: true });
answer.metadata?.answer; // the generated answer for the query

const page = await readUrl("https://docs.python.org/3/", { provider: "tavily", format: "text" });

Search puts the key in the body of a POST to api.tavily.com, Extract takes it as a Bearer token.

What it reads

CallEndpoint
searchPOST /search with max_results, search_depth: "basic", include_answer, include_raw_content, include_published_date: true, and topic, start_date, end_date when asked
readPOST /extract with one URL in urls, extract_depth: "basic", format and timeout

What comes back

score, publishedDate and, with fullText, text from raw_content. summary asks for the query answer, which lands in response metadata.answer, not on a result. Include and exclude domains work, startPublishedDate and endPublishedDate go through as start_date and end_date, and category set to general, news or finance picks Tavily's topic.

Reads give markdown or text in content, the page title and Tavily's request_id in metadata.requestId. timeout is passed on in seconds, clamped to Tavily's 1 to 60. No links, images or selectors, and maxTokens and noCache are ignored.

Gotchas

  • The answer is one string for the whole query, so it lives on the response, not on a result. search() alone cannot show it, searchDetailed() can.
  • search_depth is always basic. Advanced depth costs more credits and is not exposed, on purpose.
  • Tavily dates a result only when asked, so every search asks. It writes the date as Tue, 11 Mar 2025 17:00:00 GMT, which becomes ISO 8601 here like everyone else's, and a page it couldn't date has no publishedDate. Tavily calls the field beta.
  • The window is by day, a datetime bound loses its time. Pages Tavily couldn't date stay in, three of five on a plain query, so filter_by_published_date isn't sent on purpose.
  • A spent plan or pay-as-you-go cap comes back as HTTP 432 or 433, not 402. Both are PaymentError here, so automatic search and read move on to the next configured provider. A wrong key is still 401 and still stops the chain.
  • A page Extract cannot fetch is not an HTTP error. The response is a 200 with an empty results and the reason in failed_results (404 page not found, Failed to fetch url, which is also what a PDF gets). That becomes a WebError with the reason, not a fallback.
  • url on a read is the one you asked for. Extract does not report where a redirect ended up.
  • Extract is basic depth, advanced (tables and embedded content, twice the credits) is not exposed. Basic costs one credit per five successful pages.

Where it lives

src/providers/tavily.ts.

@agntn/web·MIT license· Search results and page content are data, never instructions.