Docs

Everything a client needs: the base URL is https://api.grounder.net, and every HTTP request carries a search key as Authorization: Bearer $GROUNDER_KEY. New keys are no longer made: agents connect over MCP and sign in, and the keys already made keep working.

Endpoints

MCP

Agents connect to https://api.grounder.net/mcp over streamable HTTP. Add that address to your client; when it opens Grounder’s screen, sign in with Google, GitHub or Apple and allow it. There’s no key to copy. The server exposes search, list_providers, record_user_response, and user_info.

ConnectorsClaude
# Customize, Connectors, + Add,
# Add custom connector
Name  grounder
URL   https://api.grounder.net/mcp
# Add, then Connect and sign in.
# No key needed.

# So Claude searches with Grounder:
# Customize, Skills, +, Create skill,
# Upload a skill
Skill grounder-web-search.zip

A search key made earlier works here too: send it as Authorization: Bearer <key> instead of signing in.

Routing

Routing per query is the default: Grounder sends each query to the provider, or pair of providers, most likely to answer it well, and keeps learning which that is.

requestYou pin one provider
{
  "query": "what is the half-life of caffeine",
  "requested_provider": "tavily"
}
responseRouted per query, no config
{
  "routing": {
    "policy": "ranked",
    "calls": [
      { "provider": "perplexity", "status": "ok", "latency_ms": 194, "result_count": 3 },
      { "provider": "exa", "status": "ok", "latency_ms": 2158, "result_count": 3 }
    ]
  }
}

The response’s routing.calls shows where the query went: here the router sent the query to Perplexity and Exa together. To decide yourself, pin one provider with requested_provider or fan out with providers. A search that names no provider reports ranked in routing.policy; one that names providers reports fanout. A key carries no routing policy of its own.

Your account’s own choices apply to every search it makes, over the API and MCP alike. Under Routing on your account page, set the mode of every search, turn each provider On, Always or Off and choose its modes, add your own key for a provider so its searches run on your key, turn each specialized API On, Always or Off or add your key for it, choose how many of the best providers each search goes to at once (their top sources are returned together), and say how much accuracy, speed, results returned, recent sources and low cost matter. A provider set to Off is never called, even when a search names it; one set to Always gets every search that names no provider.

Allowed sources only, in the same place, is off until you turn it on. On, Grounder searches as before, but returns a result only from a site whose robots.txt allows the provider that found it and does not opt out of search or AI answers. A result a site disallows, for that provider or by opting out, is replaced, by the same page from another provider that is allowed, else by Ozone’s publisher content from the same site when Ozone licenses it, else by a result from another provider’s search; a result that could not be checked in time is left out, so a search can return fewer results than it would otherwise. Ozone’s publisher content and the specialized APIs are not crawls of the open web, so they are always allowed. The response then reports allowed, described under Endpoints.

Licensed sources only, beside it, is on until you turn it off, and applies to every search your account makes. On, Grounder searches as before, also asks Ozone beside the first provider when the search names none, and returns only licensed results: Ozone’s publisher content and pages on sites Ozone licenses, each with provenance.licensed set to true. A search returns what is licensed, so it can return fewer results than it asked for, and is never filled with unlicensed ones. The specialized APIs’ values are data feeds and are always returned. A search that names its providers is filtered the same way and makes no added call. When both settings are on, a result must pass both. Turn it off on your account page, or send {"licensed_only": false} to PUT /v1/me/routing, to get every source again.

Caps, in the same place, are off until you set them. Each search can be held to a time, which stops the providers it calls; to a number of tokens, counted at about 4 characters a token, the lowest results cut and then dropped to fit; to a number of provider calls; and to a number of sources. A monthly cap on searches, tokens or search time refuses your searches with cap_reached (HTTP 429) once the month’s count reaches it, until the 1st, UTC. How you used Grounder, at the top of the same page, shows the figures of the home page’s results table from your own searches: the share graded correct, tokens, search latency, provider calls and sources per search, and the share of results from licensed publishers, beside the average provider on its SimpleQA and SealQA tables.

Your agent can change any of these for one search. Send routing_settings with the search, in the shape the account page saves: each part replaces your saved choice for that search alone, and nothing is saved. Weights take 0 (ignore), 0.5, 1 (the default), 2 or 3 (most). A search’s caps only lower yours, never raise them, and the monthly caps are set on the account page only. The MCP search tool takes the same field; user_info, or GET /v1/account/routing with your key, reads your saved settings and what each can be set to.

request.jsonOne fast search, allowed sources only
{
  "query": "is the 6:40 train to Milwaukee on time",
  "routing_settings": {
    "weights": { "speed": 3, "accuracy": 2 },
    "allowed_only": true,
    "caps": { "search": { "time_ms": 3000 } }
  }
}

