# Fetch a web page
Source: https://docs.andiai.com/api-reference/fetch-a-web-page
/api-reference/openapi.json get /api/v1/fetch
Fetches a single web page and returns clean extracted content (title, article text, metadata) as JSON, or LLM-ready markdown with format=context. The companion to /api/v1/search: search, pick a result, fetch it in full.
# Get a curated news feed
Source: https://docs.andiai.com/api-reference/get-a-curated-news-feed
/api-reference/openapi.json get /api/v1/news/{topic}
Returns a curated, ranked news feed for a fixed topic — there is no query parameter, the topic path segment selects the feed. `results` are ordered by relevance (a semantic pass tuned to the topic, weighted toward fresher articles); `news` holds the same articles in strict reverse-chronological order. `images` includes article images when available.
# API reference
Source: https://docs.andiai.com/api-reference/introduction
Technical reference for the Andi AI Search API endpoints.
The Andi AI Search API has three endpoints: search, fetch, and news.
## Base URL
```text theme={null}
https://api.andiai.com
```
## Authentication
Pass your API key in the `x-api-key` header with every request:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=test" \
-H "x-api-key: YOUR_API_KEY"
```
Get an API key from the [API Console](https://console.andiai.com).
## Endpoints
| Method | Path | Description |
| ------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `GET` | `/api/v1/search` | Search the web. Returns JSON, or LLM-ready markdown with `format=context`. |
| `POST` | `/api/v1/search` | Search the web with parameters in a JSON body. Preferred for location-bearing requests. Same output formats as GET. |
| `GET` | `/api/v1/fetch` | Fetch a web page as clean extracted content (JSON or LLM-ready markdown). See [content retrieval](/search/content-retrieval). |
| `GET` | `/api/v1/news/:topic` | Curated, ranked news feed for one of 23 fixed topics — no query required. See [news feeds](/search/news-feeds). |
The search endpoint sets the right search effort per query automatically (`searchMode=auto`, the default), with fixed [search modes](/search/search-modes) (`low-cost`, `fast`, `balanced`, `deep`, `exhaustive`) for manual control, or an `effort` parameter (`low`, `medium`, `high`, `max`) that pins the same tiers by generic name. It supports multiple output formats and filtering by domain, date, content type, and more.
Browse the **Endpoints** section in the sidebar to try the endpoints in the interactive playground.
## Pricing
Every JSON response includes `metrics.cost_dollars` — the amount charged to your account for that request in USD, after any discounts.
**Search:** Priced on outcome — the charge reflects the actual work each search performed, so simpler queries cost less. With the default `auto` mode, the price follows the effort Andi chose for the query; a pinned mode makes cost more uniform call to call.
**Fetch:** Flat $0.001 base per request, plus $0.05 per 1M tokens of extracted content. Failed fetches (422 and 503 responses) are free.
**News:** Billed like a search request — a per-token rate on the returned feed, no flat per-call fee. Cache hits bill the token rate only.
## Next steps
Make your first API call.
Automatic effort by default, manual control when you want it.
Full parameter reference.
Fetch endpoint reference.
News endpoint reference.
# Search the web
Source: https://docs.andiai.com/api-reference/search-the-web
/api-reference/openapi.json get /api/v1/search
Performs a web search and returns structured results. Search effort is set automatically per query by default (`searchMode=auto`), with fixed modes (low-cost, fast, balanced, deep, exhaustive) available for manual control. Supports domain and date filtering, query operators, and multiple output formats.
# Search the web (JSON body)
Source: https://docs.andiai.com/api-reference/search-the-web-json-body
/api-reference/openapi.json post /api/v1/search
Same contract as `GET /api/v1/search`, but accepts parameters as a JSON body. Preferred for location-bearing requests because GET-style location encoding is awkward and keeps `latitude`/`longitude` out of HTTP access logs. All GET query parameters are accepted as body fields, plus optional location fields (`latitude`, `longitude`, `accuracy`, `city`, `state`, `countryCode`, `postalCode`, `timezone`, `location`).
# AI agent tool
Source: https://docs.andiai.com/examples/ai-agent-tool
Connect an AI agent to Andi search via MCP, or define search and fetch as tools for any agent framework.
There are two ways to give your AI agent web search: connect the MCP server (zero code), or define search and fetch as tool functions in your framework.
## MCP server (recommended)
The fastest path is to point your agent at the install guide. It will configure itself:
```text theme={null}
https://api.andiai.com/install.md
```
Or connect directly — for example, in Claude Code:
```bash theme={null}
claude mcp add --transport http andi https://api.andiai.com/mcp --header "x-api-key: YOUR_API_KEY"
```
The MCP server exposes two tools: `andi_web_search` and `andi_fetch_url`. Your agent can search the web and then fetch full page content from any result. See [Build with AI agents](/resources/ai-agents) for setup snippets for Cursor, VS Code, Codex, and other clients.
## Tool definition (custom frameworks)
If your agent framework uses function/tool definitions instead of MCP, define the search tool with parameters and expected output:
```python Python (OpenAI-compatible) theme={null}
search_tool = {
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for current information. Use this when the user asks about recent events, facts you're unsure about, or anything that requires up-to-date information.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query",
},
"limit": {
"type": "integer",
"description": "Number of results (1-100, default 10)",
},
"searchMode": {
"type": "string",
"enum": ["auto", "low-cost", "fast", "balanced", "deep", "exhaustive"],
"description": "Search mode. auto (default) sets the right effort per query; pin a mode (low-cost, fast, balanced, deep, exhaustive) to control it",
},
"effort": {
"type": "string",
"enum": ["low", "medium", "high", "max"],
"description": "How hard to try — low favors speed, max favors thoroughness. Omit for the adaptive default; searchMode wins if both are set",
},
},
"required": ["query"],
},
},
}
```
```javascript JavaScript (OpenAI-compatible) theme={null}
const searchTool = {
type: "function",
function: {
name: "web_search",
description:
"Search the web for current information. Use this when the user asks about recent events, facts you're unsure about, or anything that requires up-to-date information.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "The search query",
},
limit: {
type: "integer",
description: "Number of results (1-100, default 10)",
},
searchMode: {
type: "string",
enum: ["auto", "low-cost", "fast", "balanced", "deep", "exhaustive"],
description: "Search mode. auto (default) sets the right effort per query; pin a mode (low-cost, fast, balanced, deep, exhaustive) to control it",
},
effort: {
type: "string",
enum: ["low", "medium", "high", "max"],
description: "How hard to try — low favors speed, max favors thoroughness. Omit for the adaptive default; searchMode wins if both are set",
},
},
required: ["query"],
},
},
};
```
## Tool execution
When the agent calls the tool, execute the search and return results:
```python Python theme={null}
import os
import json
import requests
api_key = os.environ["ANDI_API_KEY"]
def execute_web_search(query: str, limit: int = 5, searchMode: str = "auto", effort: str = None) -> str:
"""Execute a web search and return formatted results."""
params = {"q": query, "limit": limit, "searchMode": searchMode, "extracts": "true"}
if effort:
params["effort"] = effort
response = requests.get(
"https://api.andiai.com/api/v1/search",
params=params,
headers={"x-api-key": api_key},
)
if response.status_code != 200:
return json.dumps({"error": response.json().get("error", "Search failed")})
data = response.json()
results = []
for r in data["results"]:
result = {
"title": r["title"],
"url": r["link"],
"description": r["desc"],
}
if r.get("extracts"):
result["content"] = " ".join(r["extracts"])
results.append(result)
return json.dumps(results)
```
```javascript JavaScript theme={null}
const apiKey = process.env.ANDI_API_KEY;
async function executeWebSearch(query, limit = 5, searchMode = "auto", effort = null) {
const url = new URL("https://api.andiai.com/api/v1/search");
url.searchParams.set("q", query);
url.searchParams.set("limit", String(limit));
url.searchParams.set("searchMode", searchMode);
url.searchParams.set("extracts", "true");
if (effort) {
url.searchParams.set("effort", effort);
}
const response = await fetch(url, {
headers: { "x-api-key": apiKey },
});
if (!response.ok) {
const error = await response.json();
return JSON.stringify({ error: error.error || "Search failed" });
}
const data = await response.json();
return JSON.stringify(
data.results.map((r) => ({
title: r.title,
url: r.link,
description: r.desc,
content: r.extracts ? r.extracts.join(" ") : undefined,
}))
);
}
```
## Search then fetch workflow
An agent can search for results, then fetch full content from the most relevant page. This example pins `searchMode=deep` so every call gets broad coverage before the fetch — omit it to let `auto` decide per query:
```python theme={null}
def search_and_fetch(query: str) -> str:
"""Search, then fetch the top result's full content."""
# Step 1: Search
search_resp = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": query, "limit": 3, "searchMode": "deep"},
headers={"x-api-key": api_key},
)
results = search_resp.json()["results"]
if not results:
return "No results found."
# Step 2: Fetch the top result
fetch_resp = requests.get(
"https://api.andiai.com/api/v1/fetch",
params={"url": results[0]["link"], "format": "context"},
headers={"x-api-key": api_key},
)
if fetch_resp.status_code == 200:
return fetch_resp.text
elif fetch_resp.status_code == 503:
return "Page is still loading — retry shortly."
else:
return f"Could not fetch page (status {fetch_resp.status_code})."
```
## Putting it together
Here's how the tool fits into an agent loop:
```python theme={null}
import openai
client = openai.OpenAI()
messages = [
{"role": "system", "content": "You are a helpful assistant with web search access. Cite sources with URLs."},
{"role": "user", "content": "What are the latest developments in quantum computing?"},
]
# Step 1: LLM decides to call the tool
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=[search_tool],
)
message = response.choices[0].message
# Step 2: Execute tool calls
if message.tool_calls:
messages.append(message)
for tool_call in message.tool_calls:
args = json.loads(tool_call.function.arguments)
result = execute_web_search(**args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
# Step 3: LLM generates final answer with search context
final = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
print(final.choices[0].message.content)
```
## Using `format=context` for agents
For agents that pass search results directly into conversation context, `format=context` returns pre-formatted markdown:
```python theme={null}
def execute_web_search_context(query: str) -> str:
"""Return search results as markdown text."""
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": query, "format": "context", "limit": 5},
headers={"x-api-key": api_key},
)
return response.text
```
`format=context` reduces the code in your tool executor — no JSON parsing or formatting needed. The tradeoff is less control over result structure.
## Next steps
MCP server setup for Claude Code, Cursor, and more.
Full fetch endpoint reference.
Automatic effort by default, manual control when you want it.
Error codes and retry strategies.
# Basic search integration
Source: https://docs.andiai.com/examples/basic-search
Build a complete search integration with environment variables, error handling, and response parsing.
This example builds a complete search integration from scratch — environment setup, request handling, error recovery, and result parsing.
You need an API key to follow this guide. Get one from the [API Console](https://console.andiai.com).
## Setup
Store your API key as an environment variable rather than hardcoding it:
```bash Shell theme={null}
export ANDI_API_KEY="your-api-key"
```
```python Python (.env file) theme={null}
# .env
ANDI_API_KEY=your-api-key
```
```javascript JavaScript (.env file) theme={null}
# .env
ANDI_API_KEY=your-api-key
```
## Complete example
```bash curl theme={null}
#!/bin/bash
# Search with error handling
response=$(curl -s -w "\n%{http_code}" \
"https://api.andiai.com/api/v1/search?q=best+programming+languages&limit=5" \
-H "x-api-key: $ANDI_API_KEY")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq 200 ]; then
echo "$body" | jq '.results[] | {title, link, source}'
else
echo "Error $http_code: $(echo "$body" | jq -r '.error')"
fi
```
```python Python theme={null}
import os
import requests
api_key = os.environ["ANDI_API_KEY"]
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "best programming languages", "limit": 5},
headers={"x-api-key": api_key},
)
if response.status_code == 200:
data = response.json()
for result in data["results"]:
print(f"{result['title']}")
print(f" {result['link']}")
print(f" {result['desc'][:100]}...")
print()
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", 1)
print(f"Rate limited. Retry after {retry_after}s")
else:
error = response.json()
print(f"Error {response.status_code}: {error['error']}")
```
```javascript JavaScript theme={null}
const apiKey = process.env.ANDI_API_KEY;
const url = new URL("https://api.andiai.com/api/v1/search");
url.searchParams.set("q", "best programming languages");
url.searchParams.set("limit", "5");
const response = await fetch(url, {
headers: { "x-api-key": apiKey },
});
if (response.ok) {
const data = await response.json();
for (const result of data.results) {
console.log(result.title);
console.log(` ${result.link}`);
console.log(` ${result.desc.slice(0, 100)}...`);
console.log();
}
} else if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || 1;
console.log(`Rate limited. Retry after ${retryAfter}s`);
} else {
const error = await response.json();
console.log(`Error ${response.status}: ${error.error}`);
}
```
## How it works
1. **API key from environment** — loaded from `ANDI_API_KEY`, never hardcoded
2. **Query parameters** — `q` for the search query, `limit` to cap results at 5
3. **Status code check** — handle success, rate limiting, and other errors separately
4. **Result parsing** — each result has `title`, `link`, `desc`, and `source`
## Variations
### With text extracts
Add `extracts=true` to get longer text passages from each result page:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "best programming languages", "extracts": "true"},
headers={"x-api-key": api_key},
)
data = response.json()
for result in data["results"]:
if result.get("extracts"):
print(f"{result['title']}: {result['extracts'][0][:200]}")
```
### With domain filtering
Restrict results to specific sites:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "best programming languages",
"includeDomains": "stackoverflow.com,github.com",
},
headers={"x-api-key": api_key},
)
```
### Pinning deep mode
The default `auto` mode already escalates to deeper treatment automatically when a query needs it. Set `searchMode=deep` to guarantee that treatment on every call, accepting a 2–3 second response:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "best programming languages",
"searchMode": "deep",
},
headers={"x-api-key": api_key},
)
```
### Context format for LLMs
Get results as markdown text, ready to pass into an LLM prompt:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "best programming languages", "format": "context"},
headers={"x-api-key": api_key},
)
# Response is markdown text, not JSON
context = response.text
```
## Next steps
Use search results as context for an LLM.
Full parameter reference.
Error codes and retry strategies.
Understand the response structure.
# News monitoring
Source: https://docs.andiai.com/examples/news-monitoring
Build automated news monitoring with date filtering, intent forcing, and domain restrictions.
This example builds a news monitoring system that searches for recent articles on specific topics, filters by date and source, and collects results for processing.
## Complete example
```bash curl theme={null}
# Search for AI news from the past 24 hours
curl -s "https://api.andiai.com/api/v1/search?q=artificial+intelligence&intent=news&dateRange=24h&limit=10" \
-H "x-api-key: $ANDI_API_KEY" | jq '(.news // .results)[] | {title, link, date, source}'
```
```python Python theme={null}
import os
import requests
from datetime import datetime
api_key = os.environ["ANDI_API_KEY"]
def search_news(topic: str, date_range: str = "24h", domains: list[str] | None = None) -> list[dict]:
"""Search for recent news on a topic."""
params = {
"q": topic,
"intent": "news",
"dateRange": date_range,
"limit": 20,
}
if domains:
params["includeDomains"] = ",".join(domains)
response = requests.get(
"https://api.andiai.com/api/v1/search",
params=params,
headers={"x-api-key": api_key},
)
response.raise_for_status()
data = response.json()
articles = []
# Check both results and news arrays
for result in data.get("news", data.get("results", [])):
articles.append({
"title": result["title"],
"url": result["link"],
"source": result["source"],
"date": result.get("date"),
"summary": result["desc"],
})
return articles
# Monitor multiple topics
topics = [
"artificial intelligence regulation",
"large language models",
"AI safety research",
]
for topic in topics:
articles = search_news(topic, date_range="24h")
print(f"\n{topic}: {len(articles)} articles")
for article in articles[:3]:
print(f" [{article['source']}] {article['title']}")
```
```javascript JavaScript theme={null}
const apiKey = process.env.ANDI_API_KEY;
async function searchNews(topic, dateRange = "24h", domains = null) {
const url = new URL("https://api.andiai.com/api/v1/search");
url.searchParams.set("q", topic);
url.searchParams.set("intent", "news");
url.searchParams.set("dateRange", dateRange);
url.searchParams.set("limit", "20");
if (domains) {
url.searchParams.set("includeDomains", domains.join(","));
}
const response = await fetch(url, {
headers: { "x-api-key": apiKey },
});
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
const data = await response.json();
const results = data.news || data.results || [];
return results.map((r) => ({
title: r.title,
url: r.link,
source: r.source,
date: r.date,
summary: r.desc,
}));
}
// Monitor multiple topics
const topics = [
"artificial intelligence regulation",
"large language models",
"AI safety research",
];
for (const topic of topics) {
const articles = await searchNews(topic, "24h");
console.log(`\n${topic}: ${articles.length} articles`);
articles.slice(0, 3).forEach((a) => {
console.log(` [${a.source}] ${a.title}`);
});
}
```
## How it works
1. **`intent=news`** forces news-specific results rather than general web search
2. **`dateRange=24h`** limits results to the past 24 hours
3. **News array** — when the intent is `news`, results may appear in the `news` array alongside `results`
4. **Multiple topics** — loop through topics to monitor several areas at once
## Variations
### Restrict to trusted sources
Limit results to specific publications:
```python theme={null}
tech_sources = [
"arstechnica.com",
"wired.com",
"technologyreview.com",
"theverge.com",
]
articles = search_news(
"artificial intelligence",
date_range="week",
domains=tech_sources,
)
```
### Exclude aggregators
Remove noisy domains from results:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "AI startups funding",
"intent": "news",
"dateRange": "week",
"excludeDomains": "reddit.com,medium.com",
},
headers={"x-api-key": api_key},
)
```
### Custom date range
Search a specific time window:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "product launch",
"intent": "news",
"dateFrom": "2026-03-01",
"dateTo": "2026-03-15",
},
headers={"x-api-key": api_key},
)
```
### Scheduled monitoring
Run searches on a schedule using cron or a task scheduler:
```python theme={null}
import time
def monitor_loop(topics: list[str], interval_seconds: int = 3600):
"""Check for new articles every interval."""
while True:
for topic in topics:
articles = search_news(topic, date_range="24h")
if articles:
print(f"[{datetime.now()}] {topic}: {len(articles)} new articles")
# Process articles: send alerts, save to database, etc.
time.sleep(interval_seconds)
```
When running scheduled searches, respect your rate limits. Space requests out and use the `X-RateLimit-Remaining` header to monitor usage.
## Next steps
Domain, date, and content filtering options.
Multi-query search with result aggregation.
Rate limit configuration and handling.
Response structure and result types.
# RAG pipeline
Source: https://docs.andiai.com/examples/rag-pipeline
Use Andi search results as retrieval context for a language model to generate grounded answers.
Retrieval-augmented generation (RAG) pairs a search retrieval step with an LLM generation step. The Andi API handles retrieval — you pass the results as context to the LLM, which generates answers grounded in real web sources.
The flow: a user asks a question, your app searches the web via the Andi API, the results become context for the LLM prompt, and the LLM generates an answer grounded in those results.
## Complete example
```bash curl theme={null}
# Step 1: Search
results=$(curl -s \
"https://api.andiai.com/api/v1/search?q=what+causes+aurora+borealis&extracts=true&limit=5" \
-H "x-api-key: $ANDI_API_KEY")
# Step 2: Format context (extract titles and descriptions)
context=$(echo "$results" | jq -r '.results[] | "[\(.title)](\(.link))\n\(.desc)\n"')
echo "Context for LLM:"
echo "$context"
# Step 3: Pass $context to your LLM of choice
```
```python Python theme={null}
import os
import requests
api_key = os.environ["ANDI_API_KEY"]
# Step 1: Search for context
search_response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "what causes aurora borealis",
"extracts": "true",
"limit": 5,
},
headers={"x-api-key": api_key},
)
search_data = search_response.json()
# Step 2: Format results as context
context_parts = []
for result in search_data["results"]:
text = result["desc"]
if result.get("extracts"):
text = " ".join(result["extracts"])
context_parts.append(f"[{result['title']}]({result['link']})\n{text}")
context = "\n\n".join(context_parts)
# Step 3: Build the LLM prompt
prompt = f"""Answer the user's question using only the search results below.
Cite sources by linking to the URLs provided.
Search results:
{context}
Question: What causes the aurora borealis?"""
# Step 4: Send to your LLM (example with OpenAI-compatible API)
# completion = client.chat.completions.create(
# model="your-model",
# messages=[{"role": "user", "content": prompt}],
# )
print(prompt)
```
```javascript JavaScript theme={null}
const apiKey = process.env.ANDI_API_KEY;
// Step 1: Search for context
const url = new URL("https://api.andiai.com/api/v1/search");
url.searchParams.set("q", "what causes aurora borealis");
url.searchParams.set("extracts", "true");
url.searchParams.set("limit", "5");
const searchResponse = await fetch(url, {
headers: { "x-api-key": apiKey },
});
const searchData = await searchResponse.json();
// Step 2: Format results as context
const context = searchData.results
.map((r) => {
const text = r.extracts ? r.extracts.join(" ") : r.desc;
return `[${r.title}](${r.link})\n${text}`;
})
.join("\n\n");
// Step 3: Build the LLM prompt
const prompt = `Answer the user's question using only the search results below.
Cite sources by linking to the URLs provided.
Search results:
${context}
Question: What causes the aurora borealis?`;
// Step 4: Send to your LLM
console.log(prompt);
```
## Using `format=context`
For simpler RAG setups, use `format=context` to get results pre-formatted as markdown. This skips the manual formatting step:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "what causes aurora borealis",
"format": "context",
"limit": 5,
},
headers={"x-api-key": api_key},
)
# Response is markdown text — pass directly to your LLM
context = response.text
```
`format=context` is the fastest path to a working RAG pipeline. Use `format=json` with `extracts=true` when you need more control over how context is structured.
## Pinning deep mode for RAG
The default `auto` mode adapts effort per query and is the right default for most RAG pipelines. For research-heavy queries, set `searchMode=deep` to force broader source coverage and spell correction on every call:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "what causes aurora borealis",
"searchMode": "deep",
"extracts": "true",
"limit": 10,
},
headers={"x-api-key": api_key},
)
```
Deep mode takes \~2-3 seconds vs \~1 second for fast mode. Pin it when you need guaranteed broad coverage on every call; otherwise `auto` already applies deeper treatment when a query calls for it.
## Tips for better RAG results
* **Use `extracts=true`** to get longer text passages beyond the short `desc` field
* **Set `limit=5` to `limit=10`** — more results give the LLM more context to draw from, but too many can dilute relevance
* **Include source URLs** in the prompt so the LLM can cite them
* **Use `includeDomains`** to restrict to authoritative sources for domain-specific questions
* **Tell the LLM to say "I don't know"** when the search results don't contain the answer
## Next steps
Define Andi search as a tool for an AI agent.
Multi-query search with result aggregation.
Response structure and result types.
When to use deep vs fast search.
# Research assistant
Source: https://docs.andiai.com/examples/research-assistant
Build a research assistant with multi-query search, result aggregation, and searchMode control.
This example builds a research assistant that searches multiple related queries, deduplicates and aggregates the results. It uses the default `auto` search mode, which gives multi-query research calls deep treatment automatically — you can pin `searchMode=deep` or `exhaustive` for guaranteed research-grade coverage.
## Complete example
```bash curl theme={null}
# Multi-query search with a JSON array
curl -G -s "https://api.andiai.com/api/v1/search" \
--data-urlencode 'q=["quantum computing applications", "quantum computing challenges", "quantum computing outlook"]' \
-d "limit=10" \
-H "x-api-key: $ANDI_API_KEY" | jq '{
correctedQuery,
result_count: (.results | length),
results: [.results[] | {title, link, source}]
}'
```
```python Python theme={null}
import os
import requests
api_key = os.environ["ANDI_API_KEY"]
def research(queries: list[str], search_mode: str = "auto", limit: int = 10) -> dict:
"""Run multiple search queries and aggregate results."""
all_results = []
seen_urls = set()
corrected_queries = {}
for query in queries:
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": query,
"searchMode": search_mode,
"limit": limit,
"extracts": "true",
},
headers={"x-api-key": api_key},
)
response.raise_for_status()
data = response.json()
# Track spell corrections
if data.get("correctedQuery"):
corrected_queries[query] = data["correctedQuery"]
# Deduplicate by URL
for result in data["results"]:
if result["link"] not in seen_urls:
seen_urls.add(result["link"])
result["_query"] = data.get("correctedQuery", query)
all_results.append(result)
return {
"results": all_results,
"total_unique": len(all_results),
"corrected_queries": corrected_queries,
}
# Research a topic from multiple angles
queries = [
"quantum computing applications",
"quantum computing challenges limitations",
"recent quantum computing breakthroughs",
]
research_data = research(queries)
print(f"Found {research_data['total_unique']} unique results across {len(queries)} queries")
if research_data["corrected_queries"]:
print("\nSpell corrections:")
for original, corrected in research_data["corrected_queries"].items():
print(f" {original} -> {corrected}")
print("\nTop results:")
for result in research_data["results"][:10]:
print(f" [{result['source']}] {result['title']}")
```
```javascript JavaScript theme={null}
const apiKey = process.env.ANDI_API_KEY;
async function research(queries, searchMode = "auto", limit = 10) {
const allResults = [];
const seenUrls = new Set();
const correctedQueries = {};
for (const query of queries) {
const url = new URL("https://api.andiai.com/api/v1/search");
url.searchParams.set("q", query);
url.searchParams.set("searchMode", searchMode);
url.searchParams.set("limit", String(limit));
url.searchParams.set("extracts", "true");
const response = await fetch(url, {
headers: { "x-api-key": apiKey },
});
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
const data = await response.json();
if (data.correctedQuery) {
correctedQueries[query] = data.correctedQuery;
}
for (const result of data.results) {
if (!seenUrls.has(result.link)) {
seenUrls.add(result.link);
result._query = data.correctedQuery || query;
allResults.push(result);
}
}
}
return {
results: allResults,
totalUnique: allResults.length,
correctedQueries,
};
}
// Research a topic from multiple angles
const queries = [
"quantum computing applications",
"quantum computing challenges limitations",
"recent quantum computing breakthroughs",
];
const data = await research(queries);
console.log(
`Found ${data.totalUnique} unique results across ${queries.length} queries`
);
if (Object.keys(data.correctedQueries).length > 0) {
console.log("\nSpell corrections:");
for (const [original, corrected] of Object.entries(data.correctedQueries)) {
console.log(` ${original} -> ${corrected}`);
}
}
console.log("\nTop results:");
data.results.slice(0, 10).forEach((r) => {
console.log(` [${r.source}] ${r.title}`);
});
```
## How it works
1. **Multiple queries** — search the same topic from different angles to get broader coverage
2. **Search mode** — the default `auto` mode gives multi-query research calls deep treatment automatically; pin `searchMode=deep` or `exhaustive` for guaranteed research-grade coverage and spell correction
3. **Deduplication** — track URLs already seen to avoid duplicate results across queries
4. **Spell correction tracking** — `correctedQuery` shows when deep search fixed a typo
## Using multi-query in a single request
The API also supports passing a JSON array of up to 5 queries in the `q` parameter:
```python theme={null}
import json
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": json.dumps([
"quantum computing applications",
"quantum computing challenges",
"quantum computing outlook",
]),
"limit": 10,
},
headers={"x-api-key": api_key},
)
```
Multi-query via JSON array returns combined results in a single response. The sequential approach above gives you per-query control and deduplication, but uses more API calls.
## Variations
### With source filtering
Focus research on academic or authoritative sources:
```python theme={null}
research_data = research(
queries=["quantum computing applications"],
limit=20,
)
# Post-filter by domain
academic_results = [
r for r in research_data["results"]
if any(d in r["source"] for d in ["arxiv.org", "nature.com", "ieee.org", "acm.org"])
]
```
Or use `includeDomains` to restrict at the API level:
```python theme={null}
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={
"q": "quantum computing",
"includeDomains": "arxiv.org,nature.com,ieee.org",
},
headers={"x-api-key": api_key},
)
```
### Generating a research summary
Combine results with an LLM for a synthesized report:
```python theme={null}
# Gather context from research
context_parts = []
for result in research_data["results"][:15]:
text = result["desc"]
if result.get("extracts"):
text = " ".join(result["extracts"])
context_parts.append(f"[{result['title']}]({result['link']})\n{text}")
context = "\n\n".join(context_parts)
prompt = f"""Write a research summary based on these search results.
Organize by theme. Cite sources with URLs.
{context}"""
# Send to your LLM of choice
```
## Next steps
Spell correction and extended source coverage.
Use search results as LLM context.
Full parameter reference.
Domain, date, and content filtering.
# Filtering
Source: https://docs.andiai.com/features/filtering
Filter Andi AI Search API results by domain, date range, file type, and content location.
The API supports filtering through both query parameters and in-query operators. This page covers the parameter-based approach — see [query operators](/features/query-operators) for inline syntax.
## Domain filtering
Restrict results to specific domains or exclude unwanted ones.
### Include domains
Return results only from the specified domains:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=python+tutorials&includeDomains=docs.python.org,realpython.com" \
-H "x-api-key: YOUR_API_KEY"
```
### Exclude domains
Remove specific domains from results:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=python+tutorials&excludeDomains=w3schools.com,pinterest.com" \
-H "x-api-key: YOUR_API_KEY"
```
### Wildcards
Match all subdomains with `*`:
```bash theme={null}
# Include all subdomains of example.com
?includeDomains=*.example.com
# Exclude all subdomains of example.com
?excludeDomains=*.example.com
```
## Date filtering
Filter results by publication date using relative ranges or absolute dates.
### Relative date ranges
Use `dateRange` for common time windows:
```bash theme={null}
# Results from the past week
curl "https://api.andiai.com/api/v1/search?q=tech+news&dateRange=week" \
-H "x-api-key: YOUR_API_KEY"
```
Available values: `day`, `week`, `month`, `year`, `24h`, `7d`, `30d`, `90d`, `1y`.
### Absolute date ranges
Use `dateFrom` and `dateTo` for specific date boundaries:
```bash theme={null}
# Results from Q1 2025
curl "https://api.andiai.com/api/v1/search?q=quarterly+earnings&dateFrom=2025-01-01&dateTo=2025-03-31" \
-H "x-api-key: YOUR_API_KEY"
```
You can use one or both:
* `dateFrom` alone — results from that date onward
* `dateTo` alone — results up to that date
* Both — results within the range
## Content filtering
Filter by file type or where terms appear on the page.
### File type
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=annual+report&filetype=pdf" \
-H "x-api-key: YOUR_API_KEY"
```
### Title, URL, and body text
```bash theme={null}
# Term must appear in the page title
?intitle=tutorial
# Term must appear in the URL
?inurl=api
# Term must appear in the body text
?intext=benchmarks
```
## Combined filtering
Filters stack — combine them to narrow results precisely:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=machine+learning&includeDomains=arxiv.org,github.com&dateRange=month&filetype=pdf&limit=20" \
-H "x-api-key: YOUR_API_KEY"
```
This searches for machine learning PDFs on arxiv.org and github.com from the past month.
## Next steps
In-query operators like `site:` and `filetype:`.
Automated news search with date and domain filtering.
# Query operators
Source: https://docs.andiai.com/features/query-operators
Use in-query operators to refine search results directly in the query string.
Query operators let you control search behavior directly in the `q` parameter. They work when `parseOperators` is enabled (the default).
## Site operators
Restrict or exclude results from specific domains:
| Operator | Description | Example |
| -------- | -------------------------------- | -------------------------------------- |
| `site:` | Results only from this domain | `site:github.com python libraries` |
| `-site:` | Exclude results from this domain | `machine learning -site:wikipedia.org` |
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=site:github.com+python+libraries" \
-H "x-api-key: YOUR_API_KEY"
```
## Term operators
Require or exclude specific terms:
| Operator | Description | Example |
| -------- | ------------------------------- | ----------------------- |
| `+term` | Term must appear in results | `+python web framework` |
| `-term` | Term must not appear in results | `apple -fruit` |
## Content operators
Filter by where terms appear:
| Operator | Description | Example |
| ----------- | ------------------------------------------------ | ----------------------------------- |
| `filetype:` | Filter by file extension (alias: `ext:`) | `filetype:pdf machine learning` |
| `intitle:` | Term must be in the page title | `intitle:tutorial react hooks` |
| `inurl:` | Term must be in the URL | `inurl:api documentation` |
| `intext:` | Term must be in the body text (alias: `inbody:`) | `intext:benchmarks LLM performance` |
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=filetype:pdf+machine+learning+survey" \
-H "x-api-key: YOUR_API_KEY"
```
## Date operators
Filter results by publication date:
| Operator | Description | Example |
| --------- | ---------------------------------- | ---------------------------------- |
| `after:` | Results published after this date | `after:2025-01-01 AI news` |
| `before:` | Results published before this date | `before:2024-06-01 product launch` |
Dates use `YYYY-MM-DD` format.
## Language operators
Filter results by language:
| Operator | Description | Example |
| -------- | --------------------------------------------- | --------------------------------- |
| `lang:` | Results in this language (alias: `language:`) | `lang:es inteligencia artificial` |
Uses ISO 639-1 two-letter language codes.
## Combining operators
Operators can be combined in a single query:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=site:github.com+filetype:md+intitle:readme+machine+learning+after:2025-01-01" \
-H "x-api-key: YOUR_API_KEY"
```
This searches GitHub for markdown README files about machine learning published after January 2025.
## Disabling operator parsing
If your query contains text that looks like an operator but shouldn't be treated as one, set `parseOperators=false`:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=how+to+use+site:+prefix+in+DNS&parseOperators=false" \
-H "x-api-key: YOUR_API_KEY"
```
When disabled, the entire query is treated as literal text.
## Next steps
Parameter-based filtering for domains, dates, and content.
Full parameter reference.
# Query parameters
Source: https://docs.andiai.com/features/query-parameters
Complete reference for all Andi AI Search API query parameters.
All parameters work on both `GET /api/v1/search` (as query string parameters) and `POST /api/v1/search` (as JSON body fields). POST is preferred for location-bearing requests.
Most integrations only need `q` and `searchMode`. The filtering and output parameters below are available when you need more control.
## Core parameters
| Parameter | Type | Default | Description |
| ------------ | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `q` | string | **required** | Search query string. Also accepts a JSON array of up to 5 queries (e.g., `["query one", "query two"]`). |
| `limit` | integer | `10` | Maximum results to return (1–100) |
| `offset` | integer | `0` | Results to skip for pagination |
| `searchMode` | string | `auto` | `auto` (default) sets the search effort per query automatically. Fixed modes — `low-cost`, `fast`, `balanced`, `deep`, `exhaustive` — pin an effort level. See [search modes](/search/search-modes). |
| `effort` | string | — | Pins an effort level by generic tier name instead of a mode: `low`, `medium`, `high`, `max` (map to `fast`, `balanced`, `deep`, `exhaustive`). An explicit `searchMode` wins over `effort`. Omit for the adaptive default. See [the effort parameter](/search/search-modes#the-effort-parameter). |
| `intent` | string | `auto` | Force search intent. `auto` detects from the query; `none` disables intent detection. See [intent values](#intent-values) below. |
### Intent values
When not set, the API auto-detects intent from the query. You can force a specific intent using common aliases:
| Alias | Description | Alias | Description |
| ----------- | ------------------- | ----------- | ------------------- |
| `search` | General web search | `news` | Latest news results |
| `video` | Video search | `images` | Image search |
| `weather` | Weather data | `calculate` | Math computation |
| `wiki` | Wikipedia/knowledge | `knowledge` | Academic knowledge |
| `code` | Programming results | `recipe` | Recipe results |
| `place` | Business search | `places` | Location search |
| `questions` | Q\&A results | `time` | Time queries |
The API also accepts full intent names (e.g., `FallbackSearchIntent`, `VideoSearchIntent`) and resolves matches flexibly through exact match, case-insensitive match, and substring match.
```bash theme={null}
# Force news intent
curl "https://api.andiai.com/api/v1/search?q=AI+startups&intent=news" \
-H "x-api-key: YOUR_API_KEY"
```
## Output parameters
| Parameter | Type | Default | Description |
| ------------------ | ------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `format` | string | `json` | Response format: `json` or `context`. The `context` format returns results as markdown text with YAML frontmatter, suitable for LLM context windows. See [response format](/features/response-format). |
| `metadata` | string | `basic` | Metadata level: `basic` or `full`. With `full`, results include `contentType` and `reader` data. |
| `extracts` | boolean | `false` (`true` for `format=context`) | Include longer text extracts from result pages |
| `enrichContent` | boolean | `false` | Fetch full page content via the reader service. Budget scales with `searchMode` (fast \~2.5s cap, exhaustive \~12s cap). |
| `maxContentLength` | integer | — | Caps enriched content length |
| `imageFormat` | string | `long` | Image field naming: `short` or `long` |
| `linkFormat` | string | `link` | Field name for result URLs: `link` (default) or `url` |
```bash theme={null}
# Get results as markdown for LLM context
curl "https://api.andiai.com/api/v1/search?q=machine+learning&format=context" \
-H "x-api-key: YOUR_API_KEY"
# Get results with text extracts
curl "https://api.andiai.com/api/v1/search?q=machine+learning&extracts=true" \
-H "x-api-key: YOUR_API_KEY"
```
`metadata=full` adds latency because it fetches additional data from each result page. Use it only when you need `contentType` or `reader` data.
## Locale and safety parameters
| Parameter | Type | Default | Description |
| --------------- | ------ | ------- | ---------------------------------------------------------------------------------------------- |
| `safe` | string | `off` | Safe search level: `off`, `moderate`, or `strict` |
| `country` | string | `US` | ISO 3166-1 country code for locale bias (e.g., `US`, `GB`, `DE`) |
| `sourceCountry` | string | — | ISO 3166-1 country code to restrict results by source country |
| `language` | string | `en` | ISO 639-1 language code (e.g., `en`, `es`, `fr`). The `lang:` query operator takes precedence. |
| `units` | string | — | Unit system for weather/calculations: `metric`, `imperial`. Defaults based on `country`. |
## Date filtering
| Parameter | Type | Description |
| ----------- | ------ | ------------------------------------------------------------------------------- |
| `dateRange` | string | Relative range: `day`, `week`, `month`, `year`, `24h`, `7d`, `30d`, `90d`, `1y` |
| `dateFrom` | date | Results published on or after this date (`YYYY-MM-DD`) |
| `dateTo` | date | Results published on or before this date (`YYYY-MM-DD`) |
Use either `dateRange` for relative filtering or `dateFrom`/`dateTo` for absolute date ranges. See [filtering](/features/filtering) for examples and details.
## Domain filtering
| Parameter | Type | Description |
| ---------------- | ------ | ----------------------------------------------- |
| `includeDomains` | string | Comma-separated domains to restrict results to |
| `excludeDomains` | string | Comma-separated domains to exclude from results |
Both support wildcards: `*.example.com` matches all subdomains. See [filtering](/features/filtering) for examples and details.
## Term filtering
| Parameter | Type | Description |
| -------------- | ------ | ------------------------------------------------- |
| `includeTerms` | string | Comma-separated terms that must appear in results |
| `excludeTerms` | string | Comma-separated terms to exclude from results |
## Content filtering
| Parameter | Type | Description |
| ---------- | ------ | ------------------------------------------------ |
| `filetype` | string | File extension to filter by (e.g., `pdf`, `doc`) |
| `intitle` | string | Term that must appear in the page title |
| `inurl` | string | Term that must appear in the page URL |
| `intext` | string | Term that must appear in the page body |
## Behavior parameters
| Parameter | Type | Default | Description |
| ---------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `noCache` | boolean | `false` | Bypass cached results |
| `parseOperators` | boolean | `true` | Parse [query operators](/features/query-operators) from the query string. Set to `false` to treat operator syntax as literal text. |
| `reranker` | string | `auto` | Semantic reranker strength: `auto`, `small`, `medium`, `large`, or `xl`. Higher values improve result ordering for complex queries at the cost of latency. `xl` requires `deep` or `exhaustive` mode. See [search modes](/search/search-modes). |
## Location parameters
Location can be passed as flat keys on GET or POST. Prefer POST for location-bearing requests — a request body keeps coordinates out of URL query strings and access logs.
| Parameter | Type | Description |
| ------------- | ------ | ------------------------------------------- |
| `latitude` | number | Latitude coordinate |
| `longitude` | number | Longitude coordinate |
| `city` | string | City name |
| `state` | string | State or region |
| `countryCode` | string | Country code for the location |
| `postalCode` | string | Postal/ZIP code |
| `timezone` | string | IANA timezone (e.g., `America/Los_Angeles`) |
| `location` | string | Display name for the location |
| `accuracy` | number | Location accuracy in meters |
## Examples
### Paginated search
```bash theme={null}
# First page
curl "https://api.andiai.com/api/v1/search?q=machine+learning&limit=10" \
-H "x-api-key: YOUR_API_KEY"
# Second page
curl "https://api.andiai.com/api/v1/search?q=machine+learning&limit=10&offset=10" \
-H "x-api-key: YOUR_API_KEY"
```
### Deep mode with extracts
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=climate+change+effects&searchMode=deep&extracts=true" \
-H "x-api-key: YOUR_API_KEY"
```
### Filtered by date and domain
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=product+launch&dateRange=month&includeDomains=techcrunch.com,theverge.com" \
-H "x-api-key: YOUR_API_KEY"
```
### Multi-query search
```bash theme={null}
curl -G "https://api.andiai.com/api/v1/search" \
--data-urlencode 'q=["artificial intelligence", "machine learning"]' \
-H "x-api-key: YOUR_API_KEY"
```
Multi-query search accepts a JSON array of up to 5 queries and returns combined results in a single response. URL-encode the JSON array when passing it as a query parameter.
## Next steps
Automatic effort by default, manual control when you want it.
Domain, date, and content filtering in depth.
In-query operators like `site:` and `filetype:`.
Response structure and result types.
# Response format
Source: https://docs.andiai.com/features/response-format
Structure of the Andi AI Search API response, including result types, metrics, and the context format for LLMs.
The API returns JSON by default. The `format=context` option returns markdown with YAML frontmatter instead — see [context format](#context-format) below.
The `results_type` field tells you the shape of the response. Use it to determine which arrays and objects are present — for example, `results_type: "Weather"` means a `weather` object is included.
## Top-level fields
| Field | Type | Always present | Description |
| ------------------ | ------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `results_type` | string | Yes | Category of results (e.g., `Search`, `News`, `Weather`, `Entity`, `Calculator`) |
| `answer` | string | Yes | Generated answer for the query (may be empty) |
| `type` | string | Yes | Same as `results_type` |
| `title` | string | Yes | Title summarizing the search results |
| `results` | array | Yes | Search results |
| `metrics` | object | Yes | Search performance and cost metrics |
| `safeSearch` | object | No | Echo of safe-search state: `{requested, applied}`. Present on cache misses. Use this to confirm your `safe` parameter was honored. |
| `correctedQuery` | string | No | Spell-corrected query, when a correction was detected. Deep and exhaustive modes also re-run the search with the corrected query. |
| `related_searches` | array | No | Related search suggestions |
| `topics` | array | No | Related topics |
### Type-specific arrays
Depending on the query intent, the response may include additional arrays alongside `results`:
| Field | Present when |
| ---------- | ------------------------- |
| `videos` | Video intent queries |
| `images` | Image intent queries |
| `news` | News intent queries |
| `places` | Location/business queries |
| `profiles` | People-related queries |
| `social` | Social media queries |
| `academic` | Scholarly queries |
## Search results
Each result in the `results` array has this structure:
| Field | Type | Always present | Description |
| ---------- | ------ | -------------- | -------------------------------------------------- |
| `title` | string | Yes | Page title |
| `link` | string | Yes | Page URL (returned as `url` when `linkFormat=url`) |
| `desc` | string | Yes | Page description or summary |
| `source` | string | Yes | Domain name |
| `date` | string | No | Publication date |
| `snippet` | string | No | Query-relevant text excerpt (distinct from `desc`) |
| `answer` | string | No | Inline answer for instant answer results |
| `extracts` | array | No | Text extracts from the page (when `extracts=true`) |
### Fields added by `metadata=full`
These fields appear on results when you pass `metadata=full`. They are not included in the default `metadata=basic` response.
| Field | Type | Description |
| --------------- | ------ | ----------------------------------------------------------------------------------------------------------- |
| `type` | string | Result type when classified (see [result types](#result-types)) |
| `image` | string | Preview image URL |
| `contentType` | string | Schema.org content type (e.g., `Article`, `NewsArticle`) |
| `contentSafety` | object | Safety classification: `{rating, safeSearchApplied}` where `rating` is `"safe"`, `"unsafe"`, or `"unknown"` |
| `reader` | object | Extracted page content and metadata |
| `bang` | string | Bang shortcut for the result domain |
### Accessing key fields
```python theme={null}
data = response.json()
for result in data["results"]:
# Always present
print(result["title"], result["link"], result["source"])
# Optional fields — check before accessing
if result.get("snippet"):
print(f"Snippet: {result['snippet']}")
if result.get("extracts"):
print(f"Extract: {result['extracts'][0][:200]}")
if result.get("date"):
print(f"Published: {result['date']}")
```
## Result types
The `type` field indicates the kind of result:
| Type | Description |
| ---------------- | -------------------------------- |
| `website` | Standard web page |
| `blog` | Blog post |
| `news` | News article |
| `video` | Video content |
| `image` | Image content |
| `place` | Business or place |
| `profile` | Person or entity profile |
| `social` | Social media content |
| `academic` | Scholarly or research content |
| `calculation` | Math computation result |
| `weather` | Weather data |
| `computation` | Computed answer |
| `instant answer` | Direct answer to a factual query |
## Instant answers
Some queries trigger instant answers alongside regular results.
### Weather
Queries about weather return a `weather` object:
```json theme={null}
{
"results_type": "Weather",
"answer": "",
"type": "Weather",
"title": "Weather in San Francisco",
"results": [],
"weather": {
"location": {
"name": "San Francisco",
"country": "US",
"coordinates": {
"latitude": 37.7749,
"longitude": -122.4194
}
},
"temperature": 62,
"feelsLike": 59,
"units": "imperial",
"description": "Partly Cloudy",
"humidity": 72,
"windSpeed": 12,
"windDirection": 270,
"pressure": 1013,
"icon": "partly-cloudy",
"cloudiness": 40,
"visibility": 10000,
"timestamp": "2025-03-15T14:00:00Z"
},
"metrics": {
"query": "weather san francisco",
"intent": "WeatherIntent",
"timestamp": "2025-03-15T14:00:01Z",
"duration": 850,
"cost_dollars": 0.0018,
"queries_executed": 1,
"api_requests_count": 2,
"results_returned": 0,
"total_results_found": 0
}
}
```
Use the `units` parameter to get results in `metric` or `imperial`. The default is auto-detected from the `country` parameter.
### Calculation
Mathematical queries return a `calculation` object:
```json theme={null}
{
"results_type": "Calculator",
"answer": "",
"type": "Calculator",
"title": "150 * 1.08",
"results": [],
"calculation": {
"expression": "150 * 1.08",
"result": "162"
},
"metrics": { "..." : "..." }
}
```
Image queries return an `images` array with thumbnail and dimension data:
```json theme={null}
{
"results_type": "Search",
"answer": "",
"type": "Search",
"title": "Mountain landscape images",
"results": [],
"images": [
{
"title": "Mountain landscape",
"link": "https://example.com/photo",
"image": "https://example.com/photo.jpg",
"source": "example.com",
"type": "image",
"thumbnail": "https://example.com/photo_thumb.jpg",
"width": "1920",
"height": "1080"
}
],
"metrics": { "..." : "..." }
}
```
Image results include `thumbnail` (thumbnail URL), `width`, and `height` as string values.
## Parsing tips
* **Check `results_type` first** to know the response shape before accessing type-specific fields
* **`results` is always an array** but may be empty for instant answers (weather, calculations)
* **`desc` vs `snippet`**: `desc` is the page's general description; `snippet` is a query-relevant excerpt (when available)
* **`answer` at top level** is a generated answer string (may be empty); `answer` on individual results is an inline answer for instant answer result types
* **Optional fields** (`date`, `image`, `snippet`, `extracts`) may not be present on every result — always check before accessing
## Search intents
The `results_type` and `type` fields reflect what kind of search was performed. You can force an intent with the `intent` parameter, or let the API auto-detect it.
Common intent aliases:
| Alias | Intent | Extra fields |
| ----------- | ------------------- | ------------- |
| `search` | General web search | — |
| `news` | News articles | `news` |
| `video` | Video content | `videos` |
| `images` | Image search | `images` |
| `weather` | Weather queries | `weather` |
| `calculate` | Math expressions | `calculation` |
| `wiki` | Wikipedia/knowledge | — |
| `code` | Programming queries | — |
| `recipe` | Recipe search | — |
| `place` | Business search | `places` |
| `time` | Time queries | — |
See [query parameters](/features/query-parameters#intent-values) for the full list of intent aliases.
## Metrics
The response always includes a `metrics` object with performance and billing data:
```json theme={null}
{
"results": ["..."],
"metrics": {
"query": "quantum computing",
"intent": "InstantAnswerIntent",
"timestamp": "2026-07-15T01:29:20.468Z",
"duration": 1713,
"queries_executed": 1,
"api_requests_count": 1,
"results_returned": 3,
"total_results_found": 71800116,
"cost_dollars": 0.029886
}
}
```
| Field | Type | Description |
| --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `query` | string | The query as processed |
| `intent` | string | Detected or forced search intent |
| `timestamp` | string | Timestamp of the request |
| `duration` | number | Total request time in milliseconds |
| `cost_dollars` | number | Amount charged for this request in USD |
| `effort` | string | Resolved effort tier (`low`/`medium`/`high`/`max`). Present when `effort` was set, or when `searchMode` pins a fixed mode. |
| `queries_executed` | integer | Number of queries executed |
| `api_requests_count` | integer | Number of API requests made |
| `results_returned` | integer | Results returned in this response |
| `total_results_found` | integer | Total results found across sources |
| `cached` | boolean | Whether this response was served from cache. Only present on cache hits. |
| `cache_age_seconds` | integer | How old the cached response is, in seconds. Only present on cache hits. |
## Context format
With `format=context`, the API returns results as markdown with YAML frontmatter instead of JSON. This format is sized for LLM context windows and can be passed directly to a language model without JSON parsing.
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=climate+change+effects&format=context" \
-H "x-api-key: YOUR_API_KEY"
```
### Document-level frontmatter
The response starts with a YAML frontmatter block describing the search:
```yaml theme={null}
---
format: "andi-context/v1"
query: "climate change effects"
results_count: 10
timestamp: "2026-07-14T21:30:00Z"
search_mode: "auto"
cost_dollars: 0.0043
cached: false
response_time_ms: 1240
---
```
| Field | Always present | Description |
| ------------------- | -------------- | ------------------------------------------------------------------------------------------------------------- |
| `format` | Yes | Always `"andi-context/v1"` |
| `query` | Yes | The search query |
| `results_count` | Yes | Number of results |
| `timestamp` | Yes | Request timestamp |
| `intent` | When available | Detected search intent |
| `results_type` | When available | Type of results |
| `corrected_query` | When available | Spell-corrected query |
| `related_searches` | When available | Related search suggestions |
| `topics` | When available | Related topics |
| `search_mode` | When available | The mode this request ran with. With the default `auto`, this shows the mode Andi selected for the query. |
| `effort` | When available | Resolved effort tier (`low`/`medium`/`high`/`max`), when `effort` was set or `search_mode` pins a fixed mode. |
| `cost_dollars` | When available | Amount charged in USD |
| `cached` | When available | Whether response was from cache |
| `cache_age_seconds` | When available | Cache age in seconds |
| `response_time_ms` | When available | Response time (omitted on cache hits) |
### Per-result structure
Each result renders as an `` block with its own frontmatter:
```text theme={null}
---
title: "Climate Change Effects on Global Agriculture"
url: https://example.com/climate-agriculture
date: 2026-06-10
author: Dr. Sarah Chen
---
Climate change is altering growing seasons and precipitation patterns across
major agricultural regions...
Rising temperatures have shifted planting windows by 2-3 weeks in temperate
zones over the past decade.
```
A `source` line appears in the frontmatter only when the display source differs from the domain in the `` tag. With `metadata=full`, each article's frontmatter adds `domain`, `publisher`, `type`, `content_type`, `lang`, `word_count`, `image`, `keywords`, and `summary` when available.
### Type-specific sections
The same type-specific groups the JSON response carries as [additional arrays](#type-specific-arrays) render as trailing sections after the main results, so a context-format caller sees everything a JSON caller would. Sections appear only when the group has results, in this order: `Academic results`, `News results`, `Video results`, `Social results`, `Place results`, `Profile results`. (`images` is JSON-only.)
Each section is a markdown heading followed by a compact item list — title, url, and when available date, source, description, and duration for videos:
```text theme={null}
## News results
- title: "Heat Records Fall Across Southern Europe"
url: "https://example.com/heat-records"
date: "2026-07-12"
source: "example.com"
desc: "Temperatures exceeded seasonal norms for a third consecutive week..."
```
Items already present in the main `` results are not repeated in these sections.
### Extracts in context format
`extracts` defaults to **on** for `format=context` (the opposite of JSON, where it defaults to off). To disable extracts in context format, pass `extracts=false`.
## Next steps
Full parameter reference.
Fetch full page content from any URL.
Use search results as LLM context.
MCP server and agent integration.
# Authentication
Source: https://docs.andiai.com/getting-started/authentication
Authenticate with the Andi AI Search API using API keys in the x-api-key header.
Every request to the Andi AI Search API must include an API key in the `x-api-key` header. The same key authenticates every access path: REST requests, the [Andi CLI](/getting-started/cli), and the [MCP server](/resources/ai-agents).
```bash curl theme={null}
curl "https://api.andiai.com/api/v1/search?q=test" \
-H "x-api-key: YOUR_API_KEY"
```
```python Python theme={null}
headers = {"x-api-key": "YOUR_API_KEY"}
```
```javascript JavaScript theme={null}
const headers = { "x-api-key": "YOUR_API_KEY" };
```
## Getting an API key
1. Sign in to the [API Console](https://console.andiai.com)
2. Go to **API Keys**
3. Click **Create Key**
4. Copy the key — it's only shown once
## Setting up your environment
Store your API key as an environment variable rather than hardcoding it in your code.
Create a `.env` file in your project root:
```bash theme={null}
ANDI_API_KEY=your-api-key
```
Load it in your code:
```python theme={null}
# Python (with python-dotenv)
from dotenv import load_dotenv
load_dotenv()
import os
api_key = os.environ["ANDI_API_KEY"]
```
```javascript theme={null}
// JavaScript (with dotenv)
import "dotenv/config";
const apiKey = process.env.ANDI_API_KEY;
```
Or export directly in your shell:
```bash theme={null}
export ANDI_API_KEY="your-api-key"
```
Pass the key as an environment variable:
```bash theme={null}
docker run -e ANDI_API_KEY=your-api-key your-image
```
Or use an env file:
```bash theme={null}
docker run --env-file .env your-image
```
Add `ANDI_API_KEY` as a repository secret, then reference it in your workflow:
```yaml theme={null}
steps:
- name: Run search
env:
ANDI_API_KEY: ${{ secrets.ANDI_API_KEY }}
run: ./search-script.sh
```
Add `ANDI_API_KEY` in your project's environment variables settings. These are automatically available as `process.env.ANDI_API_KEY` in your server-side code.
## Managing keys
The API Console lets you:
* **Create** multiple keys for different applications or environments
* **Rename** keys to identify their purpose
* **Enable/disable** keys without deleting them
* **Revoke** keys that are no longer needed
Each key tracks its own usage. You can monitor request counts and credit consumption per key in the console.
Use separate API keys for development and production. This makes it easy to rotate keys, track usage per environment, and revoke a compromised key without affecting other environments.
## Security practices
Treat API keys like passwords. Never commit them to version control or expose them in client-side code.
* Store keys in environment variables or a secrets manager
* Use separate keys for development and production
* Rotate keys periodically
* Revoke keys immediately if compromised
* Restrict key access to team members who need it
* Add `.env` to your `.gitignore`
## Next steps
Automatic effort by default, manual control when you want it.
Use your key with the MCP server and agent tools.
Manage your API keys, monitor usage, and configure rate limits.
Handle authentication errors and other failures.
# CLI
Source: https://docs.andiai.com/getting-started/cli
Search the web, fetch pages, and run a local MCP server from the command line with the Andi CLI.
The [Andi CLI](https://github.com/andisearch/andi-cli) (`@andiai/cli` on npm) is a thin client for the hosted API: web search, page fetch, and a local MCP server, with no local index or ranking.
## Install
Install it globally:
```bash theme={null}
npm install -g @andiai/cli
andi search "current weather in san francisco"
```
Or run it directly without installing:
```bash theme={null}
npx -y @andiai/cli search "andi search api" --json
```
## Authentication
Get a key from the [API Console](https://console.andiai.com/signup). The CLI reads it from, in order of precedence: the `--api-key` flag, the `ANDI_API_KEY` environment variable, or `~/.andi/config.json` (`{"apiKey": "..."}`).
```bash theme={null}
export ANDI_API_KEY=YOUR_API_KEY
andi search "typescript satisfies operator"
```
`andi --help` and `andi schema` work without a key.
## Search
```bash theme={null}
andi search "who won the 2026 f1 championship" --mode deep --limit 5
```
Pass up to 5 queries at once — they run as a single API call with fused, deduplicated ranking:
```bash theme={null}
andi search "rust async runtime" "tokio vs async-std" --limit 8
```
`--mode` accepts the same values as [`searchMode`](/search/search-modes): `auto` (default), `low-cost`, `fast`, `balanced`, `deep`, `exhaustive`. Filters mirror the [query parameters](/features/query-parameters): `--country`, `--language`, `--safe`, `--date-range`, `--include-domains`, `--exclude-domains`. Add `--content` to return full page content for each result instead of extracts.
## Fetch a page
```bash theme={null}
andi fetch https://example.com/pricing --query "what does the team plan cost"
```
`--query` focuses the extracts on what you want from the page. See [content retrieval](/search/content-retrieval) for the underlying endpoint.
## Output
`--format auto|json|markdown` controls the shape. The default writes human-readable markdown on an interactive terminal and a JSON envelope (`{"ok": true, "data": ..., "meta": {...}}`) when piped or redirected. `--json` forces JSON. Exit codes distinguish auth failures, exhausted credits, and invalid arguments — run `andi schema` for a machine-readable description of every command, flag, and exit code.
## Local MCP server
`andi mcp` runs a stdio MCP server exposing the same tools as the [hosted MCP server](/resources/ai-agents#andi-search-mcp-server) — for MCP clients that only support stdio transport:
```json theme={null}
{
"mcpServers": {
"andi": {
"command": "npx",
"args": ["-y", "@andiai/cli", "mcp"],
"env": { "ANDI_API_KEY": "YOUR_API_KEY" }
}
}
}
```
For HTTP-capable clients, prefer the hosted endpoint directly — no install needed. See [build with AI agents](/resources/ai-agents).
## Let your agent set it up
Your agent installs and configures the Andi Search API itself — paste one line:
```text title="Paste into Claude Code, Cursor, or Codex" theme={null}
Set up Andi search for this project: andiai.com
```
Your agent fetches `andiai.com`, follows it to the [install guide](https://api.andiai.com/install.md) at `api.andiai.com`, then gets an API key from the [API Console](https://console.andiai.com/signup), connects the MCP server, and verifies the setup.
**Are you an AI agent?** Fetch [https://api.andiai.com/install.md](https://api.andiai.com/install.md) and follow it to configure Andi Search — MCP server, API key, and verification.
## Next steps
Hosted MCP server, Agent Skill, and discovery files.
Automatic effort by default, manual control when you want it.
Full parameter reference.
Full command and flag reference on GitHub.
# Quickstart
Source: https://docs.andiai.com/getting-started/quickstart
Set up Andi web search and page fetch in your agent or application. One-line agent install, CLI, or manual API key setup.
## Agent quick-start
Your agent installs and configures the Andi Search API itself — paste one line:
```text title="Paste into Claude Code, Cursor, or Codex" theme={null}
Set up Andi search for this project: andiai.com
```
Your agent fetches `andiai.com`, follows it to the [install guide](https://api.andiai.com/install.md) at `api.andiai.com`, then gets an API key from the [API Console](https://console.andiai.com/signup), connects the MCP server, and verifies the setup.
**Are you an AI agent?** Fetch [https://api.andiai.com/install.md](https://api.andiai.com/install.md) and follow it to configure Andi Search — MCP server, API key, and verification.
## CLI quick-start
Search from your terminal without writing any code — get a key from the [API Console](https://console.andiai.com/signup), then install the CLI and run it:
```bash theme={null}
export ANDI_API_KEY=YOUR_API_KEY
npm install -g @andiai/cli
andi search "what is RAG"
andi fetch https://en.wikipedia.org/wiki/Retrieval-augmented_generation
```
Or run it without installing:
```bash theme={null}
npx -y @andiai/cli search "what is RAG"
```
See the [CLI guide](/getting-started/cli) for output formats and the local MCP server.
## Manual setup
You need an API key to follow these steps. Get one from the [API Console](https://console.andiai.com/signup).
Sign in to the [API Console](https://console.andiai.com) and create an API key. Copy it — you'll need it for the next step. See [authentication](/getting-started/authentication) for key management and security practices.
Send a search query using the `x-api-key` header:
```bash curl theme={null}
curl "https://api.andiai.com/api/v1/search?q=what+is+RAG" \
-H "x-api-key: YOUR_API_KEY"
```
```python Python theme={null}
import requests
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "what is RAG"},
headers={"x-api-key": "YOUR_API_KEY"}
)
data = response.json()
for result in data["results"]:
print(result["title"], result["link"])
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.andiai.com/api/v1/search?q=what+is+RAG",
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(r => console.log(r.title, r.link));
```
The API returns a JSON object with top-level fields and a `results` array. Each result includes a `title`, `link`, `desc`, and `source`:
```json theme={null}
{
"results_type": "Search",
"answer": "",
"type": "Search",
"title": "what is RAG",
"results": [
{
"title": "Retrieval-Augmented Generation (RAG) Explained",
"link": "https://example.com/rag-explained",
"desc": "RAG combines a retrieval system with a language model to generate responses grounded in retrieved documents.",
"source": "example.com"
}
],
"metrics": {
"query": "what is RAG",
"intent": "FallbackSearchIntent",
"timestamp": "2026-07-14T21:30:00.000Z",
"duration": 1240,
"queries_executed": 1,
"api_requests_count": 1,
"results_returned": 10,
"total_results_found": 42,
"cost_dollars": 0.0031
}
}
```
The `metrics.cost_dollars` field shows the amount charged for this request in USD.
The second core endpoint, `/api/v1/fetch`, retrieves a single page as clean extracted content — use it to read a result in full after searching:
```bash curl theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://en.wikipedia.org/wiki/Retrieval-augmented_generation" \
-H "x-api-key: YOUR_API_KEY"
```
```python Python theme={null}
import requests
response = requests.get(
"https://api.andiai.com/api/v1/fetch",
params={"url": "https://en.wikipedia.org/wiki/Retrieval-augmented_generation"},
headers={"x-api-key": "YOUR_API_KEY"}
)
page = response.json()
print(page["title"], page["word_count"])
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.andiai.com/api/v1/fetch?url=" +
encodeURIComponent("https://en.wikipedia.org/wiki/Retrieval-augmented_generation"),
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
const page = await response.json();
console.log(page.title, page.word_count);
```
The response includes the page `title`, extracted `content` and `markdown`, `word_count`, and `metrics.cost_dollars`. Add `format=context` for LLM-ready markdown, or `query=...` for extracts focused on what you want from the page. See [content retrieval](/search/content-retrieval) for the full reference.
## Next steps
Automatic effort by default, manual control when you want it.
Connect via MCP for Claude Code, Cursor, and other tools.
Full fetch endpoint reference.
Full parameter reference for the search endpoint.
# Andi AI Search API
Source: https://docs.andiai.com/index
Web search API for AI agents, RAG pipelines, and search applications. Structured, accurate results from a web index of tens of billions of pages.
The Andi AI Search API gives your applications access to web search results scored for accuracy and relevance, drawn from an index of tens of billions of pages. Responses are structured for direct use in AI agents, RAG pipelines, and search interfaces — ranked #1 for accuracy in independent search benchmarks.
## Add Andi to your agent
Your agent installs and configures the Andi Search API itself — paste one line:
```text title="Paste into Claude Code, Cursor, or Codex" theme={null}
Set up Andi search for this project: andiai.com
```
Your agent fetches `andiai.com`, follows it to the [install guide](https://api.andiai.com/install.md) at `api.andiai.com`, then gets an API key from the [API Console](https://console.andiai.com/signup), connects the MCP server, and verifies the setup.
**Are you an AI agent?** Fetch [https://api.andiai.com/install.md](https://api.andiai.com/install.md) and follow it to configure Andi Search — MCP server, API key, and verification.
Make your first API call in minutes.
Search and fetch from the command line.
MCP server, install.md, and agent tools.
Working code for common use cases.
## Base URL
```text theme={null}
https://api.andiai.com
```
## Authentication
All requests require an API key passed in the `x-api-key` header. Get your key from the [API Console](https://console.andiai.com/signup). The same key works across the REST API, the CLI, and the MCP server — see [authentication](/getting-started/authentication) for key management and security practices.
## Core endpoints
`/api/v1/search` searches the web. [`/api/v1/fetch`](/search/content-retrieval) reads a page as clean extracted content:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=hello+world" \
-H "x-api-key: YOUR_API_KEY"
```
```bash theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://example.com/article" \
-H "x-api-key: YOUR_API_KEY"
```
## CLI
The [Andi CLI](/getting-started/cli) wraps search, fetch, and a local MCP server — search from your terminal without writing any code. It reads your key from the `ANDI_API_KEY` environment variable:
```bash theme={null}
export ANDI_API_KEY=YOUR_API_KEY
npm install -g @andiai/cli
andi search "hello world"
```
Or run it without installing: `npx -y @andiai/cli search "hello world"`
## MCP server
AI agents connect to the hosted [MCP server](/resources/ai-agents) to use Andi web search and page fetch as tools, authenticated with the same `x-api-key` header:
```text theme={null}
https://api.andiai.com/mcp
```
See [build with AI agents](/resources/ai-agents) for setup in Claude Code, Cursor, VS Code, Codex, and other MCP clients.
## What you can build
* **AI agents** that retrieve current information from the web
* **RAG applications** with real-time search grounding
* **Research tools** that gather and synthesize web sources
* **Search features** embedded in your own products
## Popular examples
Search results as context for LLM generation.
Define search as a tool for AI agents.
Automated news search with date filtering.
# Build with AI agents
Source: https://docs.andiai.com/resources/ai-agents
Connect AI agents to Andi web search via MCP, and to the Andi docs via MCP, llms.txt, and markdown content negotiation. Optimized for Claude Code, Cursor, VS Code, and other AI tools.
The Andi AI Search API is built for AI agents. You can call the REST endpoints from agents you build (see the [quickstart](/getting-started/quickstart) and [AI agent tool example](/examples/ai-agent-tool)), or connect the MCP server to your coding tools for web search and page fetch.
## Add Andi to your agent
Your agent installs and configures the Andi Search API itself — paste one line:
```text title="Paste into Claude Code, Cursor, or Codex" theme={null}
Set up Andi search for this project: andiai.com
```
Your agent fetches `andiai.com`, follows it to the [install guide](https://api.andiai.com/install.md) at `api.andiai.com`, then gets an API key from the [API Console](https://console.andiai.com/signup), connects the MCP server, and verifies the setup.
**Are you an AI agent?** Fetch [https://api.andiai.com/install.md](https://api.andiai.com/install.md) and follow it to configure Andi Search — MCP server, API key, and verification.
The [install guide](https://api.andiai.com/install.md) includes setup snippets for Claude Code, Cursor, Codex, and generic MCP clients.
## Andi Search MCP server
Use Andi as your agent's web search and page fetch tool over the [Model Context Protocol](https://modelcontextprotocol.io/).
**Server URL:** `https://api.andiai.com/mcp`
**Transport:** Streamable HTTP
**Authentication:** Pass your API key in the `x-api-key` header. Get a key from the [API Console](https://console.andiai.com/signup).
Your API key is account-level, not project-level, so a system-wide install works everywhere at once. Each client below notes how to choose between project and system-wide scope.
### Claude Code
For the current project:
```bash theme={null}
claude mcp add --transport http andi https://api.andiai.com/mcp --header "x-api-key: YOUR_API_KEY"
```
System-wide, for all projects:
```bash theme={null}
claude mcp add --scope user --transport http andi https://api.andiai.com/mcp --header "x-api-key: YOUR_API_KEY"
```
### Cursor
Add to `.cursor/mcp.json` in your project, or to `~/.cursor/mcp.json` for all projects:
```json theme={null}
{
"mcpServers": {
"andi": {
"url": "https://api.andiai.com/mcp",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}
```
### Claude web and desktop
Add this as a custom connector in your MCP server settings:
```json theme={null}
{
"mcpServers": {
"andi": {
"url": "https://api.andiai.com/mcp",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}
```
### VS Code
Add to your VS Code settings (JSON):
```json theme={null}
{
"mcp": {
"servers": {
"andi": {
"type": "http",
"url": "https://api.andiai.com/mcp",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}
}
```
### Codex CLI
Add to `~/.codex/config.toml` (system-wide — Codex config applies to all projects), with `ANDI_API_KEY` exported in your shell:
```toml theme={null}
[mcp_servers.andi]
url = "https://api.andiai.com/mcp"
env_http_headers = { "x-api-key" = "ANDI_API_KEY" }
```
The same config is shared by the Codex CLI, IDE extension, and ChatGPT desktop app.
### Other MCP clients
Any client that supports the Streamable HTTP transport can connect: server URL `https://api.andiai.com/mcp`, with your API key in the `x-api-key` request header.
### Stdio-only clients
For MCP clients that only support the stdio transport, the [Andi CLI](/getting-started/cli) runs a local server exposing the same tools:
```json theme={null}
{
"mcpServers": {
"andi": {
"command": "npx",
"args": ["-y", "@andiai/cli", "mcp"],
"env": { "ANDI_API_KEY": "YOUR_API_KEY" }
}
}
}
```
## MCP tools
### `andi_web_search`
Searches the web and returns LLM-ready markdown. Wraps `GET /api/v1/search` with `format=context` and `metadata=full`.
| Parameter | Type | Default | Description |
| ------------------ | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `q` | string | — | Search query. Supports [query operators](/features/query-operators). Required unless `queries` is supplied. |
| `queries` | array of strings | — | Run up to 5 related queries in one call and get fused, deduplicated results. Alternative to `q` — supply exactly one of the two. |
| `limit` | integer | `10` | Results to return (1–50) |
| `offset` | integer | `0` | Skip this many results — paginate deeper into an existing result set without re-searching |
| `searchMode` | string | `auto` | `auto` (default) sets the search effort per query automatically. Fixed modes — `low-cost`, `fast`, `balanced`, `deep`, `exhaustive` — pin an effort level: `fast` \~1s lowest latency; `balanced` \~1–2s everyday web search; `deep` \~2–3s adds spell correction and broader coverage; `exhaustive` multi-round agentic retrieval (up to \~15s). |
| `effort` | string | — | Pins an effort level by generic tier name instead of a mode: `low`, `medium`, `high`, `max`. An explicit `searchMode` wins over `effort`. Omit for adaptive. |
| `content` | boolean | `false` | Include cleaned page content for each result. Increases cost and tokens. |
| `maxContentLength` | integer | — | Maximum content characters per result when `content=true` |
| `country` | string | — | ISO 3166-1 country code (e.g., `US`, `GB`) |
| `language` | string | — | ISO 639-1 language code (e.g., `en`, `es`) |
| `safe` | string | — | Safe search: `off`, `moderate`, or `strict` |
| `dateRange` | string | — | Recency filter: `24h`, `7d`, `30d`, `90d`, `1y` |
| `includeDomains` | string | — | Comma-separated domains to restrict results to |
| `excludeDomains` | string | — | Comma-separated domains to exclude |
The MCP tool caps `limit` at 50. The REST endpoint allows up to 100.
Results carry the `metadata=full` fields — `content_type`, `word_count`, `lang`, `publisher`, `summary` — in each result's frontmatter when available (see [response format](/features/response-format#per-result-structure)), plus a per-call `cost_dollars`. Results default to extracts; set `content=true` or follow up with `andi_fetch_url` only when extracts aren't enough.
### `andi_fetch_url`
Fetches a web page and returns its content as LLM-ready markdown. Wraps `GET /api/v1/fetch`.
| Parameter | Type | Default | Description |
| ------------------ | ------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `url` | string | **required** | URL to fetch |
| `query` | string | — | What you want from the page. Returns query-focused extracts (`query_extracts`/`query_snippet`) instead of only full content. |
| `maxContentLength` | integer | `100000` | Maximum content length in characters (minimum 500, maximum 200000) |
See [content retrieval](/search/content-retrieval) for the full fetch endpoint reference.
### Error handling
MCP tool errors come back as a text result with `isError: true`, not a raw HTTP status — the message is written for the calling agent to act on:
| Cause | What the agent sees |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Rate limited (`429`) | Retry-after guidance in seconds |
| Out of credits (`402`) | A message pointing to [console.andiai.com](https://console.andiai.com) to top up |
| Temporarily unavailable / still warming (`503`) | A retry hint, with a wait time when the server provides one |
| Fetch page-level failure (`422`) | The reason (`not_found`, `blocked`, or `unextractable`) and a note that retrying the same URL won't help |
## Agent Skill
The API hosts an installable [Agent Skill](https://agentskills.io/) that gives agents a description of the search tool, its parameters, and usage guidance. Agents that support skills can install it directly:
```bash theme={null}
npx skills add https://api.andiai.com/.well-known/skills/andi-web-search/SKILL.md
```
The skill is also available at `https://api.andiai.com/.well-known/agent-skills/andi-web-search/SKILL.md`, with an index at [`/.well-known/agent-skills/index.json`](https://api.andiai.com/.well-known/agent-skills/index.json).
### Claude Code plugin
The [`andisearch/andi-agent-skills`](https://github.com/andisearch/andi-agent-skills) repository doubles as a Claude Code plugin marketplace. The `andi` plugin installs the `andi-web-search` skill and connects the hosted MCP server in one step:
```text theme={null}
/plugin marketplace add andisearch/andi-agent-skills
/plugin install andi@andi-agent-skills
```
Set `ANDI_API_KEY` in your environment before first use.
### Install from the GitHub repo
The same repository works with the skills CLI — `npx skills add andisearch/andi-agent-skills` — and with Codex's skill installer. See the [repo README](https://github.com/andisearch/andi-agent-skills#install) for the full install matrix, including the Codex plugin.
## Agent-native output
The API supports `format=context`, which returns search results as markdown with YAML frontmatter — sized for LLM context windows and ready to use without JSON parsing. See [response format](/features/response-format) for the full contract.
## Keyless pay-per-call
Agents can also pay per call via x402 through [Orthogonal](https://x402.orth.sh) without signing up for an API key. See the [auth guide](https://api.andiai.com/auth.md) for details.
## API discovery files
The API publishes these files for automated agent and tool integration:
| URL | Description |
| -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| [`/install.md`](https://api.andiai.com/install.md) | Step-by-step agent self-install guide |
| [`/llms.txt`](https://api.andiai.com/llms.txt) | API summary and links for LLMs |
| [`/auth.md`](https://api.andiai.com/auth.md) | Agent-readable guide to authentication and x402 |
| [`/openapi.json`](https://api.andiai.com/openapi.json) | OpenAPI specification |
| [`/.well-known/api-catalog`](https://api.andiai.com/.well-known/api-catalog) | RFC 9727 API catalog entry |
| [`/.well-known/integrations.json`](https://api.andiai.com/.well-known/integrations.json) | Integration metadata |
| [`/.well-known/mcp/server-card.json`](https://api.andiai.com/.well-known/mcp/server-card.json) | MCP server card (transport, docs URL) |
| [`/.well-known/agent-card.json`](https://api.andiai.com/.well-known/agent-card.json) | A2A-style agent card: tools, transports, and auth scheme |
| [`/.well-known/skills/andi-web-search/SKILL.md`](https://api.andiai.com/.well-known/skills/andi-web-search/SKILL.md) | Installable Agent Skill |
| [`/.well-known/oauth-protected-resource`](https://api.andiai.com/.well-known/oauth-protected-resource) | OAuth protected resource metadata |
## Docs Q\&A MCP server (searches this documentation only)
This docs site runs its own [Model Context Protocol](https://modelcontextprotocol.io/) server, separate from the Andi Search MCP server above. It lets AI tools search and read *this documentation* — it does not perform web search.
**Server URL:** `https://docs.andiai.com/mcp`
### Claude Code
```bash theme={null}
claude mcp add --transport http andi-docs https://docs.andiai.com/mcp
```
### Claude web and desktop
Add this to your MCP server settings:
```json theme={null}
{
"mcpServers": {
"andi-docs": {
"url": "https://docs.andiai.com/mcp"
}
}
}
```
### Cursor
Add to your `.cursor/mcp.json`:
```json theme={null}
{
"mcpServers": {
"andi-docs": {
"url": "https://docs.andiai.com/mcp"
}
}
}
```
### VS Code
Add to your VS Code settings (JSON):
```json theme={null}
{
"mcp": {
"servers": {
"andi-docs": {
"type": "http",
"url": "https://docs.andiai.com/mcp"
}
}
}
}
```
## llms.txt
The site publishes [llms.txt](https://llmstxt.org/) files that list all documentation pages with direct markdown URLs.
| File | Contents |
| --------------------------------------------------------- | ------------------------------------------------ |
| [`/llms.txt`](https://docs.andiai.com/llms.txt) | Page titles and descriptions with markdown links |
| [`/llms-full.txt`](https://docs.andiai.com/llms-full.txt) | Full page content inlined as markdown |
Use `/llms.txt` for discovery and navigation. Use `/llms-full.txt` when you need the complete documentation in a single request.
## Markdown content negotiation
Any docs page returns clean markdown when requested with the `Accept: text/markdown` header. The markdown response includes YAML frontmatter.
```bash theme={null}
curl -H "Accept: text/markdown" https://docs.andiai.com/getting-started/quickstart
```
The response includes `Link` headers pointing to `/llms.txt` and `/llms-full.txt`, and an `X-Llms-Txt` header.
## AI crawler access
The site's `robots.txt` explicitly allows AI crawlers including GPTBot, ClaudeBot, and PerplexityBot.
## Next steps
Search and fetch workflows for AI agents.
Full fetch endpoint reference.
Make your first API call.
JSON and context format output.
# Error handling
Source: https://docs.andiai.com/resources/error-handling
Error response format, status codes, and retry strategies for the Andi AI Search API.
When a request fails, the API returns an error response with an HTTP status code and a JSON body describing the problem.
## Error response format
All errors return both an `error` field and a `message` field:
```json theme={null}
{
"error": "Unauthorized",
"message": "Invalid API key"
}
```
## Status codes
| Code | Error | Description |
| ----- | ----------------------- | ------------------------------------------------------------------- |
| `400` | Varies | Missing or invalid parameters. Check the `error` field for details. |
| `401` | `Unauthorized` | Missing or invalid API key. |
| `402` | `Insufficient Credits` | Account balance depleted. |
| `429` | `Too Many Requests` | Rate limit exceeded. |
| `500` | `Internal server error` | Something went wrong on our end. |
The fetch endpoint can also return `422` (page-level fetch failure: `not_found`, `blocked`, or `unextractable`) and `503` (temporarily unavailable — retry). See [content retrieval error handling](/search/content-retrieval#error-handling).
**Causes:** Missing `q` parameter, invalid parameter values, malformed JSON array in `q`.
**Example messages:**
* `Missing required parameter: q`
* `Invalid value for 'searchMode'`
* `Invalid value for 'effort'. Must be one of: low, medium, high, max.`
**Fix:** Check your query string parameters against the [parameter reference](/features/query-parameters).
**Causes:** Missing `x-api-key` header, invalid or revoked API key.
**Example messages:**
* `Missing x-api-key header`
* `Invalid API key`
**Fix:** Verify your API key in the [API Console](https://console.andiai.com). Make sure you're passing it in the `x-api-key` header (not as a query parameter or in the `Authorization` header).
**Cause:** Your account has no remaining credits.
**Message:** `Your account has insufficient credits. Please add credits to continue.`
**Fix:** [Add credits](https://console.andiai.com) to your account.
**Cause:** Too many requests in the current time window.
**Message:** `Rate limit of N requests per second exceeded`
**Fix:** Back off and retry after the period specified in the `Retry-After` header. See [rate limits](/resources/rate-limits) for monitoring usage.
**Cause:** An unexpected error on our side.
**Fix:** Retry with exponential backoff. If it persists, contact [support](mailto:hello+api@andiai.com).
## Handling errors in code
Check the HTTP status code before parsing the response body:
```bash curl theme={null}
response=$(curl -s -w "\n%{http_code}" \
"https://api.andiai.com/api/v1/search?q=test" \
-H "x-api-key: $ANDI_API_KEY")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
case $http_code in
200) echo "$body" | jq '.results[:3]' ;;
429) echo "Rate limited. Retry after $(echo "$body" | jq -r '.message')" ;;
402) echo "Out of credits. Visit console.andiai.com" ;;
*) echo "Error $http_code: $(echo "$body" | jq -r '.error')" ;;
esac
```
```python Python theme={null}
import requests
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "test"},
headers={"x-api-key": "YOUR_API_KEY"},
)
if response.status_code == 200:
data = response.json()
print(f"Found {len(data['results'])} results")
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", 1)
print(f"Rate limited. Retry after {retry_after} seconds.")
elif response.status_code == 402:
print("Out of credits. Add credits at console.andiai.com")
else:
error = response.json()
print(f"Error {response.status_code}: {error['error']}")
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.andiai.com/api/v1/search?q=test",
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
if (response.ok) {
const data = await response.json();
console.log(`Found ${data.results.length} results`);
} else if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || 1;
console.log(`Rate limited. Retry after ${retryAfter}s`);
} else if (response.status === 402) {
console.log("Out of credits. Add credits at console.andiai.com");
} else {
const error = await response.json();
console.log(`Error ${response.status}: ${error.error}`);
}
```
## Retry with exponential backoff
For transient errors (`429`, `500`), retry with increasing delays:
```python Python theme={null}
import time
import requests
def search_with_retry(query: str, max_retries: int = 5) -> dict:
"""Search with exponential backoff on transient errors."""
delay = 1
for attempt in range(max_retries + 1):
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": query},
headers={"x-api-key": "YOUR_API_KEY"},
)
if response.status_code == 200:
return response.json()
if response.status_code in (429, 500) and attempt < max_retries:
wait = int(response.headers.get("Retry-After", delay))
time.sleep(wait)
delay = min(delay * 2, 60)
continue
response.raise_for_status()
```
```javascript JavaScript theme={null}
async function searchWithRetry(query, maxRetries = 5) {
let delay = 1000;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(
`https://api.andiai.com/api/v1/search?q=${encodeURIComponent(query)}`,
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
if (response.ok) return response.json();
if ((response.status === 429 || response.status === 500) && attempt < maxRetries) {
const retryAfter = response.headers.get("Retry-After");
const wait = retryAfter ? parseInt(retryAfter) * 1000 : delay;
await new Promise((r) => setTimeout(r, wait));
delay = Math.min(delay * 2, 60000);
continue;
}
throw new Error(`Search failed: ${response.status}`);
}
}
```
Do not retry `400`, `401`, or `402` errors — these indicate problems that won't resolve by retrying. Fix the request or account issue first.
## Troubleshooting
| Symptom | Likely cause | Fix |
| ------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `Missing x-api-key header` | Header not sent | Add `-H "x-api-key: YOUR_KEY"` to your request |
| `Invalid API key` | Wrong or revoked key | Check the key in the [API Console](https://console.andiai.com) |
| `Missing required parameter: q` | No query provided | Add `?q=your+query` to the URL |
| Slow responses | Using `searchMode=deep` or `metadata=full` | Switch to `searchMode=fast` (or rely on the default `auto`) and `metadata=basic` if speed matters |
| Empty `results` array | No matches for query/filters | Broaden your query or loosen filters |
## Next steps
Rate limit headers and monitoring.
API key management and security.
# Rate limits
Source: https://docs.andiai.com/resources/rate-limits
Rate limit configuration and handling for the Andi AI Search API.
Rate limits control how many requests an API key can make within a time window. They protect the service and ensure fair usage across all consumers.
## Default limits
Rate limits are configured per API key through the [API Console](https://console.andiai.com). Limits use a per-second sliding window. The default limit is set when you create a key and can be adjusted based on your needs.
Contact [support](mailto:hello+api@andiai.com) if you need higher rate limits than what's available in the console.
## Rate limit headers
When your API key has a rate limit configured, responses include rate limit headers:
| Header | Description |
| ----------------------- | --------------------------------------------------------- |
| `X-RateLimit-Limit` | Maximum requests allowed per second |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset` | Unix timestamp (seconds) when the window resets |
| `Retry-After` | Seconds to wait before retrying (only on `429` responses) |
## Handling 429 responses
When you exceed your rate limit, the API returns a `429` status code:
```json theme={null}
{
"error": "Too Many Requests",
"message": "Rate limit of N requests per second exceeded"
}
```
Use the rate limit headers to monitor usage and the `Retry-After` header to pace retries:
```python theme={null}
import time
import requests
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "test"},
headers={"x-api-key": "YOUR_API_KEY"}
)
# Check remaining quota on any response
remaining = int(response.headers.get("X-RateLimit-Remaining", 0))
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 1))
time.sleep(retry_after)
# Retry the request
```
## Monitoring usage
Use the `X-RateLimit-Remaining` header to track how close you are to your limit in real time. For aggregate usage and credit consumption, see the [API Console](https://console.andiai.com).
## Next steps
Error codes and retry strategies.
API key management and security.
# Content retrieval
Source: https://docs.andiai.com/search/content-retrieval
Fetch and extract clean content from any web page with the /api/v1/fetch endpoint.
The fetch endpoint extracts clean, structured content from a web page. You get the page title, description, full text, and metadata — without writing a scraper. It returns JSON by default, or LLM-ready markdown with `format=context`.
```bash theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://example.com/article" \
-H "x-api-key: YOUR_API_KEY"
```
## Parameters
| Parameter | Type | Default | Description |
| ------------------ | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `url` | string | **required** | Absolute URL to fetch (http or https) |
| `format` | string | `json` | `json` or `context` (markdown with YAML frontmatter) |
| `effort` | string | — | How thoroughly to retrieve the page: `low`, `medium`, `high`, `max`. Omit for the adaptive default. See [using the effort parameter](#using-the-effort-parameter). |
| `maxContentLength` | integer | 200000 | Maximum content length in characters. You can lower this, not raise it. |
| `query` | string | — | When set, the response includes `query_extracts` and `query_snippet` scoped to this query. |
## JSON response
```json theme={null}
{
"url": "https://example.com/article",
"title": "Understanding Retrieval-Augmented Generation",
"description": "RAG combines retrieval with generation for grounded LLM responses.",
"content": "Retrieval-Augmented Generation (RAG) is a technique that...",
"markdown": "# Understanding Retrieval-Augmented Generation\n\nRAG is a technique...",
"extracts": [
"RAG retrieves relevant documents from a knowledge base before generating a response.",
"This grounds the model's output in factual, up-to-date information."
],
"snippet": "RAG combines retrieval with generation for grounded LLM responses.",
"author": "Jane Smith",
"date": "2026-06-15",
"site_name": "Example Blog",
"image": "https://example.com/images/rag-diagram.png",
"lang": "en",
"word_count": 1850,
"truncated": false,
"metrics": {
"duration_ms": 1240,
"cost_dollars": 0.001
}
}
```
Fields like `author`, `date`, `image`, and `site_name` appear when the page provides them.
## Context format
With `format=context`, the response is markdown with YAML frontmatter — ready to pass into an LLM context window:
```bash theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://example.com/article&format=context" \
-H "x-api-key: YOUR_API_KEY"
```
```yaml theme={null}
---
title: Understanding Retrieval-Augmented Generation
url: https://example.com/article
description: RAG combines retrieval with generation for grounded LLM responses.
author: Jane Smith
date_published: 2026-06-15
source: example.com
lang: en
word_count: 1850
cost_dollars: 0.001
retrieved_at: 2026-07-14T21:30:00Z
---
Retrieval-Augmented Generation (RAG) is a technique that...
```
## Using the `query` parameter
Pass a `query` to get passage-level extracts scoped to a specific question:
```bash theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://example.com/article&query=how+does+RAG+reduce+hallucination" \
-H "x-api-key: YOUR_API_KEY"
```
The response includes `query_extracts` (passages relevant to the query), `query_snippet` (a query-focused summary), and `query_hash`.
## Using the `effort` parameter
Pass `effort` to control how thoroughly the page is retrieved — the same tier names used by [search modes](/search/search-modes#the-effort-parameter):
```bash theme={null}
curl "https://api.andiai.com/api/v1/fetch?url=https://example.com/article&effort=high" \
-H "x-api-key: YOUR_API_KEY"
```
`low` favors speed; `max` spends the most time extracting content, useful for pages that are slow to load or render content client-side. Omit `effort` to use the server's adaptive default. Invalid values return a `400` listing the valid tiers.
Fetch failures (`422`, `503`) are never billed, regardless of `effort`.
## Partial responses
A `200` response can include partial content when the page is slow to fully retrieve:
| Field | Type | Description |
| --------------------- | ------- | ---------------------------------------------------------------------------- |
| `partial` | boolean | `true` when the returned content is a best-effort excerpt, not the full page |
| `retry_after_seconds` | integer | Seconds to wait before re-requesting the full document |
Treat `partial: true` as incomplete — re-request the same URL after `retry_after_seconds` to get the full content. This differs from a `503`, where no content was extracted at all.
## Error handling
The fetch endpoint uses two distinct error codes for page-level failures:
| Status | Meaning | What to do |
| ------- | -------------------------------------------------------------- | ------------------------------------------ |
| **503** | Page is still being retrieved, or a transient failure occurred | Retry after the `Retry-After` header value |
| **422** | Page was reached but content could not be extracted | Do not retry — the page is not extractable |
Both 422 and 503 responses are **not billed**. You only pay for successful extractions.
Other errors (400 for a missing or invalid URL, 401, 402, 429) follow the same patterns as the search endpoint — see [error handling](/resources/error-handling).
## Pricing
Fetch calls are billed at a flat $0.001 base per request, plus $0.05 per 1M tokens of extracted content. Failed fetches (422 and 503) are free.
The billed amount is returned in `metrics.cost_dollars` (JSON) or `cost_dollars` in the frontmatter (`format=context`).
## Search extracts as an alternative
If you already have search results and want passage-level text without fetching each page, use `extracts=true` on the search endpoint instead. This returns text extracts inline on each result at no additional cost:
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=how+does+RAG+work&extracts=true" \
-H "x-api-key: YOUR_API_KEY"
```
The fetch endpoint is for when you need the full page content, or when you want to retrieve a specific URL that may not appear in search results.
## MCP tool
The `andi_fetch_url` MCP tool wraps this endpoint. See [Build with AI agents](/resources/ai-agents) for setup instructions.
## Next steps
Automatic effort by default, manual control when you want it.
Full parameter reference including `extracts`.
JSON and context format output structure.
MCP server and agent integration.
# Deep mode
Source: https://docs.andiai.com/search/deep-search
Searches your topic from multiple angles to find higher-quality results, with spell correction, in ~2–3 seconds.
Deep mode explores your topic from multiple angles to find results that a single quick search might miss. It applies stronger reranking so only strong, relevant matches make the cut, and corrects any typos in your query. Responses take about 2–3 seconds.
The default `auto` mode escalates to this treatment on its own when a query needs it — pin `searchMode=deep` when you want it on every call. Equivalent to [`effort=high`](/search/search-modes#the-effort-parameter), if you use the generic effort dial instead of naming a mode.
```bash curl theme={null}
curl "https://api.andiai.com/api/v1/search?q=quantm+computing+applications&searchMode=deep" \
-H "x-api-key: YOUR_API_KEY"
```
```python Python theme={null}
import requests
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "quantm computing applications", "searchMode": "deep"},
headers={"x-api-key": "YOUR_API_KEY"}
)
data = response.json()
if data.get("correctedQuery"):
print(f"Corrected: {data['correctedQuery']}")
for result in data["results"]:
print(f"{result['title']} — {result['source']}")
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.andiai.com/api/v1/search?q=quantm+computing+applications&searchMode=deep",
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();
if (data.correctedQuery) {
console.log(`Corrected: ${data.correctedQuery}`);
}
data.results.forEach(r => console.log(`${r.title} — ${r.source}`));
```
The example query intentionally misspells "quantum" — deep mode corrects it automatically.
## What deep mode adds
* **Query expansion** — expands your query into related variations and fans out follow-up searches from what the first pass finds
* **Thorough coverage** — finds results across multiple angles of your topic, covering sources a single search might miss
* **Higher quality results** — stronger reranking keeps only strong, relevant matches
* **Spell correction** — fixes typos and misspellings in the query
## When to pin deep mode
* You want thorough coverage on every call, regardless of how simple a query looks
* Queries come from user input that may contain typos
* You want higher-quality results and can accept a 2–3 second response
If only some of your queries need this treatment, `auto` applies it selectively — pinning is for when you want it guaranteed.
## Example response
```json theme={null}
{
"results_type": "Search",
"answer": "",
"type": "Search",
"title": "quantum computing applications",
"results": [
{
"title": "Quantum Computing: Current Applications and Future Potential",
"link": "https://example.com/quantum-applications",
"desc": "Quantum computing is being applied in cryptography, drug discovery, and optimization problems...",
"source": "example.com"
}
],
"correctedQuery": "quantum computing applications",
"metrics": {
"query": "quantum computing applications",
"intent": "FallbackSearchIntent",
"timestamp": "2026-07-14T21:30:00.000Z",
"duration": 2340,
"queries_executed": 1,
"api_requests_count": 1,
"results_returned": 10,
"total_results_found": 85,
"cost_dollars": 0.0089
}
}
```
### Handling corrected queries
The `correctedQuery` field appears when the search detects and fixes a misspelling. Use it to show users what was actually searched:
```python theme={null}
data = response.json()
if data.get("correctedQuery"):
print(f"Showing results for: {data['correctedQuery']}")
```
Any mode can return `correctedQuery` when a correction is detected. Deep and exhaustive modes go further: they re-run the search with the corrected query, so the results themselves reflect the correction.
Combine deep mode with richer output options:
```bash theme={null}
# Deep mode with text extracts
curl "https://api.andiai.com/api/v1/search?q=quantum+computing&searchMode=deep&extracts=true" \
-H "x-api-key: YOUR_API_KEY"
# Deep mode with full metadata
curl "https://api.andiai.com/api/v1/search?q=quantum+computing&searchMode=deep&metadata=full" \
-H "x-api-key: YOUR_API_KEY"
# Deep mode as LLM context
curl "https://api.andiai.com/api/v1/search?q=quantum+computing&searchMode=deep&format=context" \
-H "x-api-key: YOUR_API_KEY"
```
## Next steps
Pinned low latency for real-time applications.
Automatic effort by default, manual control when you want it.
Multi-query search with result aggregation.
Full parameter reference.
# Fast mode
Source: https://docs.andiai.com/search/fast-search
Search mode optimized for speed, returning results in ~1 second.
Fast mode returns results in about 1 second. The default `auto` mode already resolves simple queries fast — pin `searchMode=fast` when you need that latency guaranteed on every call, such as real-time applications and high-volume workloads. Equivalent to [`effort=low`](/search/search-modes#the-effort-parameter), if you use the generic effort dial instead of naming a mode.
```bash curl theme={null}
curl "https://api.andiai.com/api/v1/search?q=latest+AI+news&searchMode=fast" \
-H "x-api-key: YOUR_API_KEY"
```
```python Python theme={null}
import requests
response = requests.get(
"https://api.andiai.com/api/v1/search",
params={"q": "latest AI news", "searchMode": "fast"},
headers={"x-api-key": "YOUR_API_KEY"}
)
data = response.json()
for result in data["results"]:
print(f"{result['title']} — {result['source']}")
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.andiai.com/api/v1/search?q=latest+AI+news&searchMode=fast",
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(r => console.log(`${r.title} — ${r.source}`));
```
## When to pin fast mode
* Real-time search in user-facing applications with a hard latency budget
* High-volume automated queries
* Applications where latency matters more than exhaustive coverage
For most applications, omit `searchMode` and let `auto` decide — it resolves simple queries fast on its own. See [search modes](/search/search-modes) for the full set.
## Example response
```json theme={null}
{
"results_type": "News",
"answer": "",
"type": "News",
"title": "latest AI news",
"results": [
{
"title": "Latest AI News and Developments",
"link": "https://example.com/ai-news",
"desc": "A roundup of the latest developments in artificial intelligence...",
"source": "example.com",
"date": "2026-07-14T10:00:00.000Z"
}
],
"metrics": {
"query": "latest AI news",
"intent": "LatestNewsIntent",
"timestamp": "2026-07-14T21:30:00.000Z",
"duration": 890,
"queries_executed": 1,
"api_requests_count": 1,
"results_returned": 10,
"total_results_found": 50,
"cost_dollars": 0.0031
}
}
```
### Parsing the response
```python theme={null}
data = response.json()
# Check what type of results came back
print(data["results_type"]) # e.g., "News", "Search", "Weather"
# Access results
for result in data["results"]:
print(result["title"], result["link"])
# Check performance and cost
print(f"Returned {data['metrics']['results_returned']} results in {data['metrics']['duration']}ms")
print(f"Cost: ${data['metrics']['cost_dollars']}")
```
Get richer data from each result:
```bash theme={null}
# Text extracts from result pages
curl "https://api.andiai.com/api/v1/search?q=latest+AI+news&extracts=true" \
-H "x-api-key: YOUR_API_KEY"
# Full metadata including content type and reader data
curl "https://api.andiai.com/api/v1/search?q=latest+AI+news&metadata=full" \
-H "x-api-key: YOUR_API_KEY"
# Markdown format for passing to LLMs
curl "https://api.andiai.com/api/v1/search?q=latest+AI+news&format=context" \
-H "x-api-key: YOUR_API_KEY"
```
`metadata=full` adds latency because it fetches additional data from each result page. Use `metadata=basic` (the default) unless you need `contentType` or `reader` data.
## Fast mode vs. deep mode
| | Fast mode | Deep mode |
| ---------------- | -------------- | ------------------------ |
| Response time | \~1 second | \~2–3 seconds |
| Spell correction | Detection only | Corrects and re-searches |
| Topic coverage | Single angle | Multiple angles |
| Result quality | Good | Thorough |
Start with `auto` — it picks between these per query. Pin fast mode when latency is critical; pin deep mode when thoroughness matters more than speed. See [search modes](/search/search-modes) for the full set of modes.
## Next steps
Multi-angle search with spell correction.
Automatic effort by default, manual control when you want it.
Complete integration with error handling.
Response structure and metrics.
# News feeds
Source: https://docs.andiai.com/search/news-feeds
Curated, ranked topic news feeds with the /api/v1/news/:topic endpoint — no query required.
The news endpoint returns a curated, ranked feed of recent headlines for a fixed topic. There is no `q` parameter — the topic in the URL path selects the feed:
```bash theme={null}
curl "https://api.andiai.com/api/v1/news/technology" \
-H "x-api-key: YOUR_API_KEY"
```
This is a fast path for "give me the latest on X" requests — one call in place of a search-then-fetch loop.
## Topics
`topic` is a path segment: `GET /api/v1/news/:topic`. 23 topics are available.
| Category | Topics |
| -------------- | -------------------------------------------------------------------------------------------------------------- |
| News | `technology`, `business`, `finance`, `politics`, `sports`, `health`, `science`, `world`, `entertainment`, `us` |
| Regions | `europe`, `uk`, `asia`, `middle-east`, `africa`, `latin-america`, `australia` |
| Extended | `programming`, `startups`, `media`, `crypto`, `legal` |
| Curated groups | `top`, `hn-frontpage`, `ai-news`, `tech-news` |
An unknown topic returns `404` with the full list of valid slugs — see [unknown topics](#unknown-topics) below.
## Parameters
| Parameter | Type | Default | Description |
| --------- | ------- | ------- | --------------------------------------------- |
| `limit` | integer | 20 | Number of results to return. Clamped to 1–50. |
| `noCache` | boolean | `false` | Bypass the cache and fetch a fresh feed. |
## Response
The response uses the same shape as [`/api/v1/search`](/features/response-format): `title`, `results`, `metrics`, and (when available) `images`. A `news` array is always present.
| Field | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `title` | Plain-language description of the feed, e.g. "Latest technology news and breaking tech industry headlines" |
| `results` | Articles ordered by relevance — a semantic pass tuned to the topic, weighted toward fresher articles |
| `news` | The same articles in strict reverse-chronological order (newest first) |
| `images` | Article images, in the order of `results`, when the underlying articles carry one |
| `metrics` | Request metrics — see [response format](/features/response-format#metrics) and [pricing](#pricing) below for the billing fields |
```json theme={null}
{
"results_type": "Search",
"answer": "",
"type": "Search",
"title": "Latest world news and breaking international headlines",
"results": [
{
"title": "Ceasefire talks resume after week-long pause",
"link": "https://example-news.com/world/ceasefire-talks-resume",
"desc": "Negotiators returned to the table Monday after diplomatic efforts stalled last week.",
"source": "example-news.com",
"date": "2026-07-27T14:32:00Z"
},
{
"title": "Central bank holds rates steady amid inflation concerns",
"link": "https://example-wire.com/economy/central-bank-holds-rates",
"desc": "Policymakers cited persistent inflation as the reason for pausing further cuts.",
"source": "example-wire.com",
"date": "2026-07-27T11:05:00Z"
}
],
"news": [
{
"title": "Ceasefire talks resume after week-long pause",
"link": "https://example-news.com/world/ceasefire-talks-resume",
"desc": "Negotiators returned to the table Monday after diplomatic efforts stalled last week.",
"source": "example-news.com",
"date": "2026-07-27T14:32:00Z"
},
{
"title": "Central bank holds rates steady amid inflation concerns",
"link": "https://example-wire.com/economy/central-bank-holds-rates",
"desc": "Policymakers cited persistent inflation as the reason for pausing further cuts.",
"source": "example-wire.com",
"date": "2026-07-27T11:05:00Z"
}
],
"images": [
{
"title": "Ceasefire talks resume after week-long pause",
"link": "https://example-news.com/world/ceasefire-talks-resume",
"image": "https://example-news.com/images/ceasefire-talks.jpg",
"source": "example-news.com",
"type": "image",
"thumbnail": "https://example-news.com/images/ceasefire-talks_thumb.jpg",
"width": "1280",
"height": "720"
}
],
"metrics": {
"query": "world",
"intent": "NewsSearchIntent",
"timestamp": "2026-07-27T14:35:02.101Z",
"duration": 412,
"queries_executed": 1,
"api_requests_count": 1,
"results_returned": 2,
"total_results_found": 2,
"cost_dollars": 0.00071
}
}
```
`results` and `news` contain the same articles — only the order differs. Reach for `results` when you want the best headlines first, and `news` when you want a chronological feed.
## Unknown topics
Requesting a topic that doesn't exist returns `404` with every valid slug, so a client can discover the full list from a single failed call:
```json theme={null}
{
"error": "Unknown topic",
"message": "No curated news feed exists for topic 'gardening'.",
"valid_topics": [
"technology", "business", "finance", "politics", "sports", "health",
"science", "world", "entertainment", "us", "europe", "uk", "asia",
"middle-east", "africa", "latin-america", "australia", "programming",
"startups", "media", "crypto", "legal", "top", "hn-frontpage", "ai-news", "tech-news"
]
}
```
## Errors
| Status | Meaning |
| ------ | ---------------------------------------------------- |
| `404` | Unknown topic. The response includes `valid_topics`. |
| `503` | The feed is temporarily unavailable. Retry shortly. |
`401`, `402`, and `429` follow the same patterns as the search endpoint — see [error handling](/resources/error-handling).
## Pricing
News requests are billed the same way as a search request: a per-token rate on the returned content, with no flat per-call fee. The charge is returned in `metrics.cost_dollars`, the same field the search and fetch endpoints use.
| Field | Description |
| -------------- | --------------------------------------------------------------------------- |
| `cost_dollars` | Amount charged to your account for this request in USD, after any discounts |
Cache hits are billed the token rate only (no vendor call runs), and are marked `cached: true` in `metrics`, alongside `cache_age_seconds` — see [metrics](/features/response-format#metrics).
## Use cases
The news endpoint is built for fixed, recurring feed requests — the kind of thing a slash command or a scheduled agent job asks for repeatedly, like "latest world news" or "programming news." Because the topic is fixed, there's no query to construct and no need for a separate fetch pass to pull in full articles: one call returns a ranked, deduplicated, freshly-dated feed ready to hand to a user or an LLM.
## Next steps
Full response structure and result fields.
Fetch the full text of any article returned in a feed.
Build a query-driven news monitor with the search endpoint.
Status codes and retry strategies.
# Search modes
Source: https://docs.andiai.com/search/search-modes
Andi sets the right search effort for every query automatically — or you can pin the effort level yourself with searchMode.
Every query needs a different amount of work. A currency conversion resolves in a single fast pass. A literature review needs multiple rounds of retrieval. The `searchMode` parameter controls who decides how much work each search gets: Andi, or you.
* **`auto` (default)** — Andi reads each query and sets the compute, models, and search depth it needs. No tuning required.
* **Fixed modes** — pin an effort level (`low-cost`, `fast`, `balanced`, `deep`, `exhaustive`) when you want the same behavior on every call.
## Automatic mode
When you omit `searchMode` (or set `searchMode=auto`), Andi decides per query how much effort the search needs. Simple lookups and navigational queries resolve fast. Complex, multi-faceted, or research questions get deeper treatment — query expansion, more search angles, follow-up searches, stronger reranking, spell correction. Multi-query requests get deep coverage automatically.
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=climate+change+effects" \
-H "x-api-key: YOUR_API_KEY"
```
You are charged for the work each search actually performs, not a flat per-call rate — so simple queries stay cheap even when your traffic mix includes hard ones. The billed amount is returned in every response as `metrics.cost_dollars`.
Use `auto` unless you have a specific reason not to. It adapts as your query mix changes, so you never have to retune mode choices as your application grows.
To see which mode a request resolved to, use `format=context` — the response frontmatter includes a `search_mode` field.
## Setting the effort yourself
When you want the same effort on every call — a hard latency budget, a cost ceiling, or research-grade coverage regardless of query — pin a mode:
| Mode | Typical response | What it does |
| ------------ | ---------------- | ---------------------------------------------------------------------------------------------------------- |
| `low-cost` | \~1–2s | Budget-constrained search. More coverage than `fast`, with a cost ceiling. |
| `fast` | \~1s | Single-pass search. Lowest latency. |
| `balanced` | \~1–2s | Everyday web search. Solid coverage and ranking without deep-mode latency. |
| `deep` | \~2–3s | Searches from multiple angles. Spell correction. Higher-quality results for complex queries. |
| `exhaustive` | Up to \~15s | Multi-round agentic retrieval. Keeps searching until it finds strong matches. Research-grade thoroughness. |
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=climate+change+effects&searchMode=deep" \
-H "x-api-key: YOUR_API_KEY"
```
### Choosing a mode
* Use `fast` when latency matters most: autocomplete, real-time UIs, high-volume batch jobs.
* Use `low-cost` when per-call spend matters most and you can trade some speed for coverage.
* Use `balanced` for general-purpose search when you want predictable mid-range latency.
* Use `deep` when result quality matters more than speed: research questions, multi-faceted topics, queries that might contain typos.
* Use `exhaustive` for tasks where missing a result is worse than waiting: due diligence, competitive analysis, literature review.
### What higher effort buys
Moving up the ladder does more than allow extra time. Deeper modes expand your query into related variations, fan out follow-up searches based on what the first pass finds, and apply stronger semantic reranking. `auto` uses the same machinery, scaled to what each query needs.
## The `effort` parameter
`effort` pins the same ladder as `searchMode`, using generic tier names instead of Andi's mode names — a second way in for agents that already speak a generic effort convention.
| Tier | Equivalent mode |
| -------- | --------------- |
| `low` | `fast` |
| `medium` | `balanced` |
| `high` | `deep` |
| `max` | `exhaustive` |
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=climate+change+effects&effort=high" \
-H "x-api-key: YOUR_API_KEY"
```
An explicit `searchMode` always wins over `effort` — if you send both, the named mode applies. Left at the default `auto`, an explicit `effort` pins its equivalent mode, same as naming that mode directly. Inside `low-cost`, `effort` shapes thoroughness within that lane's own pricing and limits rather than leaving the lane.
Invalid values return a `400` listing the valid tiers: `low`, `medium`, `high`, `max`.
The resolved tier is echoed back in `metrics.effort` (JSON) and `effort` in the frontmatter (`format=context`):
```yaml theme={null}
---
search_mode: "deep"
effort: "high"
---
```
`effort` also works on [`/api/v1/fetch`](/search/content-retrieval#using-the-effort-parameter), shaping how thoroughly a page is retrieved.
## The `reranker` parameter
The `reranker` parameter controls the strength of semantic reranking applied to your results:
| Value | Reranking strength |
| ---------------- | ------------------------------------------------------------------- |
| `auto` (default) | Chosen based on query and mode |
| `small` | Light reranking, fastest |
| `medium` | Moderate reranking |
| `large` | Strong reranking |
| `xl` | Strongest reranking — requires a `deep` or `exhaustive` mode budget |
Stronger reranking improves result ordering for complex or ambiguous queries, at the cost of additional latency. The `auto` default picks an appropriate level based on your `searchMode` and query.
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=effects+of+sleep+deprivation+on+cognition&searchMode=deep&reranker=large" \
-H "x-api-key: YOUR_API_KEY"
```
## Pricing
Search requests are priced on outcome: the charge reflects the actual work the search performed. With `auto`, that means the price follows the effort Andi chose for each query. With a pinned mode, cost is more uniform call to call. The billed amount is returned in every response as `metrics.cost_dollars` (JSON) or `cost_dollars` in the frontmatter (`format=context`).
```json theme={null}
{
"metrics": {
"cost_dollars": 0.0043,
"duration": 1240,
"results_returned": 10
}
}
```
## MCP tool
Agents using the [Andi Search MCP server](/resources/ai-agents#andi-search-mcp-server) control the same behavior through the `andi_web_search` tool — its `searchMode` and `effort` parameters accept the values on this page.
## Examples
### Auto mode (recommended)
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=CRISPR+gene+therapy+clinical+trials" \
-H "x-api-key: YOUR_API_KEY"
```
### Pinned fast mode for a real-time UI
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=USD+to+EUR&searchMode=fast" \
-H "x-api-key: YOUR_API_KEY"
```
### Pinned exhaustive mode for research
```bash theme={null}
curl "https://api.andiai.com/api/v1/search?q=CRISPR+gene+therapy+clinical+trials+2026&searchMode=exhaustive&limit=40" \
-H "x-api-key: YOUR_API_KEY"
```
## Next steps
When to pin fast mode and what to expect.
Multi-angle search with spell correction.
Full parameter reference.
Response structure and metrics fields.