MCP server
Give AI agents hotel, flight and vacation-rental prices: connect Claude Code, Cursor or Claude Desktop to the ScraperCompany MCP server with your API key.
The ScraperCompany MCP server exposes the API's hotel, flight and vacation-rental data as tools an AI agent can call. Any Model Context Protocol client that can send a header works: Claude Code, Cursor, and Claude Desktop through a small bridge.
Overview
Endpoint | https://api.scrapercompany.com/mcp |
Transport | Streamable HTTP (MCP 2025-06-18; 2025-03-26 is also accepted). Stateless JSON responses: no sessions and no server-sent stream. The older HTTP+SSE transport (2024-11-05) is not served. |
Auth | Your API key, as x-api-key: YOUR_API_KEY or Authorization: Bearer YOUR_API_KEY (details) |
Billing | Each tool call is the API call it wraps: priced, rate-limited and recorded in your request history exactly as that call would be. Failed or empty results are free. |
Tools | 18, all read-only |
Connect a client
Claude Code
Add the server once; the key is read from your environment when you run the command.
claude mcp add --transport http scrapercompany https://api.scrapercompany.com/mcp \
--header "x-api-key: $SCRAPERCOMPANY_API_KEY"Cursor and other header-capable clients
In ~/.cursor/mcp.json (or your client's equivalent config), point a server at the URL and pass the key as a header:
{
"mcpServers": {
"scrapercompany": {
"url": "https://api.scrapercompany.com/mcp",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}Claude Desktop and stdio-only clients
Clients that only launch local (stdio) servers connect through the mcp-remote bridge. In claude_desktop_config.json:
{
"mcpServers": {
"scrapercompany": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.scrapercompany.com/mcp",
"--header", "x-api-key:${SCRAPERCOMPANY_API_KEY}"],
"env": { "SCRAPERCOMPANY_API_KEY": "YOUR_API_KEY" }
}
}
}Check the connection
Listing the tools is free and needs no session. A plain HTTP client gets the same JSON-RPC reply an MCP client does:
curl -X POST "https://api.scrapercompany.com/mcp" \
-H "x-api-key: $SCRAPERCOMPANY_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'A 401 means the key is missing or wrong. Treat the key like a password: keep it in the client's secret store or an environment variable rather than in a config file you share.
Tools
Each tool wraps one REST endpoint and takes that endpoint's request fields (most of them), validated the same way. Options that change a call's price, such as basis and verify_sources on the calendar, stay REST-only. Tool descriptions carry the cost too, so the agent sees what a call will charge before making it.
| Tool | Wraps | Credits |
|---|---|---|
resolve_hotelTurn a hotel name (plus city) into a Google property token. | 1 | |
resolve_google_hotelFind a hotel by name and place, with each candidate's mailing address. | 12 | |
hotel_guest_reviewsGuest reviews for one property, mixed by provider. | 2 per page | |
hotel_rate_calendarNightly prices for 1-330 nights in one call. | 5 | |
hotel_offersEvery booking site's price for one stay. | 3 | |
hotel_destination_searchList a destination's hotels with prices, ratings and filters. | 3 per page | |
compare_ota_ratesPrice one stay on Booking.com, Hotels.com, Agoda and Expedia. | 20 | |
official_site_ratesRooms and rate plans from the hotel's own booking engine. | 4 | |
expedia_find_hotelTurn a hotel name into Expedia's property id. | 1 | |
expedia_ratesExpedia's rooms and rate plans for one stay. | 8 | |
flight_searchGoogle Flights itineraries, prices and price insights. | 3 | |
flight_next_legReturn (or next multi-city) options for a chosen outbound. | 3 | |
flight_booking_optionsWho sells a complete itinerary, for how much. | 3 | |
flight_location_searchTurn a city or airport name into the codes flight search takes. | 1 | |
flight_price_calendarCheapest fare for each date around a chosen date. | 5 | |
airbnb_searchAirbnb listings with live prices. | 8 | |
airbnb_priced_calendarDay-by-day availability with Airbnb's own quoted stay prices. | 8 + 1 per priced night | |
vrbo_searchVrbo rentals with stay prices. | 1 | |
vrbo_quoteQuote one Vrbo rental, by property id or URL listing id. | 8 | |
credit_balanceYour plan, credits remaining and billing period. | Free |
A typical hotel conversation: resolve_hotel with a name and city returns a token; hotel_rate_calendar with that token and days: 60 returns 60 nightly prices; then hotel_offers for the cheapest night returns each booking site's price. For flights: flight_search with a return date, flight_next_leg with the chosen outbound's departure_token, then flight_booking_options with the return's booking_token.
Results
A successful call returns the route's JSON twice: in structuredContent, and as text in content[0].text for clients that only read text. Per-row provenance blocks are left out to save the agent's context; call the REST endpoint when you need them for an audit.
_meta on every result gives the HTTP status of the wrapped call, the request id and, when the call was metered, the credits charged and remaining (as strings, copied from the credit headers):
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [{ "type": "text", "text": "{\"token\":\"ChUIoben2Mv6-CYaCi9tLzA3czVwbjQQAQ\",\"requested_days\":60,\"coverage\":0.95,\"rates\":[...]}" }],
"structuredContent": { "token": "ChUIoben2Mv6-CYaCi9tLzA3czVwbjQQAQ", "requested_days": 60, "coverage": 0.95, "rates": ["..."] },
"isError": false,
"_meta": { "http_status": 200, "credits_charged": "5", "credits_remaining": "995", "request_id": "req_..." }
}
}Errors
Errors come back at two levels.
Tool errors are results with isError: true, so the model can read them and fix its next call. The text is {"error": ..., "status": ...} with the wrapped route's detail and status. They cover:
- a missing or unknown argument (the message lists the allowed ones);
- anything the route rejects, such as a date in the past (422) or a property Google no longer lists (404);
- a source that failed or blocked the request (502) or changed its contract (503);
- no credits left (402, once enforcement is on) or the key's rate limit (429).
An unexpected server error is also a tool error, with a generic message and the request_id to quote to support. Tool errors are not charged.
{
"jsonrpc": "2.0",
"id": 8,
"result": {
"content": [{ "type": "text", "text": "{\"error\": \"check_in is in the past\", \"status\": 422}" }],
"isError": true,
"_meta": { "http_status": 422, "request_id": "req_..." }
}
}Protocol errors are JSON-RPC errors, for problems with the message itself:
| Code | When |
|---|---|
-32700 | The body is not valid JSON (HTTP 400). |
-32600 | Not a valid JSON-RPC 2.0 request; an unsupported MCP-Protocol-Version (HTTP 400); a browser Origin that is not allowed (HTTP 403); a body over 256 KB (HTTP 413). |
-32601 | Unknown method. The server implements initialize, ping, tools/list and tools/call. |
-32602 | Unknown tool, or params / arguments that are not objects. |
-32000 | Rate limit exceeded (HTTP 429 with Retry-After: 60 for a single message). |
A missing or invalid key is an HTTP 401 before any JSON-RPC handling.
Limits
- One JSON-RPC message per POST under 2025-06-18, which removed batching. 2025-03-26 clients may batch up to 10 messages.
- Request bodies over 256 KB get 413.
- Tool calls count against your key's rate limit through the route they wrap. Every other message (
initialize,tools/list,ping), and any tool call rejected before it reaches a route, counts once. - A browser
Originmust be scrapercompany.com (or a subdomain) or localhost; anything else gets 403, which guards against DNS rebinding. Agents and CLIs send noOrigin. GETandDELETEon/mcpreturn 405: the server never opens a stream or a session.