LlamaIndex web search integration

Add ODEN web search to LlamaIndex as a tool or custom retriever: bring real-time web knowledge, with citations, into your LlamaIndex agents and query engines.

Use case1 min readUpdated 2026-07-30

LlamaIndex is an index-first framework for structuring and querying private knowledge bases. Connecting ODEN introduces temporal public web retrieval directly into LlamaIndex's indexing hierarchy without requiring custom scraping middleware.

As a LlamaIndex FunctionTool#

Expose ODEN as a callable tool for LlamaIndex OpenAIAgent or ReActAgent instances:

import os, requests
from llama_index.core.tools import FunctionTool

def query_live_web(search_topic: str) -> str:
    """Query current public web intelligence, returning a concise summary and validated references."""
    endpoint = "https://api.oden-api.com/search"
    auth_header = {"Authorization": f"Bearer {os.environ['ODEN_KEY']}"}
    payload = {"query": search_topic, "include_snippets": True, "depth": "advanced"}

    response = requests.post(endpoint, headers=auth_header, json=payload, timeout=25)
    response.raise_for_status()
    payload_data = response.json()["results"]

    formatted_sources = "\n".join(
        f"• {item['title']} - {item['url']} (confidence: {item['score']:.2f})"
        for item in payload_data["citations"]
    )
    return f"Synthesized Finding:\n{payload_data.get('answer','')}\n\nAttributed Citations:\n{formatted_sources}"

oden_tool = FunctionTool.from_defaults(
    fn=query_live_web,
    name="live_internet_research",
    description="Fetches verified, up-to-date web information with source citations."
)

As a custom LlamaIndex retriever#

Transform ODEN citations directly into native NodeWithScore objects for seamless inclusion in composable query engines:

import os, requests
from llama_index.core.retrievers import BaseRetriever
from llama_index.core.schema import NodeWithScore, TextNode

class OdenWebRetriever(BaseRetriever):
    def _retrieve(self, query_bundle):
        headers = {"Authorization": f"Bearer {os.environ['ODEN_KEY']}"}
        req_body = {
            "query": query_bundle.query_str,
            "include_snippets": True,
            "depth": "advanced"
        }
        resp = requests.post("https://api.oden-api.com/search", headers=headers, json=req_body, timeout=25)
        resp.raise_for_status()

        nodes_with_scores = []
        for cit in resp.json()["results"]["citations"]:
            text_chunk = cit.get("snippet") or f"{cit['title']}: {cit['url']}"
            meta_info = {"title": cit["title"], "source_url": cit["url"]}
            node = TextNode(text=text_chunk, metadata=meta_info)
            nodes_with_scores.append(NodeWithScore(node=node, score=float(cit["score"])))

        return nodes_with_scores

SubQuestionQueryEngine for multi-hop research#

For complex research questions that require decomposing a query into sub-problems, pair ODEN with LlamaIndex's SubQuestionQueryEngine:

from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool, ToolMetadata

# Wrap the ODEN retriever into a dedicated query engine
oden_engine = custom_query_engine_from_retriever(OdenWebRetriever())

tools = [
    QueryEngineTool(
        query_engine=oden_engine,
        metadata=ToolMetadata(
            name="live_web_engine",
            description="Searches the live public internet for current technical and news events."
        )
    ),
    QueryEngineTool(
        query_engine=internal_vector_engine,
        metadata=ToolMetadata(
            name="internal_docs_engine",
            description="Searches company private knowledge base and internal engineering wikis."
        )
    )
]

sub_engine = SubQuestionQueryEngine.from_defaults(query_engine_tools=tools)
response = sub_engine.query("Compare our internal Q3 roadmap goals with emerging regulatory requirements in the EU.")

Custom node post-processing#

Use LlamaIndex's node postprocessors to filter citation nodes by confidence score:

from llama_index.core.postprocessor import SimilarityPostprocessor

# Prune retrieved citation nodes with score below 0.65
postprocessor = SimilarityPostprocessor(similarity_cutoff=0.65)
filtered_nodes = postprocessor.postprocess_nodes(nodes)

Asynchronous query execution with aquery#

For modern async microservices built with FastAPI or asyncio event loops, execute LlamaIndex query engines asynchronously:

# Non-blocking query execution in asynchronous event loops
response = await sub_engine.aquery(
    "Synthesize the current status of EU generative AI model safety benchmarks."
)
print(str(response))

Notes#

  • The tool is best for agents that decide when to search; the retriever is best for query engines that expect nodes.
  • score maps straight onto NodeWithScore, so LlamaIndex ranking works out of the box.
  • Combine OdenWebRetriever with your own index retriever for hybrid private + web retrieval.

FAQ#

Can I combine ODEN with a LlamaIndex vector index?#

Yes. Use a router or a composable retriever: your vector index for private data, OdenWebRetriever for the live web.

Does ODEN return LlamaIndex nodes directly?#

No — it returns JSON. The OdenWebRetriever above maps that JSON to NodeWithScore in a few lines.

Which is better, tool or retriever?#

Tool for agentic flows where the model chooses to search; retriever for deterministic query engines. Many apps use both.

Build it on the free tier
1,000 searches a month, no card required.