LLM Agent Tools: API Function-Calling Patterns for Ad Research
Give your LLM agent ad research tools: function schemas for ad search and advertiser resolve, credit-economy system prompts, result compression, and evals.

Sections
Ask an LLM agent what ads a competitor is running and you'll get a confident answer either way. The difference between fiction and research is tooling. This guide covers LLM agent tools for ad research: the API function schemas, the system-prompt rules that govern a credit economy, result compression, multi-step research loops, and an eval suite. Every pattern here is framework-agnostic — it works in any function-calling stack, whether you build on OpenAI, Claude, Gemini, or a local model.
TL;DR: An agent with an ad-search tool stops hallucinating competitor claims and starts citing in-market creative. Build two core tools first (a free
resolve_advertiserprobe and a paidsearch_adscall), write credit rules into the system prompt, compress results before they touch the context window, and test the loop with trace-level evals. Tool design decides whether a research run burns 5 credits or 50.
Why Agents Hallucinate Competitor Claims Without Tools
A bare LLM answers from training data, and ad accounts turn over weekly. Ask any frontier model "what hooks is Gymshark running on Meta right now" and it will describe athlete UGC and transformation testimonials in fluent, specific prose. Some of that comes from articles written eighteen months ago. Some of it never ran at all. The model has no way to know, and neither do you.
That's an information-access problem, not a model-quality problem, which is why prompt engineering alone never fixes it. An AI agent fixes it with function calling: the model emits a structured request (call search_ads with keyword="gymshark"), your runtime executes the real HTTP call, and live results land back in the conversation as ground truth. OpenAI's function calling guide documents the loop, and every other provider runs the same dance with different envelope syntax.
The shift in output quality is blunt. Before tools: "Gymshark relies heavily on influencer-driven UGC." After tools: "12 of Gymshark's 30 longest-running Meta ads are athlete UGC. The longest has run 89 days with a heat score of 812." One claim is decoration. The other is competitive intelligence a media buyer can act on, with an ad_key behind every sentence.
Ad research happens to be an unusually good first domain for agentic tooling. The data is structured, every claim is verifiable against a creative that actually exists, and there's a built-in cost meter (credits) that punishes sloppy tool design immediately instead of six months later.
The Tool Surface: Mapping LLM Agent Tools to API Endpoints
Before any schema design, decide which capabilities the agent gets. The AdLibrary API exposes a compact set of REST endpoints, and they map cleanly onto verbs an agent can reason about:
| Agent tool | Backing endpoint | Cost |
|---|---|---|
resolve_advertiser | GET /api/advertisers/search | Free |
search_ads | POST /api/search | 1 credit per call |
track_brand | POST /api/advertisers + /curate | 1 credit per session |
enrich_ad | POST /api/enrichment | 1 credit |
confirm_page | GET /api/winners/search-pages | Free |
scan_winners | POST /api/winners/advertiser/{pageId} | 10 credits flat |
check_credits | POST /api/credits | Free |
Two design rules sit above everything else.
First, tools are verbs, not endpoints. The agent shouldn't think in REST semantics. track_brand wraps a save call plus a curate call behind one intention. Your wrapper handles auth, retries, and parameter hygiene so the model only handles research decisions.
Second, make free and paid visibly different. The free probes exist so the agent never pays to discover something a zero-cost call would have told it. Encode that split in naming, in descriptions, and later in the system prompt. It is the single most important idea in this entire architecture.
A positioning note before we build: Meta's own Ad Library API is free, official, and the right tool surface if your agent researches political and social-issue ads on Facebook and Instagram — transparency is exactly what it was built for. Commercial coverage is where it stops, and we documented the specific walls in Meta Ad Library API limitations. The AdLibrary API is the paid upgrade for the commercial job: brand and product ads across 11 platforms (Facebook, Instagram, TikTok, YouTube, Google, LinkedIn, Twitter, Pinterest, Yahoo, Unity Ads, AdMob) behind one adl_ key, with no app review. If the category is new to you, start with what an ad library API is.
Designing search_ads: The Core LLM Agent Tool Schema
The schema is the contract between model and API, written in JSON Schema. Here's a production-shaped definition:
{
"name": "search_ads",
"description": "Search live commercial ads across 11 platforms (Meta, TikTok, YouTube, Google, LinkedIn and more). COSTS 1 CREDIT per call, including every page of pagination and identical repeats. Returns ad copy, creative URLs, impression bands, estimated spend, heat score (0-1000), and runtime in days. Prefer one well-filtered call over several broad ones.",
"input_schema": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "Brand, product, or ad-copy phrase to search for."
},
"platform": {
"type": "array",
"items": {
"enum": ["facebook", "instagram", "tiktok", "youtube",
"google", "linkedin", "twitter", "pinterest"]
},
"description": "Omit to search all platforms in one call."
},
"geo": {
"type": "string",
"description": "Country code, e.g. US, GB, DE."
},
"adsType": {
"enum": ["1", "2", "3", "4"],
"description": "1=image, 2=video, 3=carousel, 4=collection."
},
"daysBack": {
"enum": [7, 14, 30, 60, 90, 180, 365],
"description": "Recency window in days."
},
"sortField": {
"enum": ["-impression", "-heat_degree", "-days", "-first_seen"],
"description": "-days surfaces the longest-running (proven) ads. -heat_degree surfaces what is trending right now. Sort by signal, not date."
},
"page": {
"type": "integer",
"description": "Each page is a fresh 1-credit search. Stay on page 1 unless instructed otherwise."
}
},
"required": ["keyword"]
}
}
Five decisions in that schema are worth copying into your own build:
- The description carries the economics. "COSTS 1 CREDIT per call" inside the tool description is the highest-impact line of prompt engineering in the whole system. Models weigh tool descriptions heavily when deciding whether and how to call.
- Enums constrain instead of documenting.
daysBackaccepts specific windows on the live API. Shipping them as an enum means the model can't inventdaysBack: 45and eat a 400 error. - Hide what the model can misuse. The live endpoint requires an
appTypevertical parameter. Hardcode it in your wrapper ("3"for e-commerce research) rather than exposing a knob the model has no basis to set. - Teach strategy in field docs. The
sortFielddescription doubles as research method. An agent that reads "-days surfaces proven ads" makes better calls than one that sees a bare enum. - Clamp in code as well as prose. Your wrapper should cap
pageat 2 andpageSizeat 60 regardless of what the model asks for. Schema descriptions persuade. Wrappers enforce.
Handle the response with the same care. Pass _credits.remaining back to the model on every call so it always sees its budget. The response also carries a total count, which makes the first page double as market sizing: "4,213 video ads matching 'protein powder' active in the last 30 days" costs the same single credit as the page itself. And pass two honesty caveats through: impressions arrive as bands rather than exact counts, and spend figures are always estimates. An agent that reports false precision is hallucinating with extra steps — when a spend number drives a decision, sanity-check the magnitude against the ad spend estimator.
The resolve_advertiser Tool: Free Probes First
When the user names a brand, the LLM agent's opening move should cost nothing.
{
"name": "resolve_advertiser",
"description": "FREE (0 credits). Resolve a brand name to its platform advertiser IDs: Meta page ID, Google advertiser ID, LinkedIn company ID. Returns a best_match with a cross-platform confidence score, plus per-platform candidates. ALWAYS call this before any paid tool when the user names a brand.",
"input_schema": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "Brand name, e.g. 'gymshark'." },
"country": { "type": "string", "description": "Two-letter country code, default US." }
},
"required": ["q"]
}
}
Brand names are ambiguous in ways that quietly poison research. "Nike" is Nike, Nike Football, and Nike Run Club, each a separate advertiser account. The resolve endpoint fans out across Meta, Google, and LinkedIn and returns a best_match with a confidence score when at least two platforms agree on the same normalized brand. A paid search against the wrong advertiser wastes the credit and, worse, feeds the agent a polluted result set it will confidently summarize.
For recurring competitor tracking, pair the probe with saved advertisers. Saving a brand with its platform IDs is free, and a track_brand call then pulls every recent ad it runs across all platforms for one credit per 30-minute session. That pairing is the backbone of automated competitor ad monitoring, and the full scheduled pipeline is covered in our end-to-end monitoring guide.
System-Prompt Rules for the Credit Economy
Schemas describe LLM agent tools one at a time. The system prompt sets policy across them, and policy is where the 5-versus-50 outcome gets decided for every LLM agent you ship. A concrete block you can adapt:
CREDIT POLICY (non-negotiable):
1. check_credits and resolve_advertiser are FREE. Start every research
run with check_credits. Refuse paid calls below 5 credits and explain.
2. resolve_advertiser ALWAYS precedes a brand-specific paid call.
3. search_ads costs 1 credit per call. Repeats and pagination are charged
in full. Never re-run a query from this session. Never go past page 2
without explicit user approval.
4. enrich_ad costs 1 credit per ad. Only enrich shortlisted ads
(runtime >= 30 days OR heat >= 700). Maximum 3 enrichments per run
unless the user raises the cap.
5. scan_winners costs 10 credits flat. Call confirm_page (free) first to
verify the page ID, and ask the user before spending.
6. End every run by reporting credits used and credits remaining.
Trace one task through both regimes: "research the protein powder market and tell me what's working." An agent without policy fires a separate search per platform (11 credits), paginates the three biggest result sets to page five (12 more), then enriches every ad that looks interesting (another 20). Roughly 43 credits, and the answer isn't better — it's a longer version of the same answer. The disciplined agent runs one all-platform search sorted by -days (1 credit), reads market size from the total field in the same response, enriches the top 3 shortlisted ads (3 credits), and reports. Four credits, same model, same question. The delta is pure policy.
Prompt rules bend under long contexts, so enforce the caps in your wrapper too. Track per-run spend in code and raise a BudgetExceededError when a paid call would cross the ceiling. That error string returns to the model as a tool result, which means the agent reads it and self-corrects instead of crashing. If you run Claude Code, this same policy block drops into CLAUDE.md and the enforcement layer comes nearly free — the Claude Code + adlibrary API workflows guide shows that setup end to end.

