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

# Core concepts

> Understand the client, resources, request and response types, pagination, Beta APIs, retries, and other building blocks of the HiddenLayer Python SDK.

This page explains the ideas you use in every HiddenLayer Python SDK program. Read it once before you start the workflow guides. Each section links to a page with more detail.

## The client

The **client** is the object that holds your configuration (credentials, region, timeouts, retries) and sends requests to the HiddenLayer API. The SDK provides two clients with the same constructor arguments and the same methods:

| Client | Import | Use it when |
| - | - | - |
| `HiddenLayer` | `from hiddenlayer import HiddenLayer` | You write ordinary (synchronous) Python: scripts, CI jobs, Flask or Django apps, notebooks. |
| `AsyncHiddenLayer` | `from hiddenlayer import AsyncHiddenLayer` | Your application already uses `asyncio` (for example FastAPI), or you need to send many requests concurrently. |

`Client` and `AsyncClient` are aliases for the same classes. With the async client, you `await` every method call.

```python Sync theme={null}
from hiddenlayer import HiddenLayer

# Reads HIDDENLAYER_CLIENT_ID and HIDDENLAYER_CLIENT_SECRET from the environment.
with HiddenLayer() as client:
    page = client.scans.jobs.list(limit=5)
    print(page.total)
```

```python Async theme={null}
import asyncio

from hiddenlayer import AsyncHiddenLayer


async def main() -> None:
    # Reads HIDDENLAYER_CLIENT_ID and HIDDENLAYER_CLIENT_SECRET from the environment.
    async with AsyncHiddenLayer() as client:
        page = await client.scans.jobs.list(limit=5)
        print(page.total)


asyncio.run(main())
```

### Create one client and reuse it

Create the client once (for example at application startup) and share it. Reusing a client matters for two reasons:

* **Connection reuse.** The client keeps a pool of HTTP connections open, so later requests are faster.
* **Token caching.** With client ID and client secret authentication, the client requests an access token once, caches it, and refreshes it shortly before it expires. The cache is protected by a lock, so a single client can be shared safely across threads. A new client per request would fetch a new token every time.

Close the client when you are done, either with a `with` block (as shown above) or by calling `client.close()` (`await client.close()` for the async client). If you do neither, connections close when the client is garbage collected.

## Resources and methods

The client groups API operations into **resources**. You call an operation as `client.<resource>.<method>()`. Some resources are nested, such as `client.scans.jobs.list()`.

| Resource | Product area | Status |
| - | - | - |
| `client.model_scanner` | AI Supply Chain Security: scan local files, folders, S3, Azure Blob, Hugging Face | High-level helper |
| `client.community_scanner` | AI Supply Chain Security: scan models at a remote location | High-level helper |
| `client.scans.jobs` | AI Supply Chain Security: list, retrieve, and request scans | Generally available |
| `client.scans.results` | AI Supply Chain Security: scan summary, paginated file results, SARIF export | Generally available |
| `client.scans.upload`, `client.scans.upload.file` | AI Supply Chain Security: multipart upload steps | Generally available |
| `client.models` | AI Supply Chain Security: retrieve and delete models | Generally available |
| `client.models.cards` | Model cards listing | **Deprecated** |
| `client.interactions` | AI Runtime Security: Interactions (analyze LLM input and output) | Generally available |
| `client.runtime` | AI Runtime Security: agentic evaluation endpoints | **Beta** |
| `client.prompt_analyzer` | Prompt Analyzer | Generally available |
| `client.evaluations.red_team` | AI Attack Simulation: low-level red team endpoints | **Beta** |
| `client.evaluation_sessions.red_team` | AI Attack Simulation: session helper with polling and callbacks | **Beta** (wraps Beta endpoints) |
| `client.sensors` | Sensors | **Deprecated** (the SDK docstring says the endpoint "will be removed after April 13, 2026") |

See the [Python API reference](/docs/integrations/sdk_python/api_reference) for every method signature.

## High-level helpers and low-level resources

Most resources map one-to-one to a REST endpoint. These are **low-level resources**: one method call sends one HTTP request.

Three attributes are **high-level helpers** written by HiddenLayer on top of the low-level resources:

* `client.model_scanner` uploads model files in parts, starts the scan, polls until it finishes, and collects all file results into one `ScanReport`.
* `client.community_scanner` asks HiddenLayer to fetch and scan a model from a remote location, then optionally waits for results.
* `client.evaluation_sessions.red_team` starts a red team workflow and drives it with polling, iteration, or a callback function.

Start with the helpers for common tasks. Use low-level resources when you need control the helpers do not offer, such as listing past scans or exporting SARIF. The [API usage examples](/docs/integrations/sdk_python/api_examples) page shows low-level recipes.

