Originally published on medium.com

Most âAI picks stocksâ demos are fiction.
You ask a chatbot for undervalued companies, it hands you five tickers, and they sound reasonable. AAPL. MSFT. A bank youâve heard of. It feels like analysis.
It isnât.
The model is reciting names from its training data. It has no idea what those companies trade at today, what their P/E is this quarter, or whether theyâre even still profitable. The numbers it quotes are frozen in time, and half of them are made up.
So I ran a different experiment. I gave Claude a real stock screener as a tool, handed it an actual market goal, and let it do the filtering itself.
This is what happened.
If youâre:
this one is for you.
A language model is a text predictor. It is very good at sounding like a financial analyst and very bad at being one.
Ask it for âthe cheapest profitable semiconductor stocksâ and it will confidently produce a list. But it has no live access to market caps, earnings, or valuations. It is pattern-matching against whatever it absorbed during training, which ended months ago.
Developers discover this the hard way. The picks look fine until you check them and realize the âlow P/Eâ stock the model loved is now trading at three times that, or split, or delisted.
You canât fix this with a better prompt. The data simply isnât in the model.
That single shift changes the whole problem.
The moment Claude can call a screener, it stops guessing and starts orchestrating. It decides what to look for, the API decides whatâs actually true, and Claude reasons over real results instead of inventing them.
The model becomes the analyst. The API becomes the data desk.
For the data layer I used the EODHD Screener API. It exposes the entire stock universe behind one endpoint and lets you filter it with simple conditions.
You pass filters and a sort order, and you get structured JSON back. Each result carries fields you can actually screen on:
No scraping. No unofficial endpoints that break on Mondays. A clean REST call that returns the same shape every time, which is exactly what you need when an LLM is going to consume the output.
That last part matters more than it sounds. Tool use only works if the data coming back is predictable. A screener that returns clean, typed JSON is the difference between an agent that reasons and one that chokes.
If you want to follow along, the screener endpoint and filter fields are documented here.
The architecture is three pieces: a function that runs the screener, a tool definition that describes it to Claude, and a loop that lets Claude call it.
Start with the function. This is the only part that talks to the market.
import os
import json
import requests
EODHD_API_KEY = os.environ["EODHD_API_KEY"]
def run_screener(filters, sort="market_capitalization.desc", limit=20):
url = "https://eodhd.com/api/screener"
params = {
"api_token": EODHD_API_KEY,
"fmt": "json",
"filters": json.dumps(filters),
"sort": sort,
"limit": limit,
}
r = requests.get(url, params=params, timeout=30)
r.raise_for_status()
return r.json()["data"]
Filters are just a list of [field, operator, value] Profitable tech stocks above a billion in market cap is two lines:
run_screener(
filters=[
["sector", "=", "Technology"],
["market_capitalization", ">", 1_000_000_000],
["earnings_share", ">", 0],
],
sort="earnings_share.desc",
)
Now describe that function to Claude as a tool. The description is the part most people rush. Donât. This is how the model knows what it can ask for.
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
screener_tool = {
"name": "run_screener",
"description": (
"Screen US stocks by fundamentals using a live market data API. "
"Returns matching tickers with fields like code, name, "
"market_capitalization, earnings_share, sector, and dividend_yield."
),
"input_schema": {
"type": "object",
"properties": {
"filters": {
"type": "array",
"description": (
"List of [field, operator, value]. Fields include "
"market_capitalization, earnings_share, sector, exchange, "
"dividend_yield. Operators: '=', '>', '<', '>=', '<='."
),
"items": {"type": "array"},
},
"sort": {
"type": "string",
"description": "field.direction, e.g. 'earnings_share.desc'",
},
"limit": {"type": "integer"},
},
"required": ["filters"],
},
}
The last piece is the loop. Give Claude a goal, let it request the screener, feed the real data back, and let it decide.
goal = (
"Find profitable, reasonably valued US technology stocks. "
"Profitable means positive earnings per share. Don't limit yourself to "
"mega-caps; include mid-caps. Return a shortlist of 5 and explain why "
"each one made the cut."
)
messages = [{"role": "user", "content": goal}]
while True:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2000,
tools=[screener_tool],
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
print(resp.content[-1].text)
break
results = []
for block in resp.content:
if block.type == "tool_use" and block.name == "run_screener":
data = run_screener(**block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(data),
})
messages.append({"role": "user", "content": results})
Thatâs the entire system. No vector database, no fine-tuning, no agent framework. One function, one tool, one loop.
I gave Claude the goal above and watched.
The first thing it did was not pick stocks. It picked filters.
It translated âprofitable, reasonably valued techâ into a concrete screen on its own:
[
["sector", "=", "Technology"],
["market_capitalization", ">", 2000000000],
["market_capitalization", "<", 50000000000],
["earnings_share", ">", 0]
]
Notice the choices. It set a floor and a ceiling on market cap to honor ânot just mega-caps.â It required positive EPS for âprofitable.â Nobody told it to do that. It reasoned from the goal to the query.
Then it called the screener, got real tickers back, and narrowed the list with a second pass before writing up its shortlist.
One thing to keep in mind: a screener reads the market as it is today. The exact constituents shift every time you run it, as earnings update and caps move. So this is a representative run, not a fixed list. Run it tomorrow and the names will differ.
Here is the shape of the shortlist it returned and the reasoning it attached to each:

What stood out wasnât the picks. It was the honesty.
Claude flagged its own limits without prompting. It noted that EPS alone doesnât mean a stock is cheap, that it had no forward estimates, and that this was a starting watchlist, not a buy signal.
Thatâs the correct answer. A screener narrows the universe. It does not tell you the future. The model understood the job better than most âAI stock pickerâ demos pretend to.
Where it fell short: with only fundamental snapshot fields, it couldnât reason about momentum, debt, or recent news. Those need more endpoints. The screener is the front door, not the whole house.
â Can AI actually pick stocks? â Not on its own. A language model has no live market data and will invent figures if you let it. Connected to a screener API, it can translate a goal into real filters and reason over real results, which is a very different and far more reliable thing.
â What is the best stock screener API for Python? â You want one that returns clean, structured JSON and lets you filter on fundamentals like market cap, EPS, and sector. EODHDâs Screener API does this through a single REST endpoint, which makes it a good fit for feeding an LLM.
â Do I need an agent framework to build this? â No. The example above is plain Python: one function, one tool definition, one loop. Frameworks add structure when you have many tools, but a single screener doesnât need one.
â Is this safe to trade on? â Treat the output as a research shortlist, not a recommendation. A screener narrows thousands of stocks to a handful worth a closer look. The analysis after that is still on you.
Get a free EODHD key and run the loop above:
Youâll get:
The point was never to let an AI gamble on tickers.
It was to stop the model from guessing, and give it something true to work with.
Thatâs the whole trick.
Looking for technical content for your company? I can help â LinkedIn · kevinmenesesgonzalez@gmail.com