# VistaLink: search

Brief for a coding agent. Add hotel search: a traveller’s sentence (or filters) in, ranked hotels out.

## Before you write code

- The API key comes from the environment as `VISTALINK_API_KEY`. Never hard-code it, never send it to a browser: call VistaLink from server code only.
- The user creates the key at https://vistalink.com/developers/onboarding. Test keys hit the real catalogue and spend credit; they never create Stripe charges.
- Base URL: https://api.vistalink.com. Every call sends `Authorization: Bearer $VISTALINK_API_KEY`.

## The call: POST /v2/search

Search hotels.

```bash
curl -X POST 'https://api.vistalink.com/v2/search' \
  -H "Authorization: Bearer $VISTALINK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "query": "quiet hotel near the Trevi Fountain with a rooftop",
  "stay": {
    "check_in": "2026-11-14",
    "check_out": "2026-11-16"
  }
}'
```

```typescript
const res = await fetch("https://api.vistalink.com/v2/search", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VISTALINK_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "query": "quiet hotel near the Trevi Fountain with a rooftop",
    "stay": {
      "check_in": "2026-11-14",
      "check_out": "2026-11-16"
    }
  }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const data = await res.json();
```

For the next page, send the same request with the `search_id` you got back; pages of one search are free.

## What comes back

- `hotels[]`: cards with `hotel_id`, name, stars, review score, photos, location and, when you sent dates, a cached `rate` (`status` is `observed`, `stale` or `unknown`).
- `why[]` on each card under semantic ranking: the criterion, the assessment and the evidence (photos or reviews).
- `search_id`: send it back for the next page (free) or to hotel detail (warm).
- `status: needs_clarification` with a question when the query is ambiguous; answering costs nothing.

## Rules that trip people up

- Free text goes in `query`; hard constraints go in `filters`. Where they disagree, filters win and `resolved.overrides` says so.
- `ranking: "semantic"` reads each hotel's photos and reviews; `"filters"` keeps plain filter order and leaves `why[]` empty.
- A `budget` is per room per night and needs a `currency`. A `stay` without `rooms` means one room for two adults.
- Send `check_in` and `check_out` in `stay` if you want prices on the cards.

## Price

- `search.structured.filters`: $0.003 per search
- `search.loose.filters`: $0.005 per search
- `search.structured.semantic`: $0.008 per search
- `search.loose.semantic`: $0.012 per search

## When it fails

- Errors come back as `{ "error": { "code", "message", "request_id", "details" } }`. Log `request_id`; support needs it.
- Retry `rate_limited` (429) after `X-RateLimit-Reset`, and `upstream_error` (502) or `unavailable` (503) with backoff. Don't retry other 4xx: fix the request.
- `insufficient_credit` (402) means the account needs a top-up; `forbidden` (403) with `details.reason: "tier_required"` means the call needs a higher tier.

## Done when

- A real query with dates returns at least one card, and each card shows its `rate.status`.
- A vague query ("a hotel in Cambridge") gets its clarifying question shown to the user, not an error.
- The key appears nowhere in the client bundle (search the build output for `vl_`).

## If you are an agent runtime rather than writing code

Connect the MCP server instead: `claude mcp add --transport http vistalink https://api.vistalink.com/mcp --header "Authorization: Bearer $VISTALINK_API_KEY"` (the shell expands the key into the stored config). Tools for this task: `search_hotels_v2`. The tools ending in `_v2` share a name with a v1 tool (`search_hotels`, `get_rates`, `get_booking`, `chat_about_hotels`), so they carry the suffix. It stays until v1 is retired, so agents built on v1 keep working.

## Full reference

- Docs: https://vistalink.com/developers/docs/search
- OpenAPI: https://api.vistalink.com/v2/openapi.json
- Every brief: https://vistalink.com/developers/agents