## Environments and base URL

An **environment** is the HiddenLayer region your account lives in. The client turns the environment into a **base URL**, the address every request starts with.

| Environment | Base URL |
| - | - |
| `prod-us` (default) | `https://api.us.hiddenlayer.ai` |
| `prod-eu` | `https://api.eu.hiddenlayer.ai` |

The client chooses the base URL in this order:

1. The `base_url` constructor argument.
2. The `environment` constructor argument.
3. The `HIDDENLAYER_BASE_URL` environment variable.
4. `prod-us`.

If you pass `environment` while `HIDDENLAYER_BASE_URL` is also set, the constructor raises a `ValueError` ("Ambiguous URL"). Set only one of them. Use `base_url` for a self-hosted endpoint, such as a local AI Runtime Security container.

Credentials are region specific, because the token request goes to the same base URL. Use credentials created in the same region you connect to.

## Authentication

The client supports two authentication methods:

* **OAuth2 client credentials** (recommended). OAuth2 client credentials is a standard flow where your program exchanges a client ID and client secret for a short-lived access token. Set `HIDDENLAYER_CLIENT_ID` and `HIDDENLAYER_CLIENT_SECRET`. The client fetches, caches, and refreshes the token for you.
* **Bearer token.** Set `HIDDENLAYER_TOKEN` to an access token you obtained elsewhere. The client sends it as-is and does not refresh it.

Configure only one method. See [Authentication and configuration](/docs/integrations/sdk_python/authentication) for setup steps.

## Request parameters are TypedDicts

Method arguments that contain nested data, such as `metadata` or `input` for `client.interactions.analyze()`, are typed as **TypedDicts**. A TypedDict is a normal Python `dict` with a declared set of keys and value types. Because of this:

* You pass plain dictionaries. You do not need to import or construct special classes.
* Editors and type checkers (such as Pyright or mypy) can autocomplete keys and flag typos.

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer()

response = client.interactions.analyze(
    metadata={"model": "gpt-4o", "requester_id": "user-1234"},
    input={"messages": [{"role": "user", "content": "Hello"}]},
)
```

## Responses are Pydantic models

Methods return **Pydantic models**. Pydantic is a data validation library. A Pydantic model is a Python object whose attributes are parsed from JSON and have declared types. You read values with attribute access, for example `report.summary.highest_severity`.

Useful helpers on every response model:

| Helper | What it does |
| - | - |
| `.to_dict()` | Converts the model to a Python dictionary. |
| `.to_json()` | Converts the model to an indented JSON string. Useful for logging or saving results. |
| `.model_fields_set` | The set of fields present in the API response. Use it to tell whether a `None` value was `null` in the JSON or missing entirely. |
| `.model_extra` | A dictionary of fields the API returned that this SDK version does not define yet. |

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer()

summary = client.scans.results.retrieve_summary("YOUR_SCAN_ID")
print(summary.status)
print(summary.to_json())

if summary.end_time is None and "end_time" not in summary.model_fields_set:
    print("The API did not return end_time.")
```

Response types live under `hiddenlayer.types`, for example `from hiddenlayer.types import InteractionAnalyzeResponse` or `from hiddenlayer.types.scans import ScanReport`. Import them for type hints.

A few endpoints return plain Python types instead of models. For example, `client.scans.results.sarif()` returns a `str`, and `client.runtime.evaluate_request()` returns a `dict`.

## Projects and the `hl_project_id` parameter

In AI Runtime Security, a **project** groups your application's traffic and selects the policy used to evaluate it. Runtime methods (`interactions.analyze`, `runtime.evaluate_*`, `prompt_analyzer.create`) and `evaluations.red_team.create` accept an optional `hl_project_id` argument. The SDK sends it as the `HL-Project-Id` HTTP header. If you omit it, the API applies its default behavior.

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer()

response = client.interactions.analyze(
    metadata={"model": "gpt-4o", "requester_id": "user-1234"},
    input={"messages": [{"role": "user", "content": "Hello"}]},
    hl_project_id="YOUR_PROJECT_ID",
)
```

The red team session helper uses the argument name `hiddenlayer_project_id` instead. See [Runtime security](/docs/integrations/sdk_python/runtime_security) and the [policy documentation](/docs/products/runtime/agentic/policy).

## Beta APIs and `BetaWarning`

Some endpoints are **Beta**: they are not generally available and can change without notice, including breaking changes. Beta functions and types are excluded from the SDK's semantic versioning guarantees. See the [Beta API guidelines](https://dev.hiddenlayer.ai/docs/beta-api-faq).

When you call a Beta endpoint, the SDK emits a `hiddenlayer.BetaWarning` (a subclass of Python's `UserWarning`) through the standard `warnings` module. In SDK version 3.10.0 this applies to:

* `client.runtime.evaluate_interaction`, `evaluate_request`, and `evaluate_response`
* All `client.evaluations.red_team` methods, including calls made by `client.evaluation_sessions.red_team`

> [!NOTE]
>
> The 3.9.0 changelog says the Beta label was removed from the runtime v2 endpoints, but version 3.10.0 still emits `BetaWarning` for them. This discrepancy needs confirmation.

After you have reviewed the Beta terms, you can silence the warning:

```python theme={null}
import warnings

