The langchain-apiverve package turns endpoints into LangChain tools with typed argument
schemas, so an agent can call them without you hand-writing a Tool per endpoint. Schemas are
fetched from the published catalog at startup, which means a new endpoint becomes an available
tool without a package upgrade.
pip install langchain-apivervePython 3.9 or later. The agent examples use LangChain 1.x (pip install -U langchain) and a
model provider package, here langchain-openai.
The shortest working example
from langchain.agents import create_agent
from langchain_apiverve import APIVerveToolkit
toolkit = APIVerveToolkit(api_key="apv_...")
tools = toolkit.get_tools(include_apis=["emailvalidator", "disposableemailchecker"])
agent = create_agent(model="openai:gpt-4o", tools=tools)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Is test@example.com a real address?"}]}
)
print(result["messages"][-1].content)Keep the key out of the source. The toolkit reads APIVERVE_API_KEY when you do not pass one:
export APIVERVE_API_KEY="apv_..."toolkit = APIVerveToolkit() # key comes from the environmentSee security for where credentials should live.
Do not give an agent every tool
This is the whole of the advice on this page, and it is the thing most likely to make the difference between an agent that works and one that does not.
get_tools() with no arguments returns a tool for essentially the entire catalog. Every one of
those definitions is serialised into the model's context on every turn. The practical effects
are a large fixed token cost per call, a slower and more expensive agent, and — the real
problem — worse tool selection, because a model choosing between hundreds of similarly-named
options picks wrong more often than one choosing between six.
Four ways to narrow it, in rough order of usefulness:
toolkit.get_tools(categories=["Data Validation", "Geography"])
toolkit.get_tools(include_apis=["emailvalidator", "iplookup", "dnslookup"])
toolkit.get_tools(exclude_apis=["websitescreenshot"])
toolkit.get_tools(max_tools=25)include_apis is the one to reach for in production. An agent with a deliberately chosen
handful of tools is more reliable than one with everything, and you almost always know which
handful the job needs.
To find out what is available while you are deciding:
toolkit.list_categories() # category names
toolkit.list_available_apis() # ids, titles, categories
toolkit.total_apis # how many were loadedCategory names are matched exactly, so use the names list_categories() returns, such as
"Data Validation" rather than "Validation". An unknown category returns no tools.
include_apis and get_tool take the endpoint's id as published — emailvalidator, not
emailvalidation. A mistyped id is silently absent from the returned list rather than an error,
so an agent that "ignores" a tool is often a tool that was never created. Check against
the catalog, or list_available_apis().
Using one endpoint without an agent
A tool is callable on its own, which is often all you want — deterministic enrichment feeding a chain, with no model deciding whether to call it:
toolkit = APIVerveToolkit()
ip_tool = toolkit.get_tool("iplookup")
data = ip_tool.invoke({"ip": "8.8.8.8"})get_tool returns None for an unknown id, so check the result before using it.
This is usually the better design when the call is not conditional. Deciding whether to look up an IP is a job for a model; looking it up is not, and paying a model to make a decision that is already made adds latency and a failure mode for nothing.
Startup and caching
The toolkit fetches schemas from the published catalog when it is first constructed and caches them in memory for the life of the process. So:
Construct the toolkit once, at startup, not per request. Each new process pays the fetch.
Startup requires network access to the catalog. There is no bundled fallback — if the fetch
fails the constructor raises RuntimeError rather than starting with a partial tool set. In a
locked-down environment, allow assets.apiverve.com.
A long-lived process will not see new endpoints until it restarts or you call
load_api_schemas(force_refresh=True).
Errors
Tool failures raise APIVerveError, which carries status_code and the response body:
from langchain_apiverve import APIVerveError
try:
result = ip_tool.invoke({"ip": "not-an-ip"})
except APIVerveError as e:
print(e.status_code, e)The status codes mean what they mean everywhere else — see
error handling. 401 is the only authentication verdict; 429 is a rate
limit or an exhausted balance.
Inside an agent built with create_agent, an APIVerveError is not handed back to the model: it
ends the run and raises from agent.invoke. Catch it there:
try:
result = agent.invoke({"messages": [{"role": "user", "content": "Where is 8.8.8.8?"}]})
except APIVerveError as e:
print(e.status_code, e)That is the right default for a 401 or a 429, which the model cannot fix and would burn turns
retrying. If you want the model to see a 400 and try a corrected argument, catch the error in a
wrapper tool or middleware and return the message as the tool result instead.
What it costs
One call per tool invocation, at that endpoint's rate. Building the toolkit and listing APIs are free.
Agents make this harder to predict than a normal integration, because the number of calls is a
model's decision rather than yours. Two guards worth having from the start: cap each run's steps with
recursion_limit, and give the agent the narrowest tool set that can do the job:
agent.invoke({"messages": [...]}, config={"recursion_limit": 10})Watch the first day's usage in analytics before leaving anything running unattended.
Next
MCP is the other way to give a model these tools, and needs no package at all — worth comparing if your agent framework speaks it. All endpoints is the catalog the toolkit loads.