### GET /console/keys Source: https://groundroute.ai/docs/authentication Lists all API keys for the tenant, returning metadata only (never the key itself). ```APIDOC ## GET /console/keys ### Description Lists all API keys for the tenant, returning metadata only (never the key itself). ### Method GET ### Endpoint /console/keys ### Response #### Success Response (200) - **id** (string) - The key ID. - **key_prefix** (string) - A masked display prefix. - **name** (string) - The key name. - **status** (string) - The key status. - **created_at** (string) - Creation timestamp. - **last_used_at** (string) - Last used timestamp. #### Response Example [ { "id": "…", "key_prefix": "gr_live…XYZ9", "name": "production", "status": "active", "created_at": "2023-01-01T00:00:00Z", "last_used_at": "2023-01-02T00:00:00Z" } ] ``` -------------------------------- ### MCP Server Configuration Source: https://groundroute.ai/docs/mcp-server Configuration example for connecting an MCP client to the GroundRoute MCP server. ```APIDOC ## MCP Server Configuration ### Description Configuration for connecting an MCP client to the GroundRoute MCP server. ### Endpoint https://api.groundroute.ai/mcp ### Transport streamable HTTP ### Authentication Authorization: Bearer ### Configuration Example { "mcpServers": { "groundroute": { "type": "http", "url": "https://api.groundroute.ai/mcp", "headers": { "Authorization": "Bearer gr_live_YOUR_KEY" } } } } ``` -------------------------------- ### GET /v1/usage Source: https://groundroute.ai/docs/search-api Documentation for the GET /v1/usage endpoint, including response and error handling. ```APIDOC ## GET /v1/usage ### Description Returns a usage summary for the authenticated tenant. Detailed usage analytics are exposed through the console API under `/v1/console`. ### Method GET ### Endpoint /v1/usage ### Response #### Success Response (200) - **usage summary** (object) - Usage summary for the authenticated tenant. #### Response Example ```json { "usage": {} } ``` ### Error Handling Errors use the standard error envelope: ```json { "error": { "type": "auth", "message": "…", "request_id": "…", "retryable": false } } ``` | Status | When | |--------|------| | `401` | Missing / invalid / revoked key (`auth`). | | `402` | Billing block — insufficient credit, no-free-managed, free cap exceeded, or spend hard-stop (`invalid_request`). | | `403` | Account inactive (`invalid_request`). | | `422` | Invalid request body. | | `429` | Rate limit or concurrency cap (`rate_limit`). | | `500` | Internal error (`unavailable`, retryable). | | `200` + `degraded: true` | Partial results — one engine failed but at least one returned. **Not** an error. | ``` -------------------------------- ### cURL request with API key Source: https://groundroute.ai/docs/authentication Example of using the API key as a Bearer token in a cURL request to the search endpoint. ```bash curl -s https://api.groundroute.ai/v1/search \ -H "Authorization: Bearer $GROUNDROUTE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "hello world"}' ``` -------------------------------- ### Call GroundRoute compatibility endpoint with httpx Source: https://groundroute.ai/docs/integrations Python example using httpx to POST to the /v1/search/compat endpoint. Only the URL and key change; the request body uses common SDK fields like query and max_results. Returns a Tavily/OpenAI-style body. ```python import httpx httpx.post( "https://api.groundroute.ai/v1/search/compat", # only the URL + key change headers={"Authorization": f"Bearer {GROUNDROUTE_KEY}"}, json={"query": "pgvector vs qdrant benchmark", "max_results": 5}, }.json() # → {"answer": ..., "results": [...]} ``` -------------------------------- ### Search API Request with cURL Source: https://groundroute.ai/docs/search-api Example of a POST request to the search endpoint using cURL, including authorization and JSON body parameters. ```bash curl -s https://api.groundroute.ai/v1/search \ -H "Authorization: Bearer $GROUNDROUTE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "transformer attention complexity", "mode": "academic", "max_results": 10, "freshness": "static", "domains": ["arxiv.org"], "lang": "en" }' ``` -------------------------------- ### Search API Response Structure Source: https://groundroute.ai/docs/search-api Example of a successful 200 OK response from the search endpoint, containing results, metadata, and usage information. ```json { "request_id": "8f1c2e7a-…", // uuid "tenant_id": "…", "results": [ { "url": "https://arxiv.org/abs/...", "title": "Attention Is All You Need", "snippet": "…", "content": null, // full body only when mode/engine provides it "score": 0.82, // normalized relevance, 0–1, or null "source_engine": "exa", // serper|brave|exa|tavily|perplexity|firecrawl "published_at": null, // ISO 8601 or null "rank": 0 // 0-based position as served } ], "answer": null, // synthesized answer (answer engines), or null "citations": [ // present when an answer cites sources { "url": "https://...", "title": "…", "index": 0 } ], "degraded": false, // true = partial results (one engine failed) "routing_meta": { "strategy": "price_led", // price_led | learned | fanout "query_class": "academic", // academic | web | news | page | answer "chosen_engine": "exa", "reason": "price_led:exa $7.00/1k (first eligible for academic, source=static)", "candidates": [ { "engine": "exa", "cost_per_1k_usd": 7.0, "eligible": true, "picked": true, "note": "cheapest eligible" }, { "engine": "brave", "cost_per_1k_usd": 5.0, "eligible": true, "picked": false, "note": null } ], "cost_usd": 0.007, // actual provider cost incurred this request "latency_ms": 412, "degraded": false, "failover_from": null, // engine that failed before chosen_engine, or null "missing_engines": [] }, "cache_meta": { "cache_tier": "miss", // miss | exact_private | exact_pooled | semantic "poolable": false, "poolability_reason": "cell_not_allowlisted", "screens_post_nfkc": true, "freshness_bucket": "none", // hourly | daily | none "cache_key_private": null, // sha256 hex (no raw query) "cache_key_pooled": null, // set only when poolable "cached_at": null, // ISO 8601 when a hit "ttl_seconds": null, "cost_avoided_usd": 0.0, // provider $ saved by this hit "similarity": null // semantic tier only }, "quality_meta": { "quality_score": null, // composite 0–1, or null when not scored "components": {}, "judge_sampled": false, "engine_agreement_rbo": null }, "usage_meta": { "request_id": "8f1c2e7a-…", "tenant_id": "…", "account_id": "…", "api_key_id": "…", "ts": "2026-06-14T07:48:14Z", "billable": true, "searches_counted": 1, "provider_key_source": "platform", // platform (managed) | byok "cost_usd": 0.007 } } ``` -------------------------------- ### Use GroundRouteRetriever in LangChain Source: https://groundroute.ai/docs/integrations Replaces a TavilySearchAPIRetriever with GroundRouteRetriever(k=5) in LangChain. The commented lines show the previous Tavily setup. The helper is imported from groundroute_langchain. ```python # LangChain — before: # from langchain_community.retrievers import TavilySearchAPIRetriever # retriever = TavilySearchAPIRetriever(k=5) from groundroute_langchain import GroundRouteRetriever retriever = GroundRouteRetriever(k=5) ``` -------------------------------- ### Example normalized search response Source: https://groundroute.ai/docs/quickstart The response includes results plus meta blocks for routing, cache, quality, and usage. Headers also carry X-Request-Id, X-Cache-Tier, and X-Degraded. ```json { "request_id": "8f1c…", "tenant_id": "…", "results": [ { "url": "https://example.com/...", "title": "pgvector vs Qdrant: a benchmark", "snippet": "…", "source_engine": "serper", "rank": 0 } ], "answer": null, "citations": [], "degraded": false, "routing_meta": { "strategy": "price_led", "chosen_engine": "serper", "cost_usd": 0.001 }, "cache_meta": { "cache_tier": "miss", "cost_avoided_usd": 0.0 }, "quality_meta": { "quality_score": null }, "usage_meta": { "cost_usd": 0.001, "provider_key_source": "platform", "billable": true } } ``` -------------------------------- ### GET /console/provider-keys Source: https://groundroute.ai/docs/byok Lists all stored provider keys with masked information. Returns one entry per stored key, including id, provider, fingerprint_prefix, status, and created_at. Never returns plaintext keys. ```APIDOC ## GET /console/provider-keys ### Description Lists all stored provider keys with masked information. Returns one entry per stored key, including id, provider, fingerprint_prefix, status, and created_at. Never returns plaintext keys. ### Method GET ### Endpoint /console/provider-keys ### Parameters None ### Response #### Success Response (200) - **id** (string) - The unique identifier for the stored key. - **provider** (string) - The provider name. - **fingerprint_prefix** (string) - A short non-reversible fingerprint prefix of the key. - **status** (string) - The status of the key. - **created_at** (string) - The timestamp when the key was created. #### Response Example [ { "id": "…", "provider": "serper", "fingerprint_prefix": "a1b2c3d4e5f6", "status": "active", "created_at": "2026-06-14T07:48:14Z" } ] ``` -------------------------------- ### Use groundrouteSearch in Vercel AI SDK Source: https://groundroute.ai/docs/integrations Replaces a tavilySearchTool with groundrouteSearch() in the Vercel AI SDK. The commented lines show the previous setup. The helper is imported from "./groundroute". ```javascript // Vercel AI SDK — before: // const tools = { search: tavilySearchTool(...) }; import { groundrouteSearch } from "./groundroute"; const tools = { search: groundrouteSearch() }; ``` -------------------------------- ### Error envelope JSON example Source: https://groundroute.ai/docs/errors-and-limits Shows the shared JSON shape of all 4xx/5xx error responses. Includes the error type, message, request_id (a UUID to quote in support requests), and a retryable flag. The type can be one of: timeout, auth, rate_limit, http, parse, unavailable, invalid_request. ```json { "error": { "type": "rate_limit", // see ErrorType below "message": "Rate limit exceeded", "request_id": "8f1c2e7a-…", // uuid — quote this in support requests "retryable": true } } ``` -------------------------------- ### POST /console/keys Source: https://groundroute.ai/docs/authentication Creates a new API key for the tenant. Returns the plaintext key once, along with metadata. ```APIDOC ## POST /console/keys ### Description Creates a new API key for the tenant. Returns the plaintext key once, along with metadata. ### Method POST ### Endpoint /console/keys ### Parameters #### Request Body - **name** (string) - Optional - A name for the key. ### Request Example { "name": "production" } ### Response #### Success Response (200) - **id** (string) - The key ID. - **api_key** (string) - The plaintext API key, shown only once. - **key_prefix** (string) - A masked display prefix. - **name** (string) - The key name. #### Response Example { "id": "…", "api_key": "gr_live_…", "key_prefix": "gr_live…XYZ9", "name": "production" } ``` -------------------------------- ### Configure MCP server for Claude Desktop or Cursor Source: https://groundroute.ai/docs/quickstart Add the GroundRoute MCP server to mcp.json so an agent can call the search tool natively. Point any MCP client at https://api.groundroute.ai/mcp with the same Bearer key. ```json { "mcpServers": { "groundroute": { "type": "http", "url": "https://api.groundroute.ai/mcp", "headers": { "Authorization": "Bearer gr_live_YOUR_KEY" } } } } ``` -------------------------------- ### POST /console/provider-keys Source: https://groundroute.ai/docs/byok Adds a new provider key. The key is validated with a live call to the provider before storage. Returns 201 on success, 400 for invalid or provider-rejected keys, and 503 if the provider is temporarily unreachable. ```APIDOC ## POST /console/provider-keys ### Description Adds a new provider key for BYOK. The key is validated with a live call to the provider before storage. ### Method POST ### Endpoint /console/provider-keys ### Parameters #### Request Body - **provider** (string) - Required - The provider name (e.g., `serper`, `brave`, `exa`, `tavily`, `firecrawl`, `perplexity`). - **key** (string) - Required - The API key for the provider. ### Request Example { "provider": "serper", "key": "your-serper-api-key" } ### Response #### Success Response (201) - **id** (string) - The unique identifier for the stored key. - **provider** (string) - The provider name. - **fingerprint_prefix** (string) - A short non-reversible fingerprint prefix of the key. - **status** (string) - The status of the key (e.g., `active`). - **created_at** (string) - The timestamp when the key was created. #### Response Example { "id": "…", "provider": "serper", "fingerprint_prefix": "a1b2c3d4e5f6", "status": "active", "created_at": "2026-06-14T07:48:14Z" } #### Error Responses - **400** - Invalid or provider-rejected key. - **503** - Provider temporarily unreachable for validation (transient — retry). ``` -------------------------------- ### Search with Python using httpx Source: https://groundroute.ai/docs/quickstart Perform the same search using Python and httpx. It prints each result's rank, title, and URL, plus the chosen engine and cache tier. ```python import os, httpx resp = httpx.post( "https://api.groundroute.ai/v1/search", headers={"Authorization": f"Bearer {os.environ['GROUNDROUTE_API_KEY']}"}, json={"query": "pgvector vs qdrant benchmark", "mode": "auto", "max_results": 5}, timeout=15, ) resp.raise_for_status() data = resp.json() for r in data["results"]: print(r["rank"], r["title"], r["url"]) print("engine:", data["routing_meta"]["chosen_engine"], "| cache:", data["cache_meta"]["cache_tier"]) ``` -------------------------------- ### Add a provider key via POST /console/provider-keys Source: https://groundroute.ai/docs/byok Adds a BYOK key for a provider. The request body contains the provider and key. The response (201) returns metadata including a fingerprint prefix, but never echoes the key itself. ```json // request { "provider": "serper", "key": "your-serper-api-key" } // response (201) — note: the key itself is never echoed back { "id": "…", "provider": "serper", "fingerprint_prefix": "a1b2c3d4e5f6", "status": "active", "created_at": "2026-06-14T07:48:14Z" } ``` -------------------------------- ### Set GroundRoute environment variables Source: https://groundroute.ai/docs/integrations Exports the GROUNDROUTE_API_KEY and GROUNDROUTE_BASE_URL environment variables required for all GroundRoute integrations. The base URL defaults to https://api.groundroute.ai. ```bash export GROUNDROUTE_API_KEY=gr_live_... export GROUNDROUTE_BASE_URL=https://api.groundroute.ai ``` -------------------------------- ### Set GROUNDROUTE_API_KEY environment variable Source: https://groundroute.ai/docs/quickstart Export the API key as an environment variable. The plaintext key is shown only once, so store it in a secret manager. ```bash export GROUNDROUTE_API_KEY="gr_live_xxxxxxxxxxxxxxxxxxxx" ``` -------------------------------- ### MCP client configuration for GroundRoute Source: https://groundroute.ai/docs/mcp-server Add this mcpServers block to your MCP client config (Claude Desktop, Cursor, etc.) to connect to GroundRoute. Replace gr_live_YOUR_KEY with your actual API key. Restart the client to see the search tool under the groundroute server. ```json { "mcpServers": { "groundroute": { "type": "http", "url": "https://api.groundroute.ai/mcp", "headers": { "Authorization": "Bearer gr_live_YOUR_KEY" } } } } ``` -------------------------------- ### search tool Source: https://groundroute.ai/docs/mcp-server The search tool allows MCP-capable agents to search the web via GroundRoute. It returns ranked results, an optional synthesized answer, and citations. ```APIDOC ## search tool ### Description Search the web via GroundRoute. Returns ranked results, an optional synthesized answer, and citations. ### Parameters | Parameter | Type | Default | Notes | |-----------|------|---------|-------| | query | string | - | Required. | | mode | string | auto | auto, web, news, academic, answer, or page. Unknown values fall back to auto. | | max_results | int | 10 | Clamped to 1-50. | | freshness | string | null | fresh, semi, or static. Other values ignored. | | domains | string[] | null | Include-filter. | | lang | string | null | ISO 639-1. | | country | string | null | ISO 3166-1 alpha-2. | ### Result Shape { "results": [ { "url": "https://...", "title": "...", "snippet": "...", "source_engine": "serper", "published_at": null } ], "answer": null, "citations": [ { "url": "https://...", "title": "...", "index": 0 } ], "meta": { "request_id": "...", "cache_tier": "miss", "degraded": false, "cost_usd": 0.001 } } ``` -------------------------------- ### Set default routing policy via API Source: https://groundroute.ai/docs/routing-and-policies Updates the workspace default routing policy using the console API. The policy must be one of price, quality, latency, or balanced. ```bash curl -s -X PUT https://api.groundroute.ai/v1/console/settings \ -H "Content-Type: application/json" \ --cookie "gr_session=…" \ -d '{"default_policy": "balanced"}' ``` -------------------------------- ### DELETE /console/provider-keys/{key_id} Source: https://groundroute.ai/docs/byok Revokes a stored provider key. Returns 404 if the key does not belong to the user. ```APIDOC ## DELETE /console/provider-keys/{key_id} ### Description Revokes a stored provider key. Returns 404 if the key does not belong to the user. ### Method DELETE ### Endpoint /console/provider-keys/{key_id} ### Parameters #### Path Parameters - **key_id** (string) - Required - The unique identifier of the key to delete. ### Response #### Success Response (200) No content. #### Error Responses - **404** - Key not found or not owned by the user. ``` -------------------------------- ### POST /console/keys/{key_id}/rotate Source: https://groundroute.ai/docs/authentication Revokes the old key and issues a new one in a single step. Returns the new plaintext key once. ```APIDOC ## POST /console/keys/{key_id}/rotate ### Description Revokes the old key and issues a new one in a single step. Returns the new plaintext key once. ### Method POST ### Endpoint /console/keys/{key_id}/rotate ### Parameters #### Path Parameters - **key_id** (string) - Required - The ID of the key to rotate. ### Response #### Success Response (200) - **api_key** (string) - The new plaintext API key, shown only once. #### Response Example { "api_key": "gr_live_…" } ``` -------------------------------- ### Search with curl using Bearer token Source: https://groundroute.ai/docs/quickstart Send a search request to POST /v1/search with the API key as a Bearer token. The response is normalized and includes meta blocks. ```bash curl -s https://api.groundroute.ai/v1/search \ -H "Authorization: Bearer $GROUNDROUTE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "pgvector vs qdrant benchmark", "mode": "auto", "max_results": 5}' ``` -------------------------------- ### Create API key response Source: https://groundroute.ai/docs/authentication The JSON response returned when creating a new API key, which includes the plaintext key that must be stored immediately. ```json { "id": "…", "api_key": "gr_live_…", // shown once — store it now "key_prefix": "gr_live…XYZ9", "name": "production" } ``` -------------------------------- ### DELETE /console/keys/{key_id} Source: https://groundroute.ai/docs/authentication Revokes the key immediately. The revoked key's auth cache is purged so it stops authenticating at once. ```APIDOC ## DELETE /console/keys/{key_id} ### Description Revokes the key immediately. The revoked key's auth cache is purged so it stops authenticating at once. ### Method DELETE ### Endpoint /console/keys/{key_id} ### Parameters #### Path Parameters - **key_id** (string) - Required - The ID of the key to revoke. ### Response #### Success Response (200) No content. ``` -------------------------------- ### Request processing flow for POST /v1/search Source: https://groundroute.ai/docs Visual representation of the request lifecycle, showing authentication, rate limiting, cache lookup, engine routing, and metadata return. ```text client → POST /v1/search → auth → rate-limit → cache lookup │ hit ───────┘ (return cached, $ avoided) miss → classify → route to cheapest engine │ engine fails → failover → engine │ return results + routing_meta + cache_meta + usage_meta ``` -------------------------------- ### GroundRoute AI Langflow component methods Source: https://groundroute.ai/docs/integrations/langflow This snippet contains the core methods of the GroundRoute AI Langflow component: _to_records, _meta, _error, search_data, and search_dataframe. It handles API requests, error responses, and formats results for Langflow. ```python @staticmethod def _to_records(resp: dict[str, Any]) -> list[dict[str, Any]]: records: list[dict[str, Any]] = [] for r in resp.get("results", []) or []: records.append({ "url": r.get("url", ""), "title": r.get("title", ""), "snippet": r.get("snippet", ""), "content": r.get("content"), "source_engine": r.get("source_engine", ""), "published_at": r.get("published_at"), }) return records @staticmethod def _meta(resp: dict[str, Any]) -> dict[str, Any]: cache_meta = resp.get("cache_meta") or {} usage_meta = resp.get("usage_meta") or {} return { "request_id": resp.get("request_id"), "cache_tier": cache_meta.get("cache_tier"), "degraded": resp.get("degraded", False), "cost_usd": usage_meta.get("cost_usd"), } def _error(self, message: str) -> list[Data]: logger.error(message) self.status = message return [Data(data={"error": message})] def search_data(self) -> list[Data]: if not self.api_key: return self._error( "GroundRoute API key is required. Get one at https://groundroute.ai/keys?utm_source=langflow" ) try: response = httpx.post( f"{_BASE_URL}/v1/search", json=self._build_payload(), headers={"Authorization": f"Bearer {self.api_key}"}, timeout=_TIMEOUT_S, ) except httpx.HTTPError as exc: return self._error(f"GroundRoute request failed: {exc}") if response.status_code != 200: return self._error( f"GroundRoute returned HTTP {response.status_code}: {response.text[:300]}" ) try: resp: dict[str, Any] = response.json() except ValueError as exc: return self._error(f"GroundRoute returned a non-JSON body: {exc}") records = self._to_records(resp) meta = self._meta(resp) data_list = [Data(data=rec) for rec in records] if resp.get("answer"): data_list.insert( 0, Data(data={"answer": resp["answer"], "citations": resp.get("citations", []), **meta}), ) engines = sorted({rec["source_engine"] for rec in records if rec.get("source_engine")}) suffix = " (degraded)" if meta["degraded"] else "" self.status = f"{len(records)} results via {', '.join(engines) or 'cache'}{suffix}" return data_list or [Data(data={"warning": "no results", **meta})] def search_dataframe(self) -> DataFrame: return DataFrame(self.search_data()) ``` -------------------------------- ### POST /v1/search Source: https://groundroute.ai/docs/search-api The core search endpoint that accepts a JSON request body and returns search results with metadata. It requires an API key via the Authorization header. ```APIDOC ## [POST] https://api.groundroute.ai/v1/search ### Description The core endpoint for performing searches. It requires an API key via the `Authorization: Bearer ` header. The request body is JSON; unknown fields are ignored, not rejected. ### Method POST ### Endpoint https://api.groundroute.ai/v1/search ### Authentication - **Authorization** (string) - Required - Bearer token with your API key. ### Headers - **Content-Type** (string) - Required - `application/json` ### Parameters #### Request Body - **query** (string, 1–2048 chars) - Required - The search query. - **mode** (enum) - Optional - Intent hint; the router may refine it. One of `auto`, `web`, `news`, `academic`, `answer`, `page`. Default: `auto`. - **max_results** (int, 1–50) - Optional - Maximum number of results. Part of the cache key. Default: `10`. - **freshness** (enum) - Optional - Explicit freshness override: `fresh`, `semi`, or `static`. `fresh` caps cache TTL and bypasses the semantic tier. Default: `null`. - **domains** (string[]) - Optional - Include-filter. Part of the cache key. Default: `[]`. - **exclude_domains** (string[]) - Optional - Exclude-filter. Default: `[]`. - **lang** (string, ISO 639-1, ≤8 chars) - Optional - Language filter. Part of the cache key. Default: `null`. - **country** (string, ISO 3166-1 alpha-2, ≤2 chars) - Optional - Country filter. Part of the cache key. Default: `null`. - **tenant_id** and **is_byok** - Server-authoritative; any value sent in the body is dropped and replaced with the value resolved from your API key. ### Request Example ``` curl -s https://api.groundroute.ai/v1/search \ -H "Authorization: Bearer $GROUNDROUTE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "transformer attention complexity", "mode": "academic", "max_results": 10, "freshness": "static", "domains": ["arxiv.org"], "lang": "en" }' ``` ### Response #### Success Response (200) - **request_id** (string) - UUID of the request. - **tenant_id** (string) - Tenant identifier. - **results** (array of SearchResult) - List of search results. - **answer** (string|null) - Synthesized answer (answer engines), or null. - **citations** (array of Citation) - Present when an answer cites sources. - **degraded** (boolean) - True if partial results (one engine failed). - **routing_meta** (object) - Routing metadata. - **cache_meta** (object) - Cache metadata. - **quality_meta** (object) - Quality metadata. - **usage_meta** (object) - Usage metadata. #### Response Example ``` { "request_id": "8f1c2e7a-…", "tenant_id": "…", "results": [ { "url": "https://arxiv.org/abs/...", "title": "Attention Is All You Need", "snippet": "…", "content": null, "score": 0.82, "source_engine": "exa", "published_at": null, "rank": 0 } ], "answer": null, "citations": [ { "url": "https://...", "title": "…", "index": 0 } ], "degraded": false, "routing_meta": { "strategy": "price_led", "query_class": "academic", "chosen_engine": "exa", "reason": "price_led:exa $7.00/1k (first eligible for academic, source=static)", "candidates": [ { "engine": "exa", "cost_per_1k_usd": 7.0, "eligible": true, "picked": true, "note": "cheapest eligible" }, { "engine": "brave", "cost_per_1k_usd": 5.0, "eligible": true, "picked": false, "note": null } ], "cost_usd": 0.007, "latency_ms": 412, "degraded": false, "failover_from": null, "missing_engines": [] }, "cache_meta": { "cache_tier": "miss", "poolable": false, "poolability_reason": "cell_not_allowlisted", "screens_post_nfkc": true, "freshness_bucket": "none", "cache_key_private": null, "cache_key_pooled": null, "cached_at": null, "ttl_seconds": null, "cost_avoided_usd": 0.0, "similarity": null }, "quality_meta": { "quality_score": null, "components": {}, "judge_sampled": false, "engine_agreement_rbo": null }, "usage_meta": { "request_id": "8f1c2e7a-…", "tenant_id": "…", "account_id": "…", "api_key_id": "…", "ts": "2026-06-14T07:48:14Z", "billable": true, "searches_counted": 1, "provider_key_source": "platform", "cost_usd": 0.007 } } ``` ### Response Headers - **X-Request-Id** (string) - The request UUID. - **X-Cache-Tier** (string) - `miss`, `exact_private`, `exact_pooled`, or `semantic`. - **X-Degraded** (string) - `true` / `false`. - **X-GR-Timing** (string) - Per-stage timing, e.g. `auth=2(hit);enforce=1;engine=410;write=0;total=413`. ### Object Shapes **SearchResult:** `url`, `title`, `snippet`, `content?`, `score?` (0–1), `source_engine`, `published_at?`, `rank` (0-based). **Citation:** `url`, `title?`, `index` (maps into the `answer` text). ``` -------------------------------- ### Authorization Bearer header format Source: https://groundroute.ai/docs/authentication The standard format for sending the API key in the Authorization header. ```http Authorization: Bearer gr_live_xxxxxxxxxxxxxxxxxxxx ``` -------------------------------- ### GroundRoute Search Langflow custom component Source: https://groundroute.ai/docs/integrations/langflow Paste this Python code into Langflow as a custom component. It defines the GroundRouteSearchComponent with inputs for API key, query, mode, max results, freshness, domains, language, and country. No SDK is required; it uses httpx and Langflow's lfx modules. ```python """GroundRoute Search: Langflow custom component (Phase 1, paste-able). Paste into Langflow as a custom component. No SDK required. """ from __future__ import annotations from typing import Any import httpx from lfx.custom.custom_component.component import Component from lfx.inputs.inputs import ( DropdownInput, IntInput, MessageTextInput, SecretStrInput, ) from lfx.log.logger import logger from lfx.schema.data import Data from lfx.schema.dataframe import DataFrame from lfx.template.field.base import Output _BASE_URL = "https://api.groundroute.ai" _MODES = ["auto", "web", "news", "academic", "answer", "page"] _FRESHNESS = ["", "fresh", "semi", "static"] _TIMEOUT_S = 30.0 class GroundRouteSearchComponent(Component): display_name = "GroundRoute Search" description = ( "One API across 6 search engines (Serper, Brave, Exa, Tavily, Firecrawl, Perplexity). " "Routes each query to the cheapest engine that clears a quality bar and caches repeats, " "so you pay no more than going direct." ) documentation = "https://groundroute.ai/docs" # Uses a Langflow-bundled icon for the custom-component path (Phase 2 ships the GroundRoute glyph). icon = "Search" name = "GroundRouteSearch" inputs = [ SecretStrInput( name="api_key", display_name="GroundRoute API Key", required=True, info="Get one at https://groundroute.ai/keys?utm_source=langflow", ), MessageTextInput( name="query", display_name="Search Query", info="What to search for.", tool_mode=True, ), DropdownInput( name="mode", display_name="Mode", options=_MODES, value="auto", advanced=True, info="auto lets GroundRoute classify the query and pick the engine class.", ), IntInput( name="max_results", display_name="Max Results", value=10, advanced=True, info="Number of results to return (clamped to 1-50).", ), DropdownInput( name="freshness", display_name="Freshness", options=_FRESHNESS, value="", advanced=True, info="Override freshness intent. Blank lets GroundRoute auto-detect.", ), MessageTextInput( name="domains", display_name="Include Domains", advanced=True, info="Comma-separated domains to restrict the search to.", ), MessageTextInput( name="lang", display_name="Language", advanced=True, info="ISO 639-1 language code, e.g. en.", ), MessageTextInput( name="country", display_name="Country", advanced=True, info="ISO 3166-1 alpha-2 country code, e.g. us.", ), ] outputs = [ Output(display_name="Data", name="data", method="search_data"), Output(display_name="DataFrame", name="dataframe", method="search_dataframe"), ] def _build_payload(self) -> dict[str, Any]: try: max_results = int(self.max_results or 10) except (TypeError, ValueError): max_results = 10 max_results = max(1, min(max_results, 50)) body: dict[str, Any] = {"query": (self.query or "").strip(), "max_results": max_results} if self.mode and self.mode != "auto": body["mode"] = self.mode if getattr(self, "freshness", ""): body["freshness"] = self.freshness domains = getattr(self, "domains", "") or "" parsed = [d.strip() for d in domains.split(",") if d.strip()] if parsed: body["domains"] = parsed if getattr(self, "lang", ""): body["lang"] = self.lang if getattr(self, "country", ""): body["country"] = self.country return body ``` -------------------------------- ### MCP search tool result shape Source: https://groundroute.ai/docs/mcp-server The search tool returns a compact, agent-friendly shape (a subset of the REST response). It includes results, an optional answer, citations, and meta information such as request_id, cache_tier, degraded, and cost_usd. ```json { "results": [ { "url": "https://...", "title": "…", "snippet": "…", "source_engine": "serper", "published_at": null } ], "answer": null, "citations": [ { "url": "https://...", "title": "…", "index": 0 } ], "meta": { "request_id": "…", "cache_tier": "miss", "degraded": false, "cost_usd": 0.001 } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.