Give a LangChain Agent a Real-Time Trending Topics Tool
A working LangChain create_agent tool for real-time trending data: Python code, streaming tool results, error handling, quotas and write safety.

You want a LangChain tool that returns real-time data, and the first thing to understand is that a tool is just a named function plus a description the model can read. LangChain's current agent harness, create_agent, is built around four things: a model, a list of tools, a prompt, and middleware. When the model decides it needs fresh information rather than something from its training, it emits a tool call, your function runs, and the result is wrapped in a ToolMessage and fed back into the loop. That is the whole mechanism — there is no separate "real-time mode" to switch on.
So the real work of adding real-time data to a LangChain agent is not the agent part. It is the tool part: authenticating to a live API, handling errors and quota, deciding what to call on every turn, and streaming the tool's output back to your UI. This guide shows all four with runnable Python, plus a short note on how the JavaScript tool API differs, and a ranked set of trade-offs nobody else puts in one table.
How a LangChain tool gets real-time data
LangChain's docs describe create_agent as a minimal, highly configurable harness around model, tools, prompt and middleware, and it replaces the earlier, heavier agent scaffolding. Agents are built on LangGraph, and the docs state they support the same streaming stack for messages, tool calls, state and custom updates. That last point matters for live data: tool output is a first-class stream event, not something buried in the final answer.
The second thing to know is that a live-data tool is not special. LangChain has no native concept of a "real-time data source". Third-party packages fill that gap by shipping prepackaged tools: dynamicfeed-tools advertises adapters for LangChain, CrewAI, LlamaIndex and Pydantic AI, and langchain-talordata brings a SERP API with Google, Bing, News, Images, Shopping, Maps, Scholar and Trends support into LangChain. Those are single-vendor shortcuts. The neutral alternative is to wrap any API you trust behind a function you control — which is what the rest of this article does, using TrendsAGI as the worked example.
Define the tool: Python function, docstring, and what the model sees
In Python you decorate a function with @tool. The function name becomes the tool name, the docstring becomes the description the model reads when deciding whether to call it, and the type hints become the argument schema. Vague docstrings are the most common reason an agent ignores a perfectly good tool, so write the docstring for a reader who has never seen your API.
import os
from langchain_core.tools import tool
from trendsagi import TrendsAGIClient
client = TrendsAGIClient(api_key=os.environ["TRENDSAGI_API_KEY"])
@tool
def list_rising_trends(category: str = "Technology", limit: int = 5) -> list:
"""Return topics gaining attention right now on social platforms.
Use this when the user asks what is trending, what is spiking,
or what people are talking about today. Do not use it for
historical questions or for anything older than a few weeks.
Args:
category: trend category, for example "Technology".
limit: how many trends to return, 1 to 20.
"""
trends = client.get_trends(
category=category, sort_by="velocity", limit=limit
)
return trends
Two details are easy to miss. First, sort_by="velocity" sorts by measured posts-per-hour snapshots, not by forecast — so tell the model that in the docstring or it will describe the numbers as predictions. Second, keep the return value small. A tool that dumps 200 rows into the context window costs money and makes the model worse at summarising. Return five.
Wire it into create_agent: minimum working code
Once the tool exists, the agent is four lines. Pass the model, the tool list, and a system prompt that tells the agent when to reach for live data versus when to answer from what it already knows.
from langchain.agents import create_agent
agent = create_agent(
model="openai:gpt-4.1", # illustrative; use a model your account can call
tools=[list_rising_trends],
prompt=(
"You are a trend analyst. Call list_rising_trends whenever the user "
"asks about current attention. Velocity is a measured snapshot rate, "
"never a forecast. If the tool returns an error, say so plainly."
),
)
result = agent.invoke({
"messages": [{"role": "user",
"content": "What is moving in Technology right now?"}]
})
print(result["messages"][-1].content)
Which model providers work is not something this article can verify beyond what LangChain's own docs expose; the docstring-and-schema contract is what matters, and any provider that supports tool calling respects it. The choice does affect tool calling in practice — some models call tools eagerly and some need prompting — so test your own provider rather than assuming.
A realistic end-to-end agent: find, analyse, propose, wait for approval
A single trending-topics tool answers "what's hot". A useful agent goes further: it checks the analytics behind a trend, reads the AI insight, drafts a campaign change, and stops for a human before anything is executed. That requires more than one tool, and it requires the last step to be a preview, never an action.
@tool
def get_trend_analytics(trend_id: int, period: str = "7d") -> dict:
"""Return volume and growth history for one trend.
Args:
trend_id: numeric id returned by list_rising_trends.
period: one of "24h", "7d" or "30d".
"""
return client.get_trend_analytics(trend_id, period=period)
@tool
def get_trend_insight(trend_id: int) -> dict:
"""Return the AI insight for a trend: summary, key themes,
sentiment, audience and marketing angles."""
return client.get_ai_insights(trend_id)
@tool
def propose_campaign_change(trend_id: int, rationale: str) -> str:
"""Draft a campaign change for a trend. This only PREVIEWS the
change for a human to approve; it never executes anything."""
return (
f"PREVIEW ONLY - no change executed.\n"
f"Trend: {trend_id}\nRationale: {rationale}\n"
"Status: awaiting human approval."
)
analyst = create_agent(
model="openai:gpt-4.1", # illustrative
tools=[list_rising_trends, get_trend_analytics,
get_trend_insight, propose_campaign_change],
prompt=(
"Find the fastest-rising Technology trend, check its 7d analytics, "
"read its insight, then call propose_campaign_change once. "
"Never claim a change was made. Finish by asking the user to approve."
),
)
out = analyst.invoke({"messages": [{"role": "user", "content":
"Find a rising tech trend and draft a campaign tweak for my approval."}]})
The agent chains four tool calls in one turn: discover, quantify, interpret, propose. The propose step is deliberately a stub that returns a preview string. In production that string would come from your own ad-platform preview call — TrendsAGI's connectors for Google Ads, Meta Ads, TikTok Ads and others preview changes before any explicit execution, and the same discipline belongs in your tool. If a tool can spend money, it should never be the last thing the model calls unsupervised.
Streaming tool calls and results back to your app
LangChain's docs say agents support streaming of messages, tool calls, state and custom updates. That is the difference between a chat UI that hangs for eight seconds and one that shows "searching trends…" immediately. The JavaScript docs are explicit that a tool's output can be wrapped in a ToolMessage and that multiple tool calls in one step execute in parallel; the Python-side event names are less documented, so verify the event shape against your installed version rather than trusting a snippet.
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "What is spiking?"}]}
):
# Surface tool activity first, then the reply.
print(chunk)
Your frontend should render three states: tool call started (show the tool name), tool result received (show a one-line summary, not raw JSON), and final message. Anything else and users will assume the app is broken.
Which live-data source should you wrap?
There is no neutral review of the third-party packages available, so judge candidates on criteria rather than brand. Use this table as a checklist.
| Criterion | What to check | Failure mode if you skip it |
|---|---|---|
| Freshness | How often is the underlying data refreshed, and is the timestamp exposed? | Agent confidently reports stale topics as current. |
| Measurement honesty | Does the provider describe rates as measured snapshots or as forecasts? | Your prompt inherits a false prediction framing. |
| Quota and rate limits | Requests per minute, per day, and what happens when you exceed them. | Agent fails mid-conversation with no explanation. |
| Latency | Typical response time, and whether you can cache per trend id. | Every turn pays the full round trip. |
| Error contract | Does the client raise, or return an error object you can inspect? | Exceptions bubble up and kill the agent run. |
On quota specifically: calling a live API on every turn is the default mistake. Cache trend lookups by id for a minute or two, and read your own usage before you scale traffic.
@tool
def check_my_quota() -> dict:
"""Return current API usage for this account."""
return client.get_api_usage()
Safety: read-only by default, writes opt-in
An agent with a live data source is one design decision away from an agent that changes your ad account. Treat read and write as separate tiers. TrendsAGI's agent tools follow this pattern: TrendsAGITools(client) is read-only, and write tools only appear when you pass TrendsAGITools(client, allow_writes=True). Its tools.call() returns a dict — {"result": ...} on success and {"error": ..., "detail": ...} on failure — so a bad parameter degrades the conversation instead of raising.
from trendsagi import TrendsAGITools
tools = TrendsAGITools(client) # read-only
out = tools.call("list_trends", {"category": "Technology"})
if "error" in out:
print("tool failed:", out["detail"])
else:
top = out["result"]["trends"][0]
print(top["id"], top["name"], top["volume"])
You can hand these straight to an agent as OpenAI, Anthropic or LangChain tool schemas via tools.openai(), tools.anthropic() or tools.langchain(). If you prefer a protocol over a package, the hosted MCP server at https://api.trendsagi.com/mcp exposes the same read-only tools to any MCP client, and the local version installs with pip install "trendsagi[mcp]".
Python versus JavaScript tool APIs
LangChain's JavaScript docs create tools with the tool helper and a Zod schema for arguments; the Python side uses a decorator plus type hints and a docstring. The concepts map one to one, but the schema is explicit in JS and inferred in Python. If you are porting a live-data tool, port the docstring text verbatim — it is the part the model reads, and rewriting it changes behaviour.
Frequently asked questions
How do I define a Python function as a LangChain tool for create_agent and what does the docstring do?
Decorate the function with @tool and pass it in the agent's tools list. The docstring becomes the description the model uses to decide when to call it, and the type hints become the argument schema, so write both for a reader who has never seen your API.
What is the minimum code to wire a real-time data API into a LangChain agent?
One decorated function that reads the API key from an environment variable and returns a small result, one create_agent(model=..., tools=[...], prompt=...) call, and one agent.invoke with a user message. Nothing else is required.
How do I pass API keys and handle errors/rate limits inside a tool?
Read keys from environment variables at module load, never from the prompt. Catch client exceptions inside the tool and return a short human-readable failure string, and expose a separate quota tool backed by get_api_usage() so the agent can report limits instead of retrying blindly.
How do I stream tool calls and tool results back to my app instead of waiting for the final answer?
Use agent.stream() and render tool-call and tool-result events as they arrive. LangChain's docs state agents support streaming of messages, tool calls, state and custom updates; confirm the exact event names for your Python version, since the public examples focus on JavaScript.
Which model providers work with create_agent and does the choice affect tool calling?
Any provider that supports tool calling fits the model-plus-tools contract, and the choice does affect behaviour: models differ in how eagerly they call tools and how well they follow a docstring. Test your provider with your own tools rather than assuming parity.
What are the honest trade-offs of live data tools versus cached or offline data?
Live tools buy freshness at the cost of latency, quota and a new failure mode on every turn. Cached data is faster and cheaper but can be wrong the moment the world changes. The pragmatic middle is a short cache keyed on the trend id, refreshed only when the user asks about something current.
How we researched this
Prepared by TrendsAGI Research. Facts checked on 22 October 2026 against the sources listed below.
Capability descriptions and code use TrendsAGI's documented REST API, Python SDK and MCP server. External facts come from the sources below.
| Claim | Applies to | Source |
|---|---|---|
| LangChain agents are built on LangGraph and support the same streaming stack for messages, tool calls, state and custom updates. | All plans | docs.langchain.com |
| A tool's output can be wrapped in a ToolMessage, and when the model calls several tools in one step they all execute in parallel. | JavaScript/TypeScript docs | docs.langchain.com |
| In JavaScript, tools are created with the `tool` helper and a Zod schema describing the arguments. | All plans | docs.langchain.com |
| LangChain integration packages exist for live-data sources, for example dynamicfeed-tools ships adapters for LangChain, CrewAI, LlamaIndex and Pydantic AI. | dynamicfeed-tools | pypi.org |
| langchain-talordata brings a SERP API with Google, Bing, News, Images, Shopping, Maps, Scholar and Trends support into LangChain. | langchain-talordata | pypi.org |
We re-research and rewrite this page at least every four months; the updated date only changes when the facts are re-checked.