hfortix-forticare
Python SDK for the Fortinet FortiCare Asset Management V3 API.
Overview
hfortix-forticare provides a fully typed Python interface to the FortiCare
Asset Management API, enabling programmatic access to:
- Product Management: Register, list, update, and decommission products
- License Management: Register, list, and download licenses
- Contract Management: View contract information
- Folder Management: Organize assets into folders
- Service Management: Register subscription services
Features
- Fully Typed —
py.typed+.pyistubs (PEP 561) for IDE auto-completion and static type checking - OAuth 2.0 — Built-in FortiCloud OAuth token acquisition, refresh, and lifecycle management
- Shared Sessions —
CloudSessionsupport for sharing credentials with other FortiCloud clients (e.g.hfortix-fortiztp) - Resilience — Optional rate limiting and circuit breaker (opt-in), automatic retries with backoff
- Auditing — Optional operation tracking and audit logging
Installation
pip install hfortix-forticare
Quick Start
1. Authentication
You have multiple options for authentication:
Option A: CloudSession (Recommended for Multi-Service)
from hfortix_core.session import CloudSession
from hfortix_forticare import FortiCare
# CloudSession manages tokens for multiple services
with CloudSession(api_id="your_api_id", password="your_password") as session:
fc = FortiCare(session=session) # Auto-uses "assetmanagement" client_id
products = fc.api.products.list.post()
# Token is automatically managed and shared efficiently
CloudSession can also configure rate limiting once for every client that uses it:
from hfortix_core.session import CloudSession
from hfortix_forticare import FortiCare
session = CloudSession(
api_id="your_api_id",
password="your_password",
# Global rate limiting - inherited by all clients
rate_limit=True,
rate_limit_max_requests=20, # 20 requests per window
rate_limit_window_seconds=60.0,
circuit_breaker=True,
)
fc = FortiCare(session=session) # Inherits 20 req/min
# Or override for a specific client
fc_fast = FortiCare(
session=session,
rate_limit_max_requests=50, # Override session's 20 req/min
)
Option B: Auto-Login with Credentials
from hfortix_forticare import FortiCare
# Auto-login with API credentials
fcc = FortiCare(
api_id="your_api_id",
password="your_password",
)
# Token is automatically obtained and managed
Option C: Use a Pre-Obtained OAuth Token
# Get token from the FortiCloud OAuth endpoint first:
# https://customerapiauth.fortinet.com/api/v1/oauth/token/
fcc = FortiCare(oauth_token="your_oauth_token_here")
2. Use the API
Wildcard note:
serial_numberfilters use SQLLIKEwildcards — use%(e.g."FGT%"), not*. It defaults to"%"(all products).
# List products with entitlements
products = fcc.api.products.list.post(
serial_number="FGT%",
status="Registered",
)
# Get product details
details = fcc.api.products.details.post(
serial_number="FGT90D1234567890",
)
# Register a product
result = fcc.api.products.register.post(
registration_units=[
{
"serialNumber": "FGT90D1234567890",
"description": "My FortiGate",
# ... other fields
}
]
)
# List contracts
contracts = fcc.api.contracts.list.post()
# List licenses
licenses = fcc.api.licenses.list.post()
# Manage folders
folders = fcc.api.folders.list.post()
# Always clean up when done
fcc.logout()
API Structure
The SDK mirrors the FortiCare Asset Management V3 API structure:
fcc.api.
├── products.
│ ├── list - List products with entitlements
│ ├── register - Register products and contracts
│ ├── details - Get product details
│ ├── description - Update product description
│ ├── location - Update product location
│ ├── folder - Update product folder
│ ├── decommission - Decommission products
│ └── transfer - Transfer products between accounts
├── licenses.
│ ├── list - List licenses
│ ├── register - Register a license
│ └── download - Download VM license file
├── contracts.
│ └── list - List contracts
├── folders.
│ ├── list - List asset folders
│ ├── create - Create new folder
│ └── delete - Delete folder
└── services.
└── register - Register subscription service
Responses
Every endpoint returns a FortiCareResponse object with attribute access
(responses are not subscriptable — use attributes or .get()):
response = fcc.api.products.list.post(serial_number="FGT%")
response.status # API status code (0 = success)
response.message # Error message (empty on success)
response.http_status_code # HTTP status code (200, 404, ...)
response.response_time # Request duration in seconds
response.raw # Full response as a plain dict
response.get("assets", []) # Dict-style access with default
# List responses expose typed collections
for asset in response.assets:
print(asset.serialNumber, asset.productModel)
Rate Limits
The FortiCare Asset Management API enforces the following limits server-side:
- 100 calls per minute
- 1000 calls per hour
- 10 errors per hour
- Batch operations: Max 10 units, max 5 errors per batch
Rate Limit Enforcement (opt-in)
Client-side enforcement is disabled by default. Enable it with
rate_limit=True (token bucket with queue support):
from hfortix_forticare import FortiCare
fcc = FortiCare(
api_id="your_api_id",
password="your_password",
rate_limit=True,
rate_limit_max_requests=100, # FortiCare: 100/min
rate_limit_window_seconds=60.0, # 60 second window
rate_limit_strategy="queue", # Queue excess requests
rate_limit_queue_size=50, # Max 50 queued requests
)
Rate limiting / circuit breaker parameters:
rate_limit: bool = False— Enable/disable rate limitingrate_limit_strategy: str = "queue"— Strategy:queue,drop, orraiserate_limit_max_requests: int = 100— Max requests per windowrate_limit_window_seconds: float = 60.0— Time window in secondsrate_limit_queue_size: int = 100— Max queued requestsrate_limit_queue_timeout: float = 30.0— Max wait time in queuerate_limit_queue_overflow: str = "block"— Overflow:block,drop, orraisecircuit_breaker: bool = False— Enable circuit breakercircuit_breaker_threshold: int = 5— Failures before openingcircuit_breaker_timeout: float = 60.0— Seconds before retrycircuit_breaker_half_open_calls: int = 3— Test calls in half-open state
Rate Limit Tracking (monitoring only)
Track your API usage to stay within limits — these counters are informational only and never block requests:
from hfortix_forticare import FortiCare
# Configure custom limits (all optional, default to None = no limit)
fcc = FortiCare(
api_id="...",
password="...",
rate_limit_calls_per_min=100, # FortiCare: 100/min
rate_limit_calls_per_hour=1000, # FortiCare: 1000/hour
rate_limit_errors_per_hour=10, # FortiCare: 10 errors/hour
)
products = fcc.api.products.list.post()
status = fcc.get_rate_limit_status()
print(f"Calls last min: {status['calls_last_min']}")
print(f"Calls last hour: {status['calls_last_hour']}")
print(f"Errors last hour: {status['errors_last_hour']}")
print(f"Within limits: {status['within_limits']}")
Error Handling
API-level errors are reported in the response body (.status != 0).
HTTP and network failures raise httpx exceptions; the opt-in rate
limiter and circuit breaker raise hfortix_core.exceptions types:
import httpx
from hfortix_core.exceptions import (
CircuitBreakerOpenError,
RateLimitExceededError,
)
try:
result = fcc.api.products.list.post(serial_number="FGT%")
if result.status == 0:
print("Success!")
else:
print(f"API Error: {result.message}")
except httpx.HTTPStatusError as e:
print(f"HTTP Error: {e.response.status_code}")
except httpx.TimeoutException:
print("Request timed out")
except httpx.RequestError as e:
print(f"Network error: {e}")
except CircuitBreakerOpenError:
print("Circuit breaker is open") # only with circuit_breaker=True
except RateLimitExceededError:
print("Rate limited") # only with rate_limit=True, strategy="raise"
Context Manager
Use as a context manager for automatic cleanup:
with FortiCare(oauth_token="...") as fcc:
products = fcc.api.products.list.post()
# ... use the client ...
# Automatically logged out
Configuration
fcc = FortiCare(
oauth_token="your_token",
# Default base URL:
base_url="https://support.fortinet.com/ES/api/registration/v3",
verify=True, # SSL verification
max_retries=3, # Retry attempts
connect_timeout=10.0, # Connection timeout (seconds)
read_timeout=300.0, # Read timeout (seconds)
)
OAuth Token Management
The SDK provides OAuth token lifecycle management for long-running
applications (available when initialized with api_id/password):
View Login Response
fcc = FortiCare(api_id="...", password="...")
response = fcc.login(force_refresh=True)
print(f"Access Token: {response['access_token']}")
print(f"Refresh Token: {response['refresh_token']}")
print(f"Expires In: {response['expires_in']} seconds")
Refresh Token
# Efficiently refresh token without re-entering credentials
# (requires a prior login(force_refresh=True) to obtain a refresh token)
refresh_response = fcc.refresh_token()
print(f"New token expires in: {refresh_response['expires_in']}s")
Track Token Expiration
# Check time remaining
remaining = fcc.get_token_time_remaining()
print(f"Token valid for {remaining} more seconds")
# Check if expired or expiring soon
if fcc.is_token_expired(buffer_seconds=300): # 5 min buffer
print("Token expiring soon, refreshing...")
fcc.refresh_token()
# Get comprehensive token info
info = fcc.get_token_info()
print(f"Created: {info['created_at_iso']}")
print(f"Expires: {info['expires_at_iso']}")
print(f"Time Remaining: {info['time_remaining']}s")
print(f"Expires Soon: {info['expires_soon']}")
Package Structure
hfortix_forticare/
├── __init__.py # Main FortiCare client
├── models.py # FortiCareResponse and typed wrappers
├── types.py # TypedDict definitions
└── api/
└── v3/
├── __init__.py # V3API class
├── products/ # Product endpoints
├── licenses/ # License endpoints
├── contracts/ # Contract endpoints
├── folders/ # Folder endpoints
└── services/ # Service endpoints
Requirements
- Python 3.9+
- httpx
- hfortix-core
License
Proprietary — free to use, but may not be resold or redistributed as a standalone product. See LICENSE for the full terms.
Copyright (c) 2025 Herman W. Jacobsen
Links
Metadata
Release files for hfortix-forticare 0.5.164
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hfortix_forticare-0.5.164.tar.gz | 34.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hfortix_forticare-0.5.164-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 102.2 kB
Release files / hfortix_forticare-0.5.164.tar.gz
| Download URL | hfortix_forticare-0.5.164.tar.gz |
|---|---|
| Size | 34.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82fdf2c11c9f2e9c3bf5928468e3632cb6f2aae0946d6e61bf341fb9d2019ec1
|
|
BLAKE2b-256 checksum How to use checksums |
3cbafc27800544d32c539158f9525f5e0019c26858ea503b2633cbb3bca6e3b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / hfortix_forticare-0.5.164-py3-none-any.whl
| Download URL | hfortix_forticare-0.5.164-py3-none-any.whl |
|---|---|
| Size | 68.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4ddfc9f6b3cf60a3e54b71d3296f0ff4c9825a4e7c141d49ce0e8fe5ffafed62
|
|
BLAKE2b-256 checksum How to use checksums |
da8368c7faf6edc2abb7d9e220a0cbfa18fa5252c5b77fee1e8db0f16576f61f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|