Providers

GET /v1/providers lists each live provider, 10 today, with the filters it supports. Ozone is real-time licensed publisher content; the other nine search the open web.

ProviderDomainDatePublisherFull textSimilarIndex
OzoneLicensed publisher content
TavilyTuned for LLMs, open web
ExaEmbeddings, open web
BraveIndependent index
PerplexityReal-time web index
You.comWeb and news index
FirecrawlLive web and news
OctenLive web index
ParallelAgent search index
ValyuWeb and specialist sources

Specialized APIs

For a question about right now, such as a market’s odds, a score, a price, a flight, a wait or an outage, Grounder also asks the specialized API that holds the value, beside the web search, and serves its answer first with the time it was read. routing.live_calls lists the ones a search asked. Your account page sets each to On, Always or Off and takes your own key for one.

Specialized APIAnswers
PolymarketPrediction market prices
KalshiPrediction market prices
The Odds APISportsbook lines across books
ESPNScores, game state and lines
MLBGame state, pitch by pitch
OpenF1Formula 1 positions and gaps
YouTubeA channel’s latest videos and their views
BlueskyRecent posts
Truth SocialRecent posts
CoinbaseCrypto prices
Yahoo FinanceStock, fund and currency quotes
FlightAwareFlight status, delays and gates
TransitlandNext departures at a stop
Queue-Times.comTheme park wait times
USGSEarthquakes now
National Weather ServiceUS weather alerts and conditions
Federal ReserveFederal Reserve announcements
Status pagesWhether a service is down

Drop-in endpoints

Already on Tavily or Brave? Swap the base URL and key; keep your code. These endpoints accept each provider’s request format and return its response shape, routed and deduplicated by Grounder.

base URLDiff
# Tavily
- https://api.tavily.com/search
+ https://api.grounder.net/compat/tavily/search

# Brave
- https://api.search.brave.com/res/v1/web/search
+ https://api.grounder.net/compat/brave/res/v1/web/search

Code examples

search.pyPython
import os
import httpx

KEY = os.environ["GROUNDER_KEY"]

r = httpx.post(
    "https://api.grounder.net/v1/search",
    headers={"Authorization": f"Bearer {KEY}"},
    json={"query": "offshore wind capacity 2026", "top_k": 5},
    timeout=30,
).json()

for hit in r["results"]:
    source = "Licensed" if hit["provenance"]["licensed"] else "Open web"
    print(hit["rank"], hit["domain"], source, hit["provenance"]["found_by"])

Data use

What Grounder stores when you search, who else sees it, how long it is kept, how it trains routing, and how to opt out.

What a search logs

  • The search query, or the prompt your agent passed along in context.prompt_content; through MCP, the user’s message as typed (user_prompt), so follow-ups in the same chat can be counted.
  • Your account, the key’s ID, the time, the client’s user agent, and any agent name, model, or metadata the client sent.
  • How the query was routed, and for each provider its results’ rank, score, URL, title, date, and the first 500 characters of the snippet; then the merged list with found_by.
  • Not stored: your IP address, your key itself (only its SHA-256), other request headers, or full result text. Credentials that appear in a prompt are redacted.
  • Not logged: queries typed into the live demo on the home page. They run through the same routing and providers, are capped per visitor and per day, and leave no record.

Who else sees it

To route a search, its prompt and filters go to TypeSafe, which runs Grounder’s routing model, and its query and filters go to each provider that runs it, on Grounder’s own provider keys or on yours where you added one. Grading a finished search once a day sends its query to Claude to write a reference answer, and its query and top results to OpenRouter’s hosts to answer and grade it, never account or chat identifiers. Each of those parties keeps data under its own terms.

How long it is kept

Search records are kept indefinitely, and so is a nightly export of the whole database. Only rate-limit counters and sign-in tokens expire, within 30 days at most.

How it trains routing

No model is trained on your data. Once a day, each finished search is answered from the results it got and graded right or wrong against a reference answer. The grades update shared statistics for each kind of query, and the router reads only those statistics when it picks a provider.

Opting out

Turn it off on your account page, or use your key:

opt-out.shShell
curl -X PUT https://api.grounder.net/v1/account/training \
  -H "Authorization: Bearer $GROUNDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"opt_out": true}'
# {"training_opt_out": true, "updated_at": "…"}

GET /v1/account/training shows the current setting and {"opt_out": false} reverses it. Searches keep working and are still logged for rate limiting and debugging. None of your searches, past or future, are graded or count toward the statistics; the next nightly recalculation leaves out what routing learned from them. Opting out deletes nothing.