Giving an LLM fresh web data: SERP APIs for RAG and grounding
A minimal Python retrieval pipeline for grounding an LLM in live search results, and why a fixed credit cost beats an unpredictable per-query bill.

Every model has a training cutoff, and the gap between that date and today only grows. Grounding fixes this the same way it has since retrieval-augmented generation became a standard pattern: search first, read what comes back, then answer from that.
The mechanics are simple. The part worth thinking through is what it costs to run, because that number behaves very differently depending on who controls the search step.
The retrieval step, concretely
Query in, organic results out, fetch the pages worth reading, hand the model a context window instead of a training cutoff:
import requests
API_KEY = "faf_live_..."
def ground(query, top_n=3):
serp = requests.get(
"https://api.fastaf.co/v1/serp",
params={"query": query, "results": top_n},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=60,
)
serp.raise_for_status()
links = [r["link"] for r in serp.json().get("organic_results", [])[:top_n]]
context = []
for link in links:
page = requests.get(
"https://api.fastaf.co/v1/scrape",
params={"url": link},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=60,
)
if page.status_code == 200:
context.append({"url": link, "text": page.json().get("text", "")[:4000]})
return context
sources = ground("latest changes to the EU AI Act")
prompt = "\n\n".join(f"Source: {s['url']}\n{s['text']}" for s in sources)
# pass `prompt` to your LLM call as context, alongside the original question
/serp costs 5 credits, /scrape costs 1 credit for a static page (5 with dynamic=true if a source needs JS rendering). One grounded answer with three sources is 5 + 3 = 8 credits, fixed, every time, regardless of what the model does with the result.
Why the built-in search tools cost something you cannot size in advance
Both major providers now ship a search tool the model can call on its own. Anthropic’s web search tool documentation, checked August 2026, prices it at “$10 per 1,000 searches, plus standard token costs for search-generated content.” OpenAI’s pricing page, checked the same month, lists its standard web search tool at “$10.00 / 1k calls + search content tokens billed at model rates.”
Neither number is the problem on its own. The problem is that the model decides how many searches to run per request, and how much of each result to read. A model with a search tool and a hard question can run one search or ten, depending on how it reasons about the task that turn. Anthropic’s docs say as much directly: “simple factual queries typically use 1-3 searches; comparative or multientity research can use 10 or more.” Multiply that variance by page content billed at model token rates, and the honest answer to “what will this cost per query” is a range, not a number, until the bill arrives.
You can cap search count per request (both providers expose a max-uses style parameter), which limits the blast radius of a single call. It does not make the per-query cost predictable, because token cost still scales with how much of each page the model decides to read.
Running retrieval yourself instead
Calling /serp and /scrape directly from your own code sidesteps the variance in a specific way: you decide top_n, you decide how much of each page to keep before it reaches the model, and the credit cost per grounded call is fixed the moment you write the pipeline, not decided per-request by the model’s own judgment.
The other lever is the key itself. Credits are finite on a FastAF key and reserved when a request starts, so an agent that loops (retries on a bad result, decomposes one question into many searches, gets stuck replanning) hits a hard stop at the balance on that key, not an unbounded bill. For an AI agent calling this pipeline as a tool, that finite balance is the spending cap: size a key to the budget for that agent, and the ceiling is enforced by the API, not by hoping the prompt telling it to “search sparingly” holds up.
def ground_with_budget(query, budget, top_n=3, cost_per_page=1):
call_cost = 5 + top_n * cost_per_page # /serp + /scrape per source
if budget["spent"] + call_cost > budget["max_credits"]:
return {"error": "grounding budget exhausted for this run"}
budget["spent"] += call_cost
return ground(query, top_n=top_n)
The short version
Grounding an LLM in live search is a search call, a handful of page fetches, and a context string, nothing more exotic than that. The part that decides whether it is cheap and predictable or an unpleasant line item at month end is who chooses how many searches and fetches happen: a model reasoning turn by turn, or a pipeline you wrote with a fixed top_n and a key with a finite balance behind it. See pricing for the per-endpoint credit cost, and the docs for the full /serp and /scrape parameter list.