Summarize Before You Stuff: Result Compression
A single search returns up to 60 ads, each a fat JSON object with creative URLs, engagement counts, geo arrays, and timestamps. Dumped raw into the conversation, that's routinely 25,000+ tokens of mostly-noise — and because the context is re-read on every subsequent turn, you pay for it again each time the agent thinks. Attention dilutes, earlier reasoning falls out of the window, and the model starts skimming its own evidence.
This is the step most LLM agent tools skip on day one, and it shows up later as vague answers over data the model technically received. Compress at the tool boundary instead. The wrapper, not the model, decides what enters context:
FORMATS = {1: "image", 2: "video", 3: "carousel", 4: "collection"}
def summarize_ad(ad: dict) -> dict:
return {
"key": ad["ad_key"], # citation handle
"advertiser": ad.get("advertiser_name"),
"platform": ad.get("platform"),
"format": FORMATS.get(ad.get("ads_type")),
"hook": (ad.get("title") or ad.get("body") or "")[:120],
"runtime_days": ad.get("days_count"),
"heat": ad.get("heat"),
"impressions_band": ad.get("impression"), # a band, not a count
}
def tool_result(resp: dict, top_n: int = 12) -> dict:
ads = sorted(resp["results"],
key=lambda a: a.get("days_count") or 0, reverse=True)
cache.store(resp["results"]) # full objects, locally
return {
"total_matching": resp.get("total"),
"credits_remaining": resp["_credits"]["remaining"],
"ads": [summarize_ad(a) for a in ads[:top_n]],
}
Four choices in there do the heavy lifting:
- Keep
ad_keyon every summary. It's the citation handle. Every claim in the agent's final answer should trace back to a key it actually fetched, which the eval suite below will assert mechanically. - Two-tier storage. Full creative objects go to a local cache, and a free
expand_ad(ad_key)tool reads from that cache. The agent can drill into any ad without another API call and without a paid repeat search. - Truncate copy to the hook. The first 120 characters are where the hook lives, and the hook is what creative analysis actually needs at shortlist stage.
- Return
credits_remainingevery time. A running budget meter inside the context beats a rule the model has to remember.
The arithmetic: 60 raw ads at 25,000+ tokens becomes 12 summaries under 1,000. The agent reasons over five searches' worth of compressed evidence in less space than one raw response used to take.
Multi-Step Research Loops That Don't Burn the Budget
With the tools and policy in place, the canonical LLM agent research loop for "what's working in this niche" looks like this:
- Probe, free.
check_creditsfor the balance,resolve_advertiserif a brand was named. - Search once, sorted by signal. One credit.
-daysfor proven creative,-heat_degreefor momentum. Thetotalfield in the same response answers any market-size question at no extra cost. - Compress and shortlist, free. The wrapper summarizes, and the agent picks the 2-3 ads that clear runtime or heat thresholds.
- Enrich the shortlist. One credit per ad. AI enrichment returns a scene-by-scene transcript, a persuasion teardown, and a 1:1 replication brief per ad — structured raw material for a creative brief.
- Synthesize, free. A cited summary with
ad_keyreferences, ending with the credit report.
Total: four to six credits for a grounded, citable answer. Notice what the loop never does. It never searches before resolving, never paginates on a hunch, and never enriches an ad that hasn't earned shortlist status through runtime or heat. Every paid call is justified by a free or already-paid signal one step earlier, which is the probe-before-pay rule expressed as workflow rather than instruction.
Three variants extend the same skeleton.
Brand deep-dive. When the question is "which of their ads actually win," scan_winners replaces steps 2-4. It scores an advertiser's whole portfolio, dedupes variants into concepts, and returns winner and loser tiers with plain-language evidence like "outran 12 variants on the same landing page." Flat 10 credits, so the policy gates it behind a free confirm_page check and explicit user approval.
Scheduled monitoring. A nightly job runs track_brand for each saved competitor, diffs returned ad_keys against yesterday's cache, and only enriches genuinely new concepts. New creative gets flagged the morning it appears. The n8n workflow recipes implement this without custom code, and a cron script does it in 30 lines.
Brief generation. Chain the loop's output into a deliverable: from competitor ad to creative brief in 20 minutes walks the single-brief version, and AI ad analysis at scale covers the batch version agencies run across a client roster. When the resulting brief feeds media planning, the agent's collected spend estimates and format mix drop straight into the ad budget planner.
An Eval Suite for Ad Research Agents
Nobody ships an API endpoint without tests, yet most builders ship LLM agent tools on vibes. Agents regress silently: a prompt tweak that improves tone can double credit spend, and a model upgrade can quietly break probe-first ordering. The fix is a small fixed set of research prompts with assertions on the trace, not the prose.
CASES = [
{
"prompt": "What angles is Gymshark running on Meta right now?",
"expect_first_tool": "resolve_advertiser",
"max_credits": 5,
},
{
"prompt": "How big is the yoga mat ad market in the US?",
"expect_tools": ["search_ads"],
"max_credits": 1, # market size = total field, one search
},
{
"prompt": "Run a winners scan on that page.",
"expect_first_tool": "confirm_page",
"max_credits": 10,
},
]
def run_case(agent, case):
trace = agent.run(case["prompt"])
calls = [c.name for c in trace.tool_calls]
# 1. Probe-before-pay ordering
if "expect_first_tool" in case:
assert calls[0] == case["expect_first_tool"], calls
# 2. Credit ceiling
assert trace.credits_used <= case["max_credits"]
# 3. Citation grounding: every cited ad_key was actually fetched
cited = extract_ad_keys(trace.final_answer)
fetched = {a["key"] for r in trace.tool_results
for a in r.get("ads", [])}
assert cited <= fetched, cited - fetched
# 4. No duplicate paid calls
queries = [c.args for c in trace.tool_calls if c.name == "search_ads"]
assert len(queries) == len({json.dumps(q, sort_keys=True)
for q in queries})
The citation-grounding assertion earns its keep first. It's a direct hallucination test: if cited - fetched is non-empty, the model invented an ad that was never in any tool result, and no amount of fluent prose excuses that. The credit ceiling catches economic regressions the same way a latency budget catches performance ones.
Track three numbers across runs over time: pass rate, median credits per case, and tokens per case. A "better" prompt that doubles credit spend is a regression even when every answer reads beautifully. This suite, more than any model choice, is the gap between a demo and dependable agentic AI — and if you'd rather start from working scripts than abstractions, the Python ad library API cookbook has the raw call layer to build on.
Wiring LLM Agent Tools Into Any Function-Calling API
The LLM agent tool schemas above are deliberately provider-neutral. One definition, four transports:
- OpenAI. Pass the schema under
toolswith"type": "function". The model returnstool_calls, and you answer withrole: "tool"messages. Details in the function calling docs. - Anthropic. Same JSON Schema under
toolswith aninput_schemakey. Claude returnstool_useblocks and expectstool_resultblocks back, per the tool use documentation. - Gemini. Function declarations inside a
toolsarray, using the same OpenAPI-flavored schema subset, per the Gemini function calling docs. - Open and local models. Most function-calling fine-tunes accept OpenAI-format definitions verbatim, so the same wrapper serves your self-hosted stack.
Whatever the transport, the wrapper layer is where the real engineering lives: the Authorization: Bearer adl_... header, retries that honor Retry-After on 429 responses (the API allows 10 requests per minute and 10,000 per day per key), the budget caps, the cache, and the compression. Run it as one small internal service so every agent in your org shares a single enforcement point. The Node.js ad intelligence service pattern shows that shape in TypeScript.
If your stack speaks Model Context Protocol, the same tool layer ships as an MCP server instead of inline definitions — identical schemas, different envelope. Build your own adlibrary MCP server does it in 60 lines of Python, and the Meta ads MCP setup guide covers wiring it into Claude Code. For orchestrating beyond a single agent, the patterns in Claude API marketing automation compose with everything here.
What a Tooled-Up Agent Costs to Run
The whole pattern compresses to one sentence: LLM agent tools for ad research are an API contract, a credit policy, and a compression layer, and the quality of those three decides everything downstream — answer accuracy, run cost, and whether your team trusts the output.
The economics favor discipline hard. API access sits on AdLibrary's Business plan at €329/mo with 1000+ monthly credits, full API access, free integration help, and prioritized feature requests. At four to six credits per disciplined run, that's roughly 200 grounded research runs a month from the included credits. A naive agent burning 50 credits per question gets 20 runs from the same balance. Same subscription, ten times the output, and the only difference is the tool design covered in this guide.
Start small and in order. Ship the two-tool core (resolve_advertiser + search_ads) against API access, add the eval suite before you add more verbs, then grow into enrichment, winners scans, and scheduled monitoring as the evals stay green. The broader playbook for feeding ad data to AI agents covers what to build once the core loop is dependable, and the Business plan is the tier built for exactly this kind of automation.
Frequently Asked Questions
Which tools does an LLM agent need for ad research?
Two LLM agent tools cover most runs: a free resolve_advertiser probe that maps a brand name to platform advertiser IDs, and a paid search_ads function over a commercial ad library API. Add enrich_ad for creative teardowns, check_credits for budget awareness, and a 10-credit scan_winners for portfolio scoring once the core loop is stable.
How do I stop an agent from wasting API credits?
Use three layers. Put costs in every tool description ("COSTS 1 CREDIT per call"), encode a credit policy in the system prompt (free probes before paid calls, enrichment caps, no duplicate searches), and enforce hard budget caps in wrapper code so a runaway loop raises an error the model can read and correct.
Do these function-calling patterns work with OpenAI, Claude, and Gemini?
Yes. All three accept JSON Schema tool definitions with minor envelope differences: OpenAI uses a tools array with type "function", Anthropic uses input_schema with tool_use blocks, and Gemini uses function declarations. Define each schema once and adapt the envelope, or expose the same tools through an MCP server.
Why not use Meta's free Ad Library API as the agent's tool?
Meta's Ad Library API is free and works well if you need political and social-issue ads on Meta platforms, which is its transparency mandate. For commercial ads, multi-platform coverage, and performance signals like heat score and runtime, a paid ad library API such as AdLibrary's is the upgrade built for research and automation.
How many credits does one agent research run cost?
A disciplined loop costs four to six credits: free brand resolution, one well-filtered search, and three to five enrichments. A winners portfolio scan is a flat 10. Without probe-first rules and caps, the same question can burn 50+ credits on redundant searches and blanket enrichment.
Related Articles

