# Build Web-Grounded Answers with Novita AI Search and LLMs

> Combine Novita AI Search with the LLM API to build retrieval-augmented generation (RAG) pipelines that answer questions using live web data with citations.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown is available with `Accept: text/markdown` and `.md` URL variants.

Source: /docs/guides/ai-search-grounded-answers

# Web-Grounded Answers

Copy pageCopy page

Copy pageCopy page

A model alone can’t answer questions about events after its training cutoff, or cite a source. The fix is grounding: retrieve relevant web content at request time and pass it to the model as context. This guide shows the pattern using Novita [AI Search](/docs/guides/ai-search-quickstart) for retrieval and the [LLM API](/docs/guides/llm-api) for synthesis — all behind one API key.

##

[​](#what-is-rag)

What is RAG?

Retrieval-augmented generation (RAG) is the pattern behind every grounded answer here: instead of relying on what the model memorized during training, you retrieve relevant documents at request time and augment the prompt with them, so the model generates its answer from that fresh, verifiable context.
That retrieval step is exactly what AI Search provides. The web becomes your knowledge base, and you don’t have to build or maintain a vector database to get started — a search call returns the passages, and the LLM does the rest. When you later want retrieval over your own private documents, the same three-step shape applies; you just swap web search for a vector store.
RAG buys you three things a bare model can’t offer:

- Freshness — answers reflect content published after the model’s training cutoff.

- Attribution — every claim can point back to a source URL the user can check.

- Reduced hallucination — grounding the model in retrieved text keeps it from inventing facts.

##

[​](#how-grounding-works)

How grounding works

A single RAG turn follows three steps:

- Search (retrieve) the web for pages relevant to the question.

- Extract (augment) the content you need — snippets or full page text — into the prompt.

- Synthesize (generate) an answer by passing that content to an LLM, asking it to cite sources.

You can do this with two calls (search + LLM) or even one, since both Exa and Tavily can synthesize an answer for you. Choose based on how much control you need over the final wording and citations.

##

[​](#let-the-provider-write-the-answer)

Let the provider write the answer

The fastest path. Tavily’s Search returns an LLM-generated answer when you set `include_answer`, and Exa offers a dedicated [Answer](/docs/api-reference/model-apis-exa-answer) endpoint. One call, no orchestration.

Tavily

Exa

```
curl -X POST 'https://api.novita.ai/v3/tavily/search' \
-H "Authorization: Bearer ${NOVITA_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"query": "What changed in the latest Python release?",
"include_answer": "advanced",
"max_results": 5
}'
```

```
curl -X POST 'https://api.novita.ai/v3/exa/answer' \
-H "Authorization: Bearer ${NOVITA_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"query": "What changed in the latest Python release?",
"text": true
}'
```

Use this when you want a good answer with minimal code. Reach for the retrieve-then-synthesize approach below when you need control over the model, tone, output format, or how citations are presented.

##

[​](#retrieve-then-synthesize-with-an-llm)

Retrieve, then synthesize with an LLM

This gives you full control. Search for sources, then hand the results to the [LLM API](/docs/guides/llm-api) with instructions to answer using only that context and to cite each claim.

```
import os
import requests
from openai import OpenAI

NOVITA_KEY = os.environ["NOVITA_API_KEY"]
QUESTION = "What changed in the latest Python release?"

# 1. Retrieve relevant web content.
search = requests.post(
"https://api.novita.ai/v3/tavily/search",
headers={"Authorization": f"Bearer {NOVITA_KEY}"},
json={
"query": QUESTION,
"max_results": 5,
"include_raw_content": "text",
},
).json()

# 2. Build a compact, numbered context block the model can cite.
sources = []
for i, r in enumerate(search["results"], start=1):
body = (r.get("raw_content") or r.get("content") or "")[:1500]
sources.append(f"[{i}] {r['title']} ({r['url']})\n{body}")
context = "\n\n".join(sources)

# 3. Synthesize a grounded, cited answer.
client = OpenAI(base_url="https://api.novita.ai/openai", api_key=NOVITA_KEY)
completion = client.chat.completions.create(
model="deepseek/deepseek-v3-0324",
messages=[
{
"role": "system",
"content": (
"Answer using ONLY the provided sources. "
"Cite claims with [n] referring to the source number. "
"If the sources don't contain the answer, say so."
),
},
{"role": "user", "content": f"Question: {QUESTION}\n\nSources:\n{context}"},
],
)
print(completion.choices[0].message.content)
```

The same flow works with Exa Search — swap the route to `/v3/exa/search` and read `text` from each result instead of `raw_content`.

##

[​](#tuning-retrieval-quality)

Tuning retrieval quality

The quality of a grounded answer depends far more on what you retrieve than on the model. A few high-impact knobs:

- Freshness. For time-sensitive questions, constrain by date. Tavily exposes `time_range` and `start_date`/`end_date`; Exa exposes `startPublishedDate`/`endPublishedDate`. This keeps stale pages out of the context.

- Source trust. Use `include_domains`/`excludeDomains` to restrict retrieval to sources you trust (official docs, reputable publishers) and to keep out low-quality sites.

- Topic hints. Tavily’s `topic` (`news`, `finance`, `general`) and Exa’s `category` steer the engine toward the right kind of result.

- Right-sizing context. Cap extracted text (Tavily `chunks_per_source`, Exa `maxCharacters`) so you pass enough signal without blowing the model’s context window or your token budget.

##

[​](#search-vs-extract-vs-crawl)

Search vs. extract vs. crawl

Pick the retrieval primitive that matches your need:

- Search — you have a question and need to discover relevant pages. The default starting point.

- Extract / Contents — you already know the URLs and want clean text from them. Good for grounding on a fixed set of documents. See [Tavily Extract](/docs/api-reference/model-apis-tavily-extract) and [Exa Contents](/docs/api-reference/model-apis-exa-contents).

- Crawl / Map — you want to ingest a whole site or section, following links. Good for building a knowledge base offline. See [Tavily Crawl](/docs/api-reference/model-apis-tavily-crawl) and [Tavily Map](/docs/api-reference/model-apis-tavily-map).

For a fuller agent that searches iteratively and reasons over many sources, see the [DeepSearcher integration](/docs/guides/deepsearcher).

##

[​](#taking-it-to-production)

Taking it to production

- Handle empty results. If search returns nothing relevant, have the model say it can’t answer rather than inventing one. The system prompt above does this.

- Cache when you can. Identical queries return similar results; caching retrieval cuts cost and latency.

- Mind rate limits. Retrieval and LLM calls are limited separately — a search request doesn’t draw down your LLM quota. Back off on `429` for either. See [LLM rate limits](/docs/guides/llm-rate-limits) for the LLM side.

- Keep keys server-side. Your Novita key authenticates every call here — never ship it in client-side code.

Last modified on July 15, 2026
