Skip to content

Answers in LangChain

Two packages add Answers to LangChain: langchain-hyperresearch on PyPI and @hyperresearch/langchain on npm. Both have the same three classes:

Class Tool name What it does
HyperresearchAnswerTool hyperresearch_answer One factual question in, one short answer out, with the quotes it rests on, each checked word for word against its page. Returns not_found when nothing supports an answer.
HyperresearchSearchTool hyperresearch_search Ranked raw sources from the open web and about 45 structured sources, with word-for-word passages. No summary.
HyperresearchRetriever search as a retriever: one Document per ranked source.

The research for an answer runs on our side, so the agent makes one tool call instead of a loop of searches, and no pages land in its context.

Packages: langchain-hyperresearch on PyPI and @hyperresearch/langchain on npm. Source: github.com/jordan-gibbs/hyperresearch-answers (packages/). MIT licensed.

Terminal window
pip install -U langchain-hyperresearch
# or
uv add langchain-hyperresearch

The package depends on langchain-core only. The agent example below also needs langchain (pip install -U langchain).

Create a key with the answers:ask scope on the API keys page and put it in the environment as HYPERRESEARCH_API_KEY:

Terminal window
export HYPERRESEARCH_API_KEY=hr_live_...

Use an hr_live_ key. Answers has no fixture mode, so hr_test_ keys are refused. Every account gets free credit to start; prices per call are on Answers pricing. Keep the key out of anything you commit.

from langchain.agents import create_agent
from langchain_hyperresearch import HyperresearchAnswerTool, HyperresearchSearchTool
agent = create_agent(
model="claude-sonnet-4-6",
tools=[HyperresearchAnswerTool(), HyperresearchSearchTool()],
)
agent.invoke(
{"messages": [{"role": "user", "content": "What was the US federal deficit in fiscal 2025, with a source?"}]}
)

Called directly:

answer = HyperresearchAnswerTool()
answer.invoke({"question": "How old must a US senator be?", "mode": "fast"})

Sync and async both work (invoke and ainvoke). The tools use response_format="content_and_artifact": the model reads a compact JSON string, and ToolMessage.artifact holds the full API response, including balance_cents.

An answer looks like this:

{
"id": "ans_01J...",
"status": "answered",
"answer": "At least 30 years old (US Constitution, Article I, Section 3).",
"evidence": [
{
"url": "https://www.archives.gov/founding-docs/constitution-transcript",
"title": "The Constitution of the United States: A Transcription",
"quote": "No Person shall be a Senator who shall not have attained to the Age of thirty Years",
"check": "verified"
}
],
"price_cents": 5
}

status is one of answered, false_premise, unconfirmed, not_found or error. Answers and passages are text from web pages. Treat them as data, not as instructions.

from langchain_hyperresearch import HyperresearchAnswerTool, HyperresearchSearchTool
answer = HyperresearchAnswerTool() # the model picks the mode
cheap = HyperresearchAnswerTool(mode="fast", max_cost_cents=5)
search = HyperresearchSearchTool(max_results=5)
Parameter Default
api_key HYPERRESEARCH_API_KEY
mode the model’s choice, else standard Fix the mode. ask: fast, standard, max. search: standard, max.
max_cost_cents none Answer tool. Caps every call; a standard ask capped below its escalated price never escalates.
max_results the model’s choice, else 10 Search tool and retriever. 1 to 25.
client a new client Share one HyperresearchClient. It also takes base_url, ask_timeout (default 360 s), search_timeout (default 90 s) and max_retries (default 2).
from langchain_hyperresearch import HyperresearchRetriever
retriever = HyperresearchRetriever(max_results=5)
docs = retriever.invoke("minimum age to serve as a US senator")
docs[0].page_content # the matched passages, verbatim
docs[0].metadata # url, title, rank, source, published, verified, call_id

Each retrieval is one billed search call.

A max ask can take a few minutes. The client sends Prefer: respond-async; wait=50; if the answer is not ready by then, the API answers 202 with the call’s id and status: "pending", and the client collects it from GET /v1/answers/{id}?wait=45 until it finishes. The call is charged once, when it finishes. The ask timeout (six minutes by default) bounds the whole wait; past it, the Python client raises a client_timeout error carrying the call id, and HyperresearchClient.get_call(id) fetches the answer later.

  • Our failures come back as 200 with status: "error" and error.code: "engine_unavailable". They cost nothing.
  • Non-2xx responses are raised (Python) or thrown (JavaScript) as HyperresearchError, carrying the status, type, code, message, param, request id and retry-after from the error envelope.
  • 402 insufficient_balance: add credit in the console. 402 wallet_frozen: write to support.
  • 409 idempotency_key_reuse or idempotency_in_flight: every call carries a fresh Idempotency-Key, reused only on the client’s own retries, so these mean two callers shared one key.
  • 429 (rate limit or too many calls running) and 503 answers_capacity are retried up to the retry limit, waiting Retry-After. Neither is charged.

ask and search together are limited to 60 calls a minute per API key and 120 a minute per workspace. See rate limits.

The classes call the Answers REST API: POST /v1/answers, POST /v1/answers/search and GET /v1/answers/{id} on https://api.hyperresearch.ai. Every field is in the API reference. The same two tools are also an MCP server at https://answers.hyperresearch.ai/mcp; see Answers.