# Search

Find documents in your business's files by their words and by their meaning. Words match inside compounds too, so "Stuhl" finds "Bürostuhl". Meaning crosses languages, so "office chairs" finds "Bürostühle". Search finds documents. It answers no questions and changes nothing.

## Search

Call `GET /public/v1/search` with your words in `q`. The key needs the scope `search:read`.

```bash
curl -sS -G https://api.ocost.io/public/v1/search \
  -H "Authorization: Bearer $OCOST_KEY" \
  --data-urlencode "q=Wartungsvertrag Aufzug" \
  --data-urlencode "kind=contract"
```

```python
found = requests.get(
    f"{API}/search",
    headers=auth,
    params={"q": "Bürostuhl", "party": "Musterbüro Lieferant", "limit": 10},
    timeout=15,
).json()
for r in found["results"]:
    first = r["passages"][0] if r["passages"] else None
    print(r["number"], r["seller"], r["issue_date"], first and first["page"])
```

```javascript
const params = new URLSearchParams({ q: 'Skonto', kind: 'invoice' });
const found = await fetch(`${API}/search?${params}`, { headers: auth }).then((r) => r.json());
for (const r of found.results) console.log(r.number, r.file_url);
```

The answer lists one result per document, best first:

```json
{
  "results": [
    {
      "document_id": "01a1b08c-7aa1-7b52-9d4e-2f1f1c0c4f11",
      "file_id": "01a1b08c-6e3a-7562-8f4a-7924722667b1",
      "file_name": "invoice.pdf",
      "kind": "invoice",
      "number": "RE-2026-1187",
      "seller": "Musterbüro Lieferant GmbH",
      "buyer": "Beispiel Handel GmbH",
      "issue_date": "2026-11-03",
      "gross": "1270.92",
      "currency": "EUR",
      "pages": [1],
      "passages": [
        { "page": 1, "text": "4 Bürostuhl ErgoPlus schwarz à 189,00 €", "highlights": [[2, 11]], "found_by": "both" }
      ],
      "facts": [],
      "file_url": "/public/v1/files/01a1b08c-6e3a-7562-8f4a-7924722667b1",
      "document_url": "/public/v1/documents/01a1b08c-7aa1-7b52-9d4e-2f1f1c0c4f11"
    }
  ],
  "total": 1,
  "truncated": false,
  "meaning": "available"
}
```

## Narrow it down

Every parameter but `q` is optional. Repeat `kind` for several kinds.

| Parameter | Meaning |
| --- | --- |
| `q` | Required, 1 to 200 characters. |
| `match` | `hybrid` uses words and meaning, and is the default. `keywords` uses words only, `meaning` meaning only. `exact` finds only documents holding `q` as written, ignoring case, accents and spacing. |
| `kind` | A kind such as `invoice`, `credit_note`, `delivery_note` or `contract`. |
| `since`, `until` | The issue date, ISO 8601, inclusive. |
| `due_since`, `due_until` | An invoice's due date, ISO 8601, inclusive. |
| `party` | Any party's name as read. Case, umlauts and legal forms don't matter, so "Müller Sanitär GmbH" finds "MUELLER SANITAER G.m.b.H.". |
| `vat_id` | A party's VAT ID as read, ignoring spaces and case. |
| `gross_min`, `gross_max` | The gross in your business's currency, converted at the ECB's rate, inclusive. |
| `direction` | `incoming` from suppliers, or `outgoing` to customers. |
| `status` | An invoice's: `open` (unpaid) or `paid`. |
| `category` | An ECLASS code of any level, eight digits such as `24000000`. It finds documents with a line in that class or under it. |
| `limit`, `offset` | `limit` 1 to 50, 20 by default. `offset` up to 250. |

## Read the results

Each result names its document and file, with the fields shown above. A file without a document comes as itself, with `document_id` set to `null`.

- `passages` holds up to five passages of about 300 characters each, best first.
- `highlights` marks the matched words in a passage's `text` as `[start, end)` ranges.
- `found_by` says whether a passage matched by `words`, by `meaning` or by `both`.
- `facts` lists the facts on a document's card that hold your words.
- `file_url` and `document_url` lead to the file and the document in this API.

`total` counts the matches. Ocost ranks the best 300, and `truncated` says there were more. Deleted files never show up.

When `meaning` is `unavailable`, search by meaning is building or unreachable. The words still answer.