import hiddenlayer

warnings.filterwarnings("ignore", category=hiddenlayer.BetaWarning)
```

## Pagination

Endpoints that return many items use one of three patterns. Each one needs different code, so check the return type in the [API reference](/docs/integrations/sdk_python/api_reference).

| Pattern | Used by | How to page |
| - | - | - |
| Cursor pagination (`SyncCursorPagination` / `AsyncCursorPagination`) | `client.scans.results.list_files()` | Iterate the result with `for` (or `async for`) and the SDK fetches pages automatically. Items of the current page are in `.items`. For manual control, use `.has_next_page()` and `.get_next_page()`. |
| Offset pagination (`SyncOffsetPage` / `AsyncOffsetPage`) | `client.models.cards.list()` (deprecated) | Iterate to auto-paginate. Items of the current page are in `.results`. |
| Plain list with `limit` and `offset` | `client.scans.jobs.list()` | Not auto-paginated. The response has `items`, `limit`, `offset`, and `total`. Increase `offset` yourself until you reach `total`. |

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer()

# Cursor pagination: the loop fetches more pages as needed.
for file_result in client.scans.results.list_files("YOUR_SCAN_ID", page_size=50):
    print(file_result.file_location, file_result.status)
```

See [API usage examples](/docs/integrations/sdk_python/api_examples) for full paging loops.

## Raw and streaming responses

By default, methods return the parsed body only. Two prefixes give you access to the underlying HTTP response:

* `client.<resource>.with_raw_response.<method>(...)` returns an `APIResponse` with `.status_code`, `.headers`, and `.parse()` (which returns the normal parsed object). Use it when you need a response header, such as the `hl-runtime-action` header from the agentic evaluation endpoints.
* `client.<resource>.with_streaming_response.<method>(...)` must be used in a `with` block. It does not read the body until you call a method such as `.iter_bytes()`, `.iter_lines()`, `.json()`, or `.parse()`. Use it for large responses.

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer()

raw = client.scans.results.with_raw_response.retrieve_summary("YOUR_SCAN_ID")
print(raw.status_code, raw.headers.get("content-type"))
summary = raw.parse()
```

## Retries and timeouts

The client retries failed requests automatically so that short outages do not break your program.

| Setting | Default | How to change |
| - | - | - |
| Retries | 2 retries with exponential backoff (starting at 0.5 seconds, capped at 8 seconds). The client honors `retry-after` headers. | `max_retries=` on the client or with `client.with_options()`. `0` disables retries. |
| Retried conditions | Connection errors, timeouts, and HTTP 408, 409, 429, and 5xx responses | Not configurable |
| Timeout | 60 seconds per request, 5 seconds to connect | `timeout=` on the client, with `client.with_options()`, or per method call. Accepts a number of seconds or an `httpx.Timeout`. |

```python theme={null}
from hiddenlayer import HiddenLayer

client = HiddenLayer(max_retries=4, timeout=30.0)

# Override for a single call without changing the shared client.
client.with_options(timeout=120.0, max_retries=0).scans.jobs.list(limit=10)
```

`client.with_options()` returns a new client that shares the original's connection pool, so it is cheap to call.

> [!NOTE]
>
> Retries and timeouts apply to each HTTP request. They do not limit how long `model_scanner` or `community_scanner` wait for a scan when `wait_for_results=True`. Those helpers poll until the scan finishes, with no overall time limit. See [Model scanning](/docs/integrations/sdk_python/model_scanning).

## Logging

The SDK uses Python's standard `logging` module. Set the `HIDDENLAYER_LOG` environment variable to turn it on:

* `info` enables informational messages from the SDK and its HTTP library (`httpx`), such as requests sent and retries.
* `debug` adds detailed request information, including request bodies.

> [!WARNING]
>
> Debug logging can write prompts, model responses, and other sensitive request data to your logs. Do not enable `HIDDENLAYER_LOG=debug` in production or anywhere logs are shared.

## Next steps

* [Model scanning](/docs/integrations/sdk_python/model_scanning)
* [Runtime security](/docs/integrations/sdk_python/runtime_security)
* [Attack simulation](/docs/integrations/sdk_python/attack_simulation)
* [Error handling](/docs/integrations/sdk_python/error_handling)
