FoxNose is a managed knowledge layer for RAG and AI agents — auto-embeddings, hybrid search, and zero ETL pipelines to maintain.
This is the official Python SDK for FoxNose Management and Flux APIs.
- Type-safe clients - Full type hints and Pydantic models
- Sync and async - Both synchronous and asynchronous clients
- Automatic retries - Configurable retry with exponential backoff
- JWT authentication - Built-in token refresh support
- Flux introspection - Discover routes and live schema via
/_routerand/_schema
SDK Documentation: foxnose-python.readthedocs.io
FoxNose Platform:
pip install foxnose-sdkTo get started, you'll need a FoxNose account. Create one here.
from foxnose_sdk.management import ManagementClient
from foxnose_sdk.auth import JWTAuth
client = ManagementClient(
base_url="https://api.foxnose.net",
environment_key="your-environment-key",
auth=JWTAuth.from_static_token("YOUR_ACCESS_TOKEN"),
)
# List collections
collections = client.list_collections()
for collection in collections.results:
print(f"{collection.name} ({collection.key})")
client.close()Note (0.6.0): Folder-named methods (
list_folders,create_folder,add_api_folder,list_folder_versions,list_folder_fields, etc.) remain as deprecated aliases that emit a one-shotDeprecationWarningon first use per process. They keep their original wire behaviour (hitting the legacy/folders/...URL alias on the server) and will be removed in 1.0. Prefer the*_collection*names in new code.
from foxnose_sdk.management import AsyncManagementClient
async def main():
client = AsyncManagementClient(
base_url="https://api.foxnose.net",
environment_key="your-environment-key",
auth=JWTAuth.from_static_token("YOUR_ACCESS_TOKEN"),
)
collections = await client.list_collections()
await client.aclose()Collections can embed Components as nested fields with explicit pin
semantics (component, component_version, auto_update). The
NestedFieldMeta helper builds the meta block for you, and
sync_collection_component advances pinned fields to a target Component
version on demand.
from foxnose_sdk import (
ManagementClient,
FoxnoseConfig,
NestedFieldMeta,
)
from foxnose_sdk.auth import JWTAuth
client = ManagementClient(
FoxnoseConfig(base_url="https://api.foxnose.com"),
environment_key="prod",
auth=JWTAuth("ACCESS_TOKEN"),
)
# Embed a Component as a pinned nested field on a Collection draft.
client.create_collection_field(
"articles",
"v2-draft",
{
"key": "seo",
"name": "SEO",
"type": "nested",
"required": True,
"meta": NestedFieldMeta(
component="cmp-seo-metadata",
component_version="ver-abc12345",
auto_update=False, # default — pin until explicit sync
).to_meta(),
},
)
# Later, advance every pinned nested field to its Component's current
# version (empty body = sync all pinned).
result = client.sync_collection_component("articles")
print(result.synced_paths, result.schema_version)
# Advance specific paths to a chosen Component version.
result = client.sync_collection_component(
"articles",
field_paths=["seo"],
to_versions={"seo": "ver-def67890"},
)sync_collection_component returns a SyncComponentResponse with
synced_paths, skipped (per-path reasons), and schema_version (UID
of the newly published Collection schema version, or None if no field
needed advancing). On compatibility conflict the server returns 409
component_sync_conflict; quota exhaustion returns 422
too_many_versions. Both surface as FoxnoseAPIError.
Billing and quota responses raise typed subclasses of FoxnoseAPIError, so
existing except FoxnoseAPIError handlers keep working while new code can read
the typed attributes:
from foxnose_sdk import (
SpendCapExceeded,
PlanExhausted,
PlanLimitExceeded,
RateLimitExceeded,
)
try:
client.create_collection({"name": "Blog"})
except SpendCapExceeded as e: # HTTP 402
print(f"Spend cap {e.cap_usd}; resets at {e.cycle_resets_at}: {e.raise_cap_url}")
except PlanExhausted as e: # HTTP 402
print(f"Allowance for {e.axis} exhausted; resets at {e.window_resets_at}")
except PlanLimitExceeded as e: # HTTP 403
print(f"{e.entity}: {e.current}/{e.limit}. Upgrade: {e.upgrade_url}")
except RateLimitExceeded as e: # HTTP 429
print(f"Rate limited; retry after {e.retry_after}s")All four subclass FoxnoseAPIError, so a single except FoxnoseAPIError still
catches them if you don't need the typed fields.
from foxnose_sdk.flux import FluxClient
from foxnose_sdk.auth import SimpleKeyAuth
client = FluxClient(
base_url="https://<env_key>.fxns.io",
api_prefix="v1",
auth=SimpleKeyAuth("PUBLIC_KEY", "SECRET_KEY"),
)
resources = client.list_resources("blog-posts")
client.close()# Install with dev dependencies
pip install -e .[test,docs]
# Run tests
pytest
# Run tests with coverage
pytest --cov=foxnose_sdk --cov-report=term-missing
# Build docs
mkdocs serveApache 2.0 - see LICENSE for details.