Metadata-Version: 2.4
Name: securonix-spotter-client
Version: 1.2.1
Summary: Python client for the Securonix Spotter API (shared gateway search workflow)
Author: Securonix Spotter client contributors
Project-URL: Documentation, https://documentation.securonix.com/r/content/spotter-api.htm
Project-URL: Homepage, https://github.com/williamgomez71/SecuronixAPI
Project-URL: Repository, https://github.com/williamgomez71/SecuronixAPI
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.31.0
Requires-Dist: python-dotenv>=1.0.0

# Securonix Spotter API client

Python client for **[Securonix Spotter](https://documentation.securonix.com/r/content/spotter-api.htm)** search via the shared **snypr-service-gateway**: authenticate, submit async queries, poll status, and fetch results as JSON.

Official Spotter API documentation: [Securonix Documentation — Spotter API](https://documentation.securonix.com/r/content/spotter-api.htm).

**Source code:** [github.com/williamgomez71/SecuronixAPI](https://github.com/williamgomez71/SecuronixAPI)

End-to-end flow implemented by this library:

1. `GET /ws/token/generate` for the WS session token.
2. `POST /api/v2/oauth/token` with `wstoken` for the OAuth Bearer (JWT in practice).
3. `POST /spotter-api/spotter/api/v1/search/queries`.
4. Poll `GET .../status` until `COMPLETED`.
5. `GET .../results` and return structured JSON.

The sequence matches the working examples in the [SecuronixAPI](https://github.com/williamgomez71/SecuronixAPI) repository (`examples/`).

## Install

From [PyPI](https://pypi.org/) (distribution name **`securonix-spotter-client`**):

```bash
pip install securonix-spotter-client
```

From a [clone of the GitHub repository](https://github.com/williamgomez71/SecuronixAPI):

```bash
git clone https://github.com/williamgomez71/SecuronixAPI.git
cd SecuronixAPI
pip install .
```

Import the **`securonix_spotter`** package in Python:

```python
from securonix_spotter import SpotterClient
```

## Environment variables

- `TENANT_BASE_URL` (e.g. `https://<tenant>.securonix.net/Snypr`)
- `TENANT_USERNAME`
- `TENANT_PASSWORD`

A `.env` file is loaded automatically via `python-dotenv` (without overwriting variables already set in the shell).

If you used older names (`SPOTTER_BASE_URL`, `SPOTTER_USERNAME`, `SPOTTER_PASSWORD`), rename those keys in `.env` to the `TENANT_*` names above.

## Default gateway URL

The URL most tenants use:

- `https://us-east1-prod01.securonix.net/shared/snypr-service-gateway`

If you omit `gateway_url` when constructing `SpotterClient`, that base URL is used by default.

## Default time range (when you omit `from_time` / `to_time`)

The Spotter gateway rejects `fromTime=0` and `toTime=0` (error `1005`: invalid time range). The client therefore **always** sends a valid window.

| What you pass | Effective window |
|----------------|------------------|
| **Both omitted or both `0`** (default) | **Last 24 hours in UTC**: from `(now UTC − 24h)` to `now UTC`, as epoch **milliseconds**. |
| **`from_time` only** (`> 0`, `to_time` ≤ 0) | From your `from_time` **to now** (UTC, ms). |
| **`to_time` only** (`from_time` ≤ 0, `to_time` > 0) | **24 hours before** `to_time` **up to** `to_time` (UTC, ms). |
| **Both set** | Your exact range (must satisfy `from_time < to_time`). |

So if you call `execute(query)` with no time arguments, you are querying the **past 24 hours**, not “all time”.

`from_time` and `to_time` are always **Unix epoch in milliseconds** in **UTC**.

## Quick start

```python
from securonix_spotter import SpotterClient

client = SpotterClient()
query = "index = violation | stats count by tenantname policyname criticality"
results = client.execute(query)
print(results)
```

## Example: explicit time range (`from_time` / `to_time`)

```python
from datetime import datetime, timedelta, timezone

from securonix_spotter import SpotterClient

client = SpotterClient()
query = "index = violation | stats count by tenantname policyname criticality"

# Last 2 hours
end = datetime.now(timezone.utc)
start = end - timedelta(hours=2)
from_time = int(start.timestamp() * 1000)
to_time = int(end.timestamp() * 1000)

results = client.execute(query, from_time=from_time, to_time=to_time)
print(results)
```

## Example: fixed UTC window

```python
from datetime import datetime, timezone

from securonix_spotter import SpotterClient

client = SpotterClient()
query = "index = violation | stats count by tenantname policyname criticality"

from_time = int(datetime(2026, 4, 28, 0, 0, 0, tzinfo=timezone.utc).timestamp() * 1000)
to_time = int(datetime(2026, 4, 28, 10, 0, 0, tzinfo=timezone.utc).timestamp() * 1000)

results = client.execute(
    query,
    from_time=from_time,
    to_time=to_time,
    poll_interval=3,
    timeout=420,
)
print(results)
```

## Example: explicit gateway + error handling

```python
from securonix_spotter import SpotterAuthError, SpotterClient, SpotterTimeoutError

gateway = "https://us-east1-prod01.securonix.net/shared/snypr-service-gateway"
client = SpotterClient(gateway_url=gateway)

try:
    data = client.execute(
        "index = violation | stats count by tenantname policyname criticality",
        poll_interval=3,
        timeout=420,
    )
    print(data)
except SpotterAuthError as exc:
    print(f"Authentication error: {exc}")
except SpotterTimeoutError as exc:
    print(f"Polling timeout: {exc}")
```

## Example: step-by-step (`run_query` → poll → `get_results`)

```python
from securonix_spotter import SpotterClient

client = SpotterClient()
query = (
    'index = violation and policyname = "possible brute force attack detected - csso" '
    "| STATS sourceaddress destinationaddress accountname destinationport"
)

query_id = client.run_query(
    query,
    limit=5000,
    sort_by="sourceaddress",
    sort_order="desc",
    timeout=600,
)
client.wait_for_results(query_id, poll_interval=2, timeout=600)
results = client.get_results(query_id, offset=0, limit=500)
print(results)
```

## Scripts in `examples/`

- `examples/basic_query.py`
- `examples/advanced_query.py`