Claude Code + adlibrary API: End-to-End Competitor Intelligence Workflows
Run five Claude Code workflows against the adlibrary API for automated competitor monitoring: Slack alerts, bulk teardowns, hook extraction across 500 ads, monthly landscape reports, and new entrant detection.

adlibrary MCP server: build your own in 60 lines of Python
Build a custom adlibrary MCP server with fastmcp in 60 lines of Python. Expose ad search, timeline, and enrichment as Claude tools—pair with Meta Ads MCP.

Python Ad Library API Scripts: A Working Cookbook
Five copy-paste Python ad library API scripts: resolve brands, search into pandas, track watchlists, scan winners, and batch-enrich ads with caching.

AI Ad Analysis at Scale: Enriching Competitor Ad Creatives via API
AI ad analysis via API: turn 200 competitor ads into queryable rows of hooks, claims, and creative structure. Batch enrichment, cost math, caching.

How to Automate Competitor Ad Monitoring End to End
Build Slack competitor ad alerts in 30 minutes: an incoming webhook, a Node poll script with first_seen filtering, dedup on ad_key, and cron scheduling.

Node.js Ad Intelligence Service: Wrap an Ad API in Your Stack
Wrap the AdLibrary API in a Node.js ad intelligence service: typed client, credit-aware caching, webhook-out alerts, fixtures, and Railway/Coolify deploys.

Claude API for Marketing Automation: Patterns, Stacks, and Real Workflows
Build production-grade marketing automations with Claude API: bulk ad copy, competitor monitoring, email triage, and SEO pipelines. Includes prompt caching Python examples.

Run Paid Ads from Claude Code: The Terminal-First Ad-Ops Playbook
Step-by-step playbook for Google, Meta, and LinkedIn ad ops from Claude Code in your terminal — read-before-write rollout, weekly rituals, what stays human.