Parloa Python SDK
Official Python client for the Parloa API.
Installation
pip install parloa
Usage
Authenticate with Parloa.token_provider(). It caches OAuth tokens and refreshes them before expiry, so you pass the provider to any client that needs auth. Omit base_url to let the SDK resolve the correct API gateway per API group; pass base_url only if you want every sub-client pinned to one gateway (see Automatic base URL resolution).
from parloa import Parloa
CLIENT_ID = "client_01HZX9YQK0V4N4G9Z8B7Q1T2C3"
CLIENT_SECRET = "client_secret"
AGENT_ID = "agt_01HZX9YQK0V4N4G9Z8B7Q1T2C3"
provider = Parloa.token_provider(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
)
client = Parloa(token=provider)
skills = client.skills.list(agent_id=AGENT_ID)
print(skills.data[0].name)
simulations = client.simulations.list_definitions(agent_id=AGENT_ID)
print(simulations.items[0].name)
Async usage follows the same pattern with AsyncParloa:
import asyncio
from parloa import AsyncParloa
async def main() -> None:
provider = AsyncParloa.token_provider(
client_id="client_01HZX9YQK0V4N4G9Z8B7Q1T2C3",
client_secret="client_secret",
)
client = AsyncParloa(token=provider)
skills = await client.skills.list(agent_id="agt_01HZX9YQK0V4N4G9Z8B7Q1T2C3")
print(skills.data[0].name)
asyncio.run(main())
Automatic base URL resolution
By default, when you omit base_url, the SDK resolves the correct API gateway for each API group before sending a request. You can also force this on with resolve_base_url_automatically=True even when a base_url is set (useful when base_url points at the default API gateway but sub-clients should route to tenant-specific gateways).
# No base_url — resolution is on by default
client = Parloa(token=provider)
agents = client.agents.list()
# Explicit opt-out: pin every sub-client to one gateway
client = Parloa(base_url="https://runtime.example.com", token=provider)
What happens on each request
- You access a sub-client, for example
client.agents. - Before the HTTP request is sent, an httpx event hook looks up your URL map — from an in-memory cache when available, otherwise from the tenant URLs API (
GET /v0/urls). - The hook looks up the tag for that sub-client (e.g.
agents) and rewrites the request host to the tenant URL. - The actual API call goes to the resolved gateway.
Tenant URLs are cached for one hour, so later calls to the same process reuse the map without another /v0/urls round trip.
@startuml
actor User
participant "Parloa" as Client
participant "__getattribute__" as GetAttr
participant "Tenant URLs\n(/v0/urls)" as TenantUrls
participant "Agents API" as Agents
User -> Client : agents.list()
Client -> GetAttr : access client.agents
GetAttr -> Client : build AgentsClient\n(with URL resolver hook)
Client -> TenantUrls : GET /v0/urls\n(cached after first call)
TenantUrls --> Client : {"agents": "https://runtime.example.com", ...}
Client -> Agents : GET /v0/agents\n(host rewritten)
Agents --> Client : response
Client --> User : agents
@enduml
The tenant_urls sub-client is excluded from this rewrite — it must always hit the default API gateway to fetch the map.
Why __getattribute__ is involved
BaseClient is generated by the Fern SDK from the OpenAPI spec. It defines sub-clients like agents and oauth as ordinary @property methods, each sharing a single httpx client and one fixed base_url. We can't change those property implementations directly, so Parloa overrides __getattribute__ to intercept access to registered sub-clients. When you write client.agents:
- Python calls
Parloa.__getattribute__("agents"). - The handler walks the class hierarchy, finds the
agentsproperty onBaseClient, and looks it up in_BASE_CLIENT_PROPERTY_CLIENTS. - It calls
_create_client_wrapper("agents"), which attaches a per-tag URL resolver, then instantiatesAgentsClientwith that wrapper. - The sub-client is cached on
_agentsso this setup runs only once.
Inside the handler, reads and writes use object.__getattribute__ and object.__setattr__ instead of normal attribute access. That avoids infinite recursion: a plain self._agents inside __getattribute__ would call __getattribute__ again.
This acts as a guard when a new SDK version is generated: if Fern adds a sub-client to BaseClient but nobody registers it in _BASE_CLIENT_PROPERTY_CLIENTS, accessing it raises AttributeError with a message asking you to add it to the registry. That covers the common case of new @property sub-clients, but if Fern changes the SDK shape in a bigger way — for example, replacing properties with a different access pattern — the override logic itself may need to be updated.
Tests
- Registry coverage —
tests/sdk/test_client_registry.pyparametrizes over every@propertyonBaseClient/AsyncBaseClientand checks thatParloa/AsyncParloacan construct it. This catches sub-clients that Fern added but nobody wired into the override registry. - Missing URL groups —
tests/sdk/test_base_url_rerouting.pyincludestest_missing_url_group_raises_key_error(and the async variant). If/v0/urlsreturns a map without the tag a sub-client needs (e.g.agents), the SDK raisesKeyErrorwith a clear message instead of sending the request to the wrong host.
See demo/README.md for a live walkthrough with debug logging against a compiled wheel.
Metadata
Release files for parloa 0.0.61
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| parloa-0.0.61-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details for parloa-0.0.61-cp310-abi3-win_amd64.whl |
| parloa-0.0.61-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ x86-64, Linux glibc 2.5+ x86-64 | Details for parloa-0.0.61-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
| parloa-0.0.61-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details for parloa-0.0.61-cp310-abi3-macosx_11_0_arm64.whl |
Total release size: 35.3 MB
Release files / parloa-0.0.61-cp310-abi3-win_amd64.whl
| Download URL | parloa-0.0.61-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 11.6 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
21617b5cc3ae53be8f99926e5b2cfe1cd7f836d13c7670ce7239e34e9a252f4f
|
|
BLAKE2b-256 checksum How to use checksums |
3ba101f5e4b84997f6aacc20d10ccbbfc6612e3c406adadd3e705846cfc6ec6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / parloa-0.0.61-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | parloa-0.0.61-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 13.0 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
1b0d9c151cbc3e646343d94f5a24c916e712eb35626a6e44b504b50590d4df13
|
|
BLAKE2b-256 checksum How to use checksums |
00c3db68a3e76a8d3eeebec7c5cc0eee93e17a9b1f169bb0bd7968a519e074d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / parloa-0.0.61-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | parloa-0.0.61-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 10.7 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
6882f653c207e5cf2fbabb18128ce0351b0c6f8a21924feabfeadf13deb0b3df
|
|
BLAKE2b-256 checksum How to use checksums |
df6835ae484aa19f133fde252857fa36373d2a83381db4760d7ea0cd1bc380b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logSecurity for parloa 0.0.61
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 3 files in this release have been uploaded with provenance metadata.
Attestation for parloa-0.0.61-cp310-abi3-win_amd64.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.