mcp-searchapi
# mcp-searchapi
An [MCP](https://modelcontextprotocol.io) server for [SearchApi.io](https://www.searchapi.io). Eight search engines exposed as discrete tools, returning trimmed structured results instead of raw SERP payloads.
```bash
npx -y mcp-searchapi
```
## Why this exists
Two SearchApi MCP packages were already on npm when this was written. Both are single-version releases from mid-2025 that have not been updated since, neither has a test suite, and one of them targets SearchAPI**.site**, a different product. So this is not a third copy of the same thing:
**One tool per engine, not one `search` tool with a mode flag.** A model picks `google_jobs` correctly far more often than it picks `search(engine="google_jobs")`. Every parameter carries a description written as guidance, because those descriptions are the prompt the model actually reads.
**Trimmed structured output.** SearchApi returns results under a different key per engine (`organic_results`, `local_results`, `shopping_results`, `videos`, `jobs`), each with a different field set, and the payloads are large. Measured against live responses on 7 August 2026:
| Tool | Raw payload | Returned at `limit: 10` | Saved |
|---|---:|---:|---:|
| `google_search` | 171,668 B | 2,507 B | 98.5% |
| `google_shopping` | 117,262 B | 4,391 B | 96.3% |
| `google_maps` | 71,607 B | 3,172 B | 95.6% |
| `youtube_search` | 56,836 B | 3,587 B | 93.7% |
| `google_jobs` | 86,433 B | 7,772 B | 91.0% |
| `google_news` | 38,176 B | 4,221 B | 88.9% |
| `amazon_search` | 47,732 B | 7,718 B | 83.8% |
| `google_scholar` | 19,066 B | 5,813 B | 69.5% |
The percentage matters less than the ceiling: whatever the engine sends back, a page of results stays under 8KB. Handing a model 171KB of ad blocks, favicons and pagination tokens spends its context window on things it will never use.
Every result normalises to a common core (`position`, `title`, `url`, `snippet`) plus the extras that matter for that engine: price and rating for shopping, coordinates and opening state for maps, company and posting date for jobs, channel and view count for YouTube, citation counts for Scholar.
## Setup
Get a key at [searchapi.io](https://www.searchapi.io). The free tier is 100 searches a month.
### Claude Desktop
`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows:
```json
{
"mcpServers": {
"searchapi": {
"command": "npx",
"args": ["-y", "mcp-searchapi"],
"env": { "SEARCHAPI_API_KEY": "your-key" }
}
}
}
```
### Cursor
`~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"searchapi": {
"command": "npx",
"args": ["-y", "mcp-searchapi"],
"env": { "SEARCHAPI_API_KEY": "your-key" }
}
}
}
```
### VS Code
`.vscode/mcp.json`:
```json
{
"servers": {
"searchapi": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-searchapi"],
"env": { "SEARCHAPI_API_KEY": "your-key" }
}
}
}
```
### Hosted
```bash
SEARCHAPI_API_KEY=your-key npx -y mcp-searchapi --http --port 3000
```
Serves streamable HTTP at `/mcp` and a health check at `/health`. Stateless, so it scales horizontally without sticky sessions.
## Tools
| Tool | Engine | Use it for | Extra fields |
|---|---|---|---|
| `google_search` | Google | General questions, fact-checking, finding sources | `source`, `domain` |
| `google_news` | Google News | Recent events, "what's happening with X" | `source`, `date` |
| `google_shopping` | Google Shopping | Price comparison, product availability | `price`, `rating`, `reviews`, `seller`, `delivery` |
| `google_jobs` | Google Jobs | Vacancies, hiring, job market | `company`, `location`, `postedAt`, `scheduleType` |
| `google_scholar` | Google Scholar | Papers, citations, academic sources | `publication`, `authors`, `citedBy` |
| `google_maps` | Google Maps | Places, local business, "near me" | `address`, `rating`, `openState`, `latitude`, `longitude` |
| `youtube_search` | YouTube | Videos, tutorials, talks | `channel`, `views`, `length`, `publishedTime` |
| `amazon_search` | Amazon | Amazon product lookups | `asin`, `price`, `rating`, `isSponsored` |
### Parameters
`query` is required everywhere. `limit` (default 10, max 50) caps what comes back to the model. The rest are offered only on the engines that accept them:
| Parameter | Meaning | Available on |
|---|---|---|
| `location` | Geo-target as `City,Country` | search, news, shopping, jobs, maps |
| `gl` | Two-letter country code | search, news, shopping, maps, youtube, amazon |
| `hl` | Two-letter language code | all except amazon |
| `num` | Results to request from the engine | search, news, shopping, scholar |
| `page` | Page number, from 1 | all except youtube |
`google_jobs` deliberately does not offer `gl`: the API rejects country codes on that engine (`gl=ie` returns HTTP 400 `Unsupported value`). Use `location` instead. Advertising a parameter the API will reject is worse than not having it, because the model has no way to discover the constraint.
### Example
```
google_jobs(query: "backend engineer", location: "Dublin,Ireland", limit: 3)
```
```json
{
"engine": "google_jobs",
"query": "backend engineer",
"totalResults": 10,
"results": [
{
"position": 1,
"title": "Backend Engineer",
"url": "https://...",
"company": "Example Ltd",
"location": "Dublin, Ireland",
"via": "LinkedIn",
"postedAt": "2 days ago",
"scheduleType": "Full-time",
"snippet": "..."
}
]
}
```
`totalResults` reports what the engine returned, not what was kept, so the model can tell there is more available behind a higher `limit`.
## Errors
Failures come back as tool errors with something actionable, not a bare status code:
| Condition | Message |
|---|---|
| Missing key | Server exits at startup rather than failing on the first call |
| 401 | Names `SEARCHAPI_API_KEY` and links where to get one |
| 429 | Says quota or rate limit, and notes the free tier is 100/month |
| 400 | Passes through the API's own message, for example the `gl` rejection above |
| Timeout | Reports the elapsed limit; defaults to 30s |
| No results | Not an error. Returns an empty `results` array with a `notice` |
## Development
```bash
npm install
npm test # 77 tests, no network
npm run typecheck
npm run build
SEARCHAPI_API_KEY=... node scripts/smoke.mjs # end-to-end, costs 1 search
```
Tests run against real captured API responses in `test/fixtures/`, one per engine plus an empty-result case, so they exercise the shapes the API actually returns rather than the shapes it was assumed to return. Nothing in `npm test` touches the network.
Adding an engine is a data change: append an entry to `ENGINES` in `src/engines.ts` with its result key, supported parameters and a normaliser. The server registers tools from that registry.
## Licence
MIT
TDQS
Scored across 8 tools
Each tool targets a distinct search vertical (general web, news, shopping, jobs, academic, maps, videos, Amazon). The descriptions clearly specify when to use each, leaving no ambiguity between them.
Most tools follow a 'google_<service>' pattern, but youtube_search and amazon_search use a '<service>_search' pattern, creating a minor inconsistency. The overall style is still readable and predictable.
With 8 tools, the server is well-scoped for a search aggregation service. Each tool covers a major search category without unnecessary redundancy.
The tool surface covers a broad range of search needs: general web, news, shopping, jobs, academic, maps, videos, and e-commerce. For a search-focused server, this is a comprehensive set with no obvious missing core capabilities.