Skip to main content

FortiCare Asset Management API Client

Project description

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 Typedpy.typed + .pyi stubs (PEP 561) for IDE auto-completion and static type checking
  • OAuth 2.0 — Built-in FortiCloud OAuth token acquisition, refresh, and lifecycle management
  • Shared SessionsCloudSession support 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_number filters use SQL LIKE wildcards — 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 limiting
  • rate_limit_strategy: str = "queue" — Strategy: queue, drop, or raise
  • rate_limit_max_requests: int = 100 — Max requests per window
  • rate_limit_window_seconds: float = 60.0 — Time window in seconds
  • rate_limit_queue_size: int = 100 — Max queued requests
  • rate_limit_queue_timeout: float = 30.0 — Max wait time in queue
  • rate_limit_queue_overflow: str = "block" — Overflow: block, drop, or raise
  • circuit_breaker: bool = False — Enable circuit breaker
  • circuit_breaker_threshold: int = 5 — Failures before opening
  • circuit_breaker_timeout: float = 60.0 — Seconds before retry
  • circuit_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

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hfortix_forticare-0.5.164.tar.gz (34.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hfortix_forticare-0.5.164-py3-none-any.whl (68.1 kB view details)

Uploaded Python 3

File details

Details for the file hfortix_forticare-0.5.164.tar.gz.

File metadata

  • Download URL: hfortix_forticare-0.5.164.tar.gz
  • Upload date:
  • Size: 34.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hfortix_forticare-0.5.164.tar.gz
Algorithm Hash digest
SHA256 82fdf2c11c9f2e9c3bf5928468e3632cb6f2aae0946d6e61bf341fb9d2019ec1
MD5 c5a07b9125c8f7c592e6e507624e79db
BLAKE2b-256 3cbafc27800544d32c539158f9525f5e0019c26858ea503b2633cbb3bca6e3b6

See more details on using hashes here.

File details

Details for the file hfortix_forticare-0.5.164-py3-none-any.whl.

File metadata

File hashes

Hashes for hfortix_forticare-0.5.164-py3-none-any.whl
Algorithm Hash digest
SHA256 4ddfc9f6b3cf60a3e54b71d3296f0ff4c9825a4e7c141d49ce0e8fe5ffafed62
MD5 395d1d91469e5d64730e500b2f128a0e
BLAKE2b-256 da8368c7faf6edc2abb7d9e220a0cbfa18fa5252c5b77fee1e8db0f16576f61f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page