Disclosure: I build the tools used here (Keyword Search Volume, AI Answer Scraper and AI Search Visibility Tracker on Apify). The sample output below comes from real runs I made on October 19 and 22, 2026; each tool function was run against those saved rows (no new calls), so you see exactly what your agent would read.
An agent with a browser tool can read any page. There are two questions it still cannot answer on its own:
- How many people actually search for this? (demand, CPC)
- What do ChatGPT, Perplexity and the other AI assistants tell buyers about it, and which sources do they cite?
This post wires both into an agent two ways: through MCP (no code) and as plain function-calling tools (about 60 lines of Python). It does not replace a page-content tool: for crawling and RAG ingestion, use something like Apify's Website Content Crawler or RAG Web Browser, Tavily, Exa or Firecrawl. These tools sit next to them.
Option 1: MCP, no code
Apify runs a hosted MCP server and exposes any Actor as a tool. Load only the tools you need, because fewer tools means better tool selection:
https://mcp.apify.com?tools=wrenfield-data/keyword-search-volume,wrenfield-data/ai-answer-scraper,wrenfield-data/ai-search-visibility-tracker
-
Claude Code:
claude mcp add --transport http wrenfield "<the URL above>", then type/mcpand sign in to Apify. -
Claude Desktop, Cursor, VS Code, ChatGPT apps: add the URL as a remote MCP server and sign in with Apify (OAuth). Clients without OAuth can send
Authorization: Bearer <APIFY_TOKEN>. - Each tool shows the agent its input schema, so a prompt like this works as written:
Get US search volume for "crm software" and "email marketing tools". Then check whether Perplexity recommends HubSpot for "What is the best CRM for a small business?" and tell me which keyword to write about first.
Option 2: function tools in your own agent loop
If you run your own loop (OpenAI-style tool calls, Anthropic tool use, LangGraph, anything), each tool is one HTTP call to the Apify API. The helper below runs an Actor synchronously, returns its rows, and passes maxTotalChargeUsd so Apify stops the run at that spend, which is the guardrail you want when a model decides how often to call it.
"""Three agent tools backed by Wrenfield Data Actors on Apify. Each returns a short Markdown string an LLM can read.
Needs: pip install requests ; export APIFY_TOKEN=... Every call is capped with maxTotalChargeUsd."""
import os
import requests
def run_actor(actor: str, payload: dict, max_usd: float = 0.50) -> list[dict]:
"""Run an Apify Actor synchronously and return its dataset rows. Aborts spending beyond max_usd."""
r = requests.post(
f"https://api.apify.com/v2/acts/{actor.replace('/', '~')}/run-sync-get-dataset-items",
params={"maxTotalChargeUsd": max_usd},
headers={"Authorization": f"Bearer {os.environ['APIFY_TOKEN']}"},
json=payload, timeout=300)
r.raise_for_status()
return r.json()
def keyword_demand(keywords: list[str], country: str = "United States") -> str:
"""Monthly Google search volume and CPC per keyword."""
rows = run_actor("wrenfield-data/keyword-search-volume", {"keywords": keywords, "location": country}, max_usd=0.50)
out = ["| keyword | searches / month | CPC (USD) | competition |", "|---|---|---|---|"]
for r in rows:
if r.get("type") != "keyword":
continue
vol = f"{r['search_volume']:,}" if r.get("found") and r.get("search_volume") is not None else "no data"
cpc = f"{r['cpc']:.2f}" if r.get("cpc") is not None else "-"
out.append(f"| {r['keyword']} | {vol} | {cpc} | {r.get('competition') or '-'} |")
return "\n".join(out)
def ai_answer(prompt: str, engines: tuple[str, ...] = ("perplexity", "chatgpt")) -> str:
"""What AI engines answer to a prompt, with the sources they cite."""
rows = run_actor("wrenfield-data/ai-answer-scraper", {"prompts": [prompt], "engines": list(engines)}, max_usd=0.25)
out = []
for r in rows:
if r.get("status") != "ok":
out.append(f"## {r.get('engine_name')}: no answer ({r.get('note')})")
continue
src = ", ".join(r.get("cited_domains", [])[:5]) or "none"
out.append(f"## {r['engine_name']}\n{r['answer_markdown'][:1500]}\n\nCited domains: {src}")
return "\n\n".join(out)
def brand_check(brand: str, domain: str, prompts: list[str], competitors: list[str] | None = None,
engines: tuple[str, ...] = ("perplexity", "chatgpt")) -> str:
"""Does each engine name the brand for each prompt, in what position, and which competitors does it name instead?"""
rows = run_actor("wrenfield-data/ai-search-visibility-tracker",
{"brands": [brand], "domains": [domain], "prompts": prompts, "engines": list(engines),
"competitors": competitors or []}, # e.g. ["Salesforce | salesforce.com", "Pipedrive | pipedrive.com"]
max_usd=1.00)
out = []
for r in rows:
if r.get("type") != "check":
continue
if r.get("status") != "ok":
out.append(f"- {r['engine_name']} | {r['prompt']}: no answer")
continue
said = f"named (position {r['brand_position']})" if r.get("brand_mentioned") else "not named"
rivals = ", ".join(r.get("competitors_mentioned") or r.get("other_brands_mentioned") or []) or "no competitors named"
out.append(f"- {r['engine_name']} | {r['prompt']}: {brand} {said}; others: {rivals}")
return "\n".join(out)
Two design choices that matter for agents:
- Return Markdown or short text, not raw rows. A 31-field JSON row costs tokens and the model ignores most of it. Each function keeps the fields the model needs.
-
Cap every call. The caps above are $0.50, $0.25 and $1.00 per call. At current Free-plan prices,
keyword_demandcosts $0.004 per keyword,ai_answer$0.015 per answer, andbrand_check$0.04 per prompt per engine (the tracker price is scheduled to change to $0.06 on November 2, 2026; check the Actor page).
What the agent reads
keyword_demand(["crm software", "email marketing tools"]) (real rows, Google Ads live source, run on Oct 22, 2026):
| keyword | searches / month | CPC (USD) | competition |
|---|---|---|---|
| crm software | 201,000 | 37.13 | LOW |
| email marketing tools | 4,400 | 101.75 | LOW |
ai_answer("What is the best CRM for a small business?", ("perplexity",)) (real Perplexity answer from Oct 19, 2026, cut in this post; the function itself returns up to 1,500 characters):
## Perplexity
**For most small businesses, HubSpot CRM is the best starting point**: it offers a free entry point, contact management, deal pipelines, and marketing/service features in one ecosystem.[4][6] Choose **Zoho CRM** instead if keeping costs low and gaining broad functionality matter most; choose **Pipedrive** if your main priority is a simple, sales-focused pipeline.[2][4]
| Best for | CRM | Why choose it |
|---|---|---|
| Best overall starting point | **HubSpot CRM** | Easy to adopt, with free tools and sales, marketing, and service capabilities.[4][6] |
[... 4 more rows and a short recommendation list cut for this post ...]
Cited domains: salesforce.com, pcmag.com, techradar.com, wise.com, microsoft.com
brand_check("HubSpot", "hubspot.com", ["What is the best CRM for a small business?"], ["Salesforce | salesforce.com", "Pipedrive | pipedrive.com"]) (real run, Oct 22, 2026):
- Perplexity | What is the best CRM for a small business?: HubSpot named (position 1); others: Salesforce, Pipedrive
- ChatGPT | What is the best CRM for a small business?: HubSpot named (position 1); others: Salesforce, Pipedrive
The brand check returns booleans and positions, not prose, so the agent can branch on brand_mentioned instead of reading an essay.
Putting it together in one prompt
You are a content strategist. For each candidate topic, call
keyword_demand. For the best one, callai_answerandbrand_checkand write a three-line brief: the demand, who the AI assistants recommend today, and one source they cite that we should try to get listed on.
The loop is: demand first (cheap), then AI answers only for the topic that earns it (more expensive per call). That ordering keeps the cost per brief at well under a dollar.
Limits, so you can trust what you see
- AI answers are samples. The same prompt gives a different answer next time and differs between engines. One answer is an anecdote; ten prompts across two engines, tracked weekly, is a signal.
- The volume sample is a paid-plan source. Free Apify plan runs of Keyword Search Volume read a keyword database and are capped at 10 keywords per run.
- Perplexity and ChatGPT agree here (HubSpot first in both), but that is one question in one category, not a general finding.
- Not a replacement for page tools. If your agent needs the text of a page, use a crawler or a search API that returns page content.
Links
- Agent landing page with the full output rows, cost per task and MCP config: https://wrenfielddata.com/search-data-for-ai-agents/
- MCP repo (MIT): https://github.com/wrenfielddata/wrenfield-mcp
- Actors: Keyword Search Volume · AI Answer Scraper · AI Search Visibility Tracker
If a field you need is missing, tell me in the comments and I will look at it.
Top comments (0)