# Search books

Full-text search over editions. Use this to **browse**. To identify a known title, use [match](/docs/match) instead.

MCP: `search_books` `{ "q": "Project Hail Mary", "limit": 5 }`

## When to use

- Filtered exploration (`year`, `language`, `subject`)
- “Show me books like…”

Do **not** search an ISBN. Do **not** use search as the first call when the user named one book — [match](/docs/match) ranks and collapses better for that.

## Request

```bash
curl -sS -H "Authorization: Bearer $FDS_API_KEY" \
  "https://api.fulldatasets.com/v1/books/search?q=Project%20Hail%20Mary&limit=5"
```

| Query | Required | Notes |
|---|---|---|
| `q` | one of `q` or `genre` | Title-ish query. Do not put a genre name or an author-only string here |
| `author` | no | Rank filter on names. Not AND’d into title FTS |
| `year` | no | `publication_year` |
| `language` | no | e.g. `eng` |
| `subject` | no | Exact raw OL tag (`Magicians -- Fiction`) |
| `genre` | no | Closed facet: `fantasy`, `science_fiction`, `mystery`, `romance`, … |
| `kind` | no | `primary` \| `graphic` \| `derivative` \| `audio` \| `set` \| `any`. Default prefers primary |
| `limit` | no | Default `20`, max `100` |
| `cursor` | no | Pagination from `next_cursor` |

## Response `200`

```json
{
  "results": [{ "id": "fds_ed_…", "title": "Project Hail Mary" }],
  "next_cursor": "fds_ed_…"
}
```

Each item is a [book object](/docs/data-model). `next_cursor` is `null` when there is no next page.

## Ranking

1. Folded `title_key` / `title_normalized` variants first (articles, apostrophes, subtitles)
2. Open Library shelf/rating popularity among those hits (genre browse uses this first)
3. Title-only `websearch_to_tsquery` fallback — author tokens are not AND’d into FTS
4. Prefer `primary` (novel/text). Graphic novels, companions, and study guides are demoted unless `kind=graphic` or the query itself looks graphic
5. One canonical edition per work

Browse genre with `genre=fantasy` (no `q`). That matches edition subjects, not a `w.genres` column. `q=epic fantasy` still searches titles and will look like keyword spam.

Author-only: [`GET /v1/authors/search`](/docs/authors). “Books like these”: [recommend](/docs/recommend).

Search is short-TTL and hits Postgres (not the 24h point-lookup cache).

## Errors

| Status | `code` | When |
|---|---|---|
| `400` | `bad_request` | Missing both `q` and `genre` |

## See also

- [Match](/docs/match) — resolve *the* book
- [Authors](/docs/authors) — name search
- [Recommend](/docs/recommend) — content-similar works
- [ISBN](/docs/isbn) — exact product
