> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rankraft.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> rankraft-py — the official Python client for the RankRaft AI API.

[rankraft-py on PyPI](https://pypi.org/project/rankraft-py/)

## Install

```bash pip theme={"system"}
pip install rankraft-py
pip install 'rankraft-py[pandas]'   # adds .to_pandas()
```

```bash uv theme={"system"}
uv add rankraft-py
uv add 'rankraft-py[pandas]'
```

```bash poetry theme={"system"}
poetry add rankraft-py
poetry add 'rankraft-py[pandas]'
```

Requires Python 3.10+. One runtime dependency (`httpx`); pandas is optional. The package installs as `rankraft-py` and imports as `rankraft`.

## Quick start

```python theme={"system"}
from datetime import date
from rankraft import RankRaft, F

with RankRaft() as rr:                       # reads RANKRAFT_API_KEY
    site = rr.sites.list()[0]

    page = rr.reports.visibility(
        site_id=site["id"],
        start_date=date(2026, 9, 1),
        end_date=date(2026, 9, 30),
        group_by=["platform"],
        metrics=["visibility_score", "mention_rate", "citation_rate"],
        filter=F("platform").in_(["chatgpt", "ai_mode"]) & ~F("category").is_("Brand"),
    )

    print(page.info["total_results"])
    df = page.to_pandas()
```

Every parameter is keyword-only, and names match the [API reference](/api-reference/introduction) (`site_id`, `start_date`, `group_by`). Dates can be strings (`"2026-09-01"`) or `datetime.date`.

## Methods

| Method | Endpoint |
| - | - |
| `rr.me()` | [Get key details](/api-reference/endpoints/get-me) — a good connection test |
| `rr.sites.list()` · `rr.sites.get(site_id=…)` | [List sites](/api-reference/endpoints/list-sites) · [Get a site](/api-reference/endpoints/get-site) |
| `rr.sites.competitors(site_id=…)` | [List competitors](/api-reference/endpoints/list-competitors) |
| `rr.sites.actions(site_id=…, status="open")` | [List actions](/api-reference/endpoints/list-actions) |
| `rr.platforms.list()` | [List answer engines](/api-reference/endpoints/list-platforms) |
| `rr.reports.visibility()` · `.citations()` · `.sentiment()` | [Visibility](/api-reference/endpoints/query-visibility), [citations](/api-reference/endpoints/query-citations), [sentiment](/api-reference/endpoints/query-sentiment) |
| `rr.answers.query()` | [List answers](/api-reference/endpoints/list-answers) |
| `rr.reports.bots()` · `.referrals()` | [AI crawler](/api-reference/endpoints/query-bots) and [AI referral](/api-reference/endpoints/query-referrals) reports |
| `rr.prompts.list()` · `.create()` · `.update()` · `.set_status()` | [Prompts](/api-reference/endpoints/list-prompts) |
| `rr.openapi()` | [OpenAPI specification](/api-reference/endpoints/get-open-api) |
| `rr.request()` | Any route the client does not model yet |

## Pagination

```python theme={"system"}
page = rr.reports.citations(site_id=site_id, start_date="2026-09-01", end_date="2026-09-30")

for row in page:                     # this page only
    ...

for row in page.auto_paging():       # every row, every page
    warehouse.write(row)

for p in page.iter_pages():          # a page at a time
    ...

rows = page.all()                    # collect everything
df = page.to_pandas(all_pages=True)
```

Iterating a page stays on that page; the methods that fetch more pages say so in their name, so a notebook cell never makes surprise requests. The client re-sends your exact query with each cursor, so pages always line up.

### `to_pandas()`

Returns a DataFrame with UTC datetimes, numeric metric columns and categorical dimensions — the types you would otherwise fix by hand after `pd.DataFrame(page.data)`. Needs the `pandas` extra.

## Filters

Pass a plain dict, or build one with `&`, `|` and `~`:

```python theme={"system"}
from rankraft import F

F("platform").in_(["chatgpt", "ai_mode"]) & ~F("category").is_("Brand")
F("prompt").contains("pricing") | F("bot_type").not_is("index")
```

Operators: `is_`, `not_is`, `in_`, `not_in`, `contains`, `not_contains`, all case-insensitive. Numbers and booleans are converted to strings for you (`F("verified").is_(True)`). Which fields each report accepts: [Filtering](/api-reference/filtering).

## Errors

```python theme={"system"}
from rankraft import RateLimitError, ValidationError, PlanRequiredError

try:
    rr.reports.visibility(...)
except RateLimitError as err:
    print(err.retry_after_seconds)
except ValidationError as err:
    print(err.issues)
except PlanRequiredError:
    print("needs an Enterprise plan")
```

Every error carries `status`, `code`, `details` and `request_id` — include the request id when you contact support. The classes match the JavaScript SDK: `BadRequestError`, `InvalidCursorError`, `AuthenticationError`, `PlanRequiredError`, `PermissionDeniedError`, `NotFoundError`, `ConflictError`, `PayloadTooLargeError`, `ValidationError`, `PromptLimitError` (`err.remaining`, `err.limit`), `RateLimitError` (`err.retry_after_seconds`) and `InternalServerError`. See [Errors](/api-reference/errors) for every code.

## Retries

On by default, with two retries after the first attempt. A `429` is retried after exactly the `Retry-After` time. Other errors are retried only for operations that are safe to repeat. `max_retry_wait` caps the total time one call may spend waiting.

```python theme={"system"}
rr = RankRaft(max_retries=2, max_retry_wait=70.0, on_retry=lambda info: log.warning(info))
```

## Managing prompts

Needs a key with **Read and manage prompts** access. Prompts can be created, re-categorised and switched on or off, but never deleted, so their history is always kept.

```python theme={"system"}
result = rr.prompts.create(
    site_id=site_id,
    prompts=[{"text": "best mixed pickle brand", "category": "Discovery"}],
    dry_run=True,    # preview, change nothing
)
result["created"], result["existing"]

rr.prompts.set_status(site_id=site_id, prompt_ids=["…"], status="disabled")
```

## Options

| Option | Default |
| - | - |
| `api_key` | `RANKRAFT_API_KEY` |
| `base_url` | `RANKRAFT_BASE_URL` or `https://api.rankraft.ai/v1` |
| `timeout` | `60.0` seconds per attempt |
| `max_retries` | `2` |
| `max_retry_wait` | `70.0` seconds total per call |
| `http_client` / `transport` | your own `httpx` client or transport |
| `default_headers` | `None` |
| `on_retry` | — |

Use `with RankRaft() as rr:` (or call `rr.close()`) so connections are released. The client is synchronous; use it on a server or in a notebook, never in code a browser can read.

## Package

[rankraft-py on PyPI](https://pypi.org/project/rankraft-py/) · MIT licence


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.