# Search

> Search your CRM by meaning, and find the people whose history suggests interest in a listing, through the Fondaro API.

Two routes find people by what is in their history, not by a field. Both need `crm:read`, neither needs an active plan, and both read notes, call transcripts and summaries, human-written emails and the assistant's lead memory.

A member's search covers the leads assigned to them and leads shared with them by a collaborating agency; an admin's covers the whole agency. Results are ranked people with short evidence snippets. An empty `matches` array means no relevant history was found.

## Stay within the search rate limit

These two routes cost more to run than a plain read, so they share one limit: **30 requests a minute per API key, and 30 a minute for your whole agency** across all its keys. Over it, the API answers `429` with `RATE_LIMITED`, a `Retry-After` header and the message "Semantic search rate limit exceeded". See [Errors and limits](https://www.fondaro.com/docs/api/errors-and-limits.md) for the headers.

## Search the CRM by meaning

`POST /v1/search`

Finds people whose history matches a question or phrase. Date filters refer to when the source activity happened, not when it was stored.

| Parameter | In | Type | Required | Description |
|-----------|----|------|----------|-------------|
| `query` | Body | string | Yes | 1 to 500 characters |
| `leadId` | Body | integer | No | Search the history of one lead only |
| `maxResults` | Body | integer | No | 1 to 30 |
| `occurredFrom` | Body | string | No | Only activity on or after this date |
| `occurredTo` | Body | string | No | Only activity on or before this date |

```bash
curl -X POST https://api.fondaro.com/v1/search \
  -H "Authorization: Bearer $FONDARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "wants a sea view and a pool", "maxResults": 5 }'
```

```json
{ "matches": [], "tookMs": 1812, "reranked": true }
```

A match carries `leadId`, `matchCount` (relevant passages), `sourceCount` (distinct notes, calls or emails), `bestMatchAt` and `evidence`, a list of `{ "snippet": "..." }`. `reranked` is `false` when the first order was served without reranking. Errors: `400` for an empty or over-long `query`; `429` over the limit.

## Match people to a listing

`POST /v1/listing-matches`

Finds people whose history suggests interest in a listing. Name one of your own listings with `propertyListingId`, or a listing from a source with `source` and `listingId`.

A listing from a property portal (`idealista`, `rightmove` and the other portals property search lists) counts toward your agency's daily property site allowance and can answer `429` with `DAILY_LIMIT_REACHED` once that is used up. Your own listings (`internal`) and the feeds your agency connects (`resales_online`, `zoddak`, `inmobalia`) do not use the allowance.

| Parameter | In | Type | Required | Description |
|-----------|----|------|----------|-------------|
| `propertyListingId` | Body | UUID | One form | One of your own listings |
| `source` | Body | string | One form | A source id, with `listingId` |
| `listingId` | Body | string | One form | The listing's id at that source |
| `country` | Body | string | No | The listing's country, when its source needs one |
| `maxResults` | Body | integer | No | 1 to 30 |

```bash
curl -X POST https://api.fondaro.com/v1/listing-matches \
  -H "Authorization: Bearer $FONDARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "propertyListingId": "3f6c2a10-0000-4000-8000-000000000002" }'
```

```json
{
  "matches": [],
  "tookMs": 936,
  "reranked": true,
  "listingQuery": "Penthouse for sale in Marbella, Málaga. Around 550k EUR. 3 bedrooms, 2 bathrooms. Private pool, garden, terrace, garage. ..."
}
```

`listingQuery` is the text the listing became, so you can judge a match. The matches have the same shape as in the search above. Errors: `429` over the shared limit or the daily allowance.

Source: https://www.fondaro.com/docs/api/v1/search
