Metadata-Version: 2.4
Name: raily-ai
Version: 0.4.0
Summary: Official Python SDK for the Raily search API.
Project-URL: Homepage, https://raily.ai
Project-URL: Documentation, https://docs.raily.ai
Project-URL: Source, https://github.com/Raily-ai/raily-python
Author-email: Raily <support@raily.ai>
License: Apache-2.0
License-File: LICENSE
Keywords: mcp,rag,raily,retrieval,search
Requires-Python: >=3.10
Requires-Dist: fastmcp<4,>=3.0
Description-Content-Type: text/markdown

# Raily Python SDK

Official Python client for the [Raily](https://raily.ai) search API — search your own
sources from your app with a typed, one-call client.

[Documentation](https://docs.raily.ai) · [Source](https://github.com/Raily-ai/raily-python)

## Install

```bash
pip install raily-ai
```

## Quickstart

```python
from raily import Raily

client = Raily(api_key="rly_...", endpoint="https://<your-endpoint-url>")

for hit in client.search("anti-aging serum", limit=5):
    print(hit.score, hit.fields.get("title"))
```

## Async

```python
from raily import AsyncRaily

client = AsyncRaily(api_key="rly_...", endpoint="https://<your-endpoint-url>")
hits = await client.search("anti-aging serum", limit=5)
```

## Authentication

Create an API key in your Raily workspace (**Identities → API keys**) and pass it as
`api_key`. The `endpoint` is your endpoint's URL. A key is scoped to one workspace and
to the endpoint it was issued for — using it elsewhere is rejected.

```python
import os
client = Raily(api_key=os.environ["RAILY_API_KEY"], endpoint=os.environ["RAILY_ENDPOINT"])
```

## Results

`search()` returns a list of `SearchHit`:

| field | type | meaning |
|---|---|---|
| `score` | `float \| None` | relevance score |
| `fields` | `dict[str, str]` | flat `{name: value}` map of the source's display fields, e.g. `hit.fields["title"]` |
| `text` | `str \| None` | a rendered text summary |
| `source_collection` | `str \| None` | the source the hit came from |
| `id` | `str \| None` | stable result id |
| `raw` | `dict` | the full original result, for anything not surfaced above |

`fields` is a flat dict, so you read a value by name:

```python
hit.fields["title"]              # e.g. "Anti-Aging Vitamin C Serum"
hit.fields.get("published_date") # None if this source doesn't expose it
```

The exact keys come from the source's configured display fields (e.g. `title`, `author`,
`published_date`). Print one hit's `fields` to see what your source exposes.

## Debugging empty results

A bare `[]` doesn't say *why*. Pass `explain=True` for the endpoint's message plus a hint:

```python
res = client.search("articles", explain=True)
print(res.info.count, res.info.message, res.info.note)  # note is set only when empty
```

Most empty results are a too-generic query (`"articles"` matches nothing — search a topic),
a freshness filter, or an unindexed corpus. Or construct with `debug=True` to log a hint on
every empty result.

## Errors

```python
from raily import RailyError, RailyAuthError

try:
    client.search("query")
except RailyAuthError:
    ...  # key missing / invalid / expired / not authorized for this endpoint
except RailyError:
    ...  # everything else (network, server, protocol)
```

## License

Apache-2.0
