A Python client library for the Ruckus One API.
- OAuth2 Authentication: Automatic token management with caching and refresh
- Resource-Based API: Intuitive object-oriented interface (client.venues.all(), client.aps.get(), etc.)
- Full API Coverage: Access to all Ruckus One API endpoints
- Type Hints: Full type annotations for better IDE support
- Error Handling: Custom exceptions for different error scenarios
- Retry Logic: Automatic retry with exponential backoff
- Client-Side Validation: Pre-validation of required fields and data formats
- Address Validation: Country name validation using full country names (rejects ISO codes)
- Async Operations: Support for asynchronous API operations with polling
- Multi-Region Support: Support for US, EU, and Asia regions
- Logging: Configurable logging throughout the client
- Python 3.8+: Modern Python features and type hints
pip install ruckus-oneOr install from source:
git clone https://github.com/wifinity/python-ruckus-one.git
cd python-ruckus-one
pip install -e .from ruckus_one import RuckusOneClient
# Initialize the client with your OAuth2 credentials
client = RuckusOneClient(
region="us", # or "eu", "asia"
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
)
# List all venues
venues = client.venues.all()
print(f"Found {len(venues)} venues")
# Get a specific venue by ID
venue = client.venues.get(id="venue_123")
# Or get by name (with caching)
venue = client.venues.get(name="Main Office")
# List APs in a venue
aps = client.aps.list(venue_id=venue["id"])
# Get AP by serial number
ap = client.aps.get_by_serial(venue_id=venue["id"], serial_number="ABC123")
# Close the client when done
client.close()from ruckus_one import RuckusOneClient
# Basic initialization
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
)
# With custom configuration
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret",
delegated_tenant_id="delegated_tenant_id", # For MSP/delegated accounts
timeout=30.0,
max_retries=3,
enable_retry=True,
log_level="DEBUG",
)The client can be used as a context manager for automatic cleanup:
with RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
) as client:
venues = client.venues.all()
# Client is automatically closed when exiting the contextThe client provides resource objects for intuitive API access:
# Get all venues (automatically handles pagination)
venues = client.venues.all()
# Get a single venue by ID
venue = client.venues.get(id="venue_123")
# Or get by name (with caching)
venue = client.venues.get(name="Main Office")
# Filter venues (client-side filtering)
filtered = client.venues.filter(name="Office")
# Create a venue
new_venue = client.venues.create(
name="New Office",
address={
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"country": "United States" # Must be full country name, not ISO code
}
)
# Update a venue
updated = client.venues.update("venue_123", {"name": "Updated Office"})
# Delete a venue (or multiple venues)
client.venues.delete("venue_123")
# Or delete multiple:
client.venues.delete(["venue_123", "venue_456"])Address Validation: The address.country field must be a full country name (e.g., "United States", "United Kingdom"). ISO codes (e.g., "US", "GB") are rejected with a clear error message.
# List APs in a venue
aps = client.aps.list(venue_id="venue_123")
# Get AP by serial number
ap = client.aps.get_by_serial(
venue_id="venue_123",
serial_number="ABC123"
)
# Create/add an AP to a venue
# Note: Both serialNumber and name are required
new_ap = client.aps.create(
venue_id="venue_123",
name="R550 Access Point", # Required
serialNumber="ABC123", # Required
description="Office AP",
tags=["Production"]
)
# Update an AP
updated = client.aps.update(
venue_id="venue_123",
serial_number="ABC123",
data={"name": "Updated AP Name"}
)
# Delete an AP (or multiple APs by serial number)
client.aps.delete(venue_id="venue_123", serial_number="ABC123")
# Or delete multiple:
client.aps.delete(venue_id="venue_123", serial_number=["ABC123", "DEF456"])
# Move an AP to a different venue
client.aps.move(
venue_id="venue_123",
serial_number="ABC123",
target_venue_id="venue_456"
)
# Get LLDP neighbors
# Note: The AP does not always have a live, constant list of neighbors.
# The data must be collected (scanned) before it can be queried.
# First, trigger neighbor collection:
result = client.aps.collect_neighbors(
venue_id="venue_123",
serial_number="ABC123",
neighbor_type="LLDP_NEIGHBOR" # or "RF_NEIGHBOR"
)
# Wait a moment for the scan to complete (AP performs scan asynchronously),
# then query for neighbors:
neighbors = client.aps.get_lldp_neighbors(
venue_id="venue_123",
serial_number="ABC123",
page=1,
page_size=25,
sort_field="deviceName", # Optional
sort_order="ASC" # Optional, "ASC" or "DESC"
)
# If no neighbor data is available, get_lldp_neighbors() returns an empty listAP Network Settings
Get or update an AP's network settings (IP type, address, netmask, gateway, DNS). The API validates IP and netmask formats.
# Get current network settings
settings = client.aps.get_network_settings(
venue_id="venue_123",
serial_number="ABC123"
)
# Returns dict with ipType, ip, netmask, gateway, primaryDnsServer, secondaryDnsServer
# Update network settings (e.g. set static IP)
client.aps.update_network_settings(
venue_id="venue_123",
serial_number="ABC123",
settings={
"ipType": "STATIC",
"ip": "192.168.1.10",
"netmask": "255.255.255.0",
"gateway": "192.168.1.1",
"primaryDnsServer": "8.8.8.8",
"secondaryDnsServer": "8.8.4.4",
}
)# Get all Wi-Fi networks
networks = client.wifi_networks.all()
# Get a single network by ID
network = client.wifi_networks.get(id="network_123")
# Or get by name (with caching)
network = client.wifi_networks.get(name="Guest Network")
# Filter networks (client-side filtering)
filtered = client.wifi_networks.filter(ssid="Guest")
# Create a Wi-Fi network
# Note: name, type, and wlan (with ssid) are required
new_network = client.wifi_networks.create(
name="Guest Network", # Required
type="psk", # Required (one of: aaa, dpsk, guest, hotspot20, open, psk)
ssid="GuestSSID", # Required (will be nested in wlan object)
# ... other network configuration
)
# Update a network
updated = client.wifi_networks.update(
"network_123",
{"ssid": "UpdatedSSID"}
)
# Delete a network
client.wifi_networks.delete("network_123", deep=True)
# List networks activated at a venue
venue_networks = client.wifi_networks.list_venue_networks(venue_id="venue_123")
# Activate network at a venue
result = client.wifi_networks.activate_at_venue(
venue_id="venue_123",
network_id="network_123"
)
# Deactivate network at venue (using network-venue association ID)
client.wifi_networks.deactivate_at_venue("network_venue_association_id")# List AP groups in a venue
ap_groups = client.ap_groups.list(venue_id="venue_123")
# Get AP group by ID
ap_group = client.ap_groups.get(venue_id="venue_123", id="apgroup_123")
# Or get by name (with caching)
ap_group = client.ap_groups.get_by_name(
venue_id="venue_123",
name="Main AP Group"
)
# Create an AP group
# Note: name is required
new_group = client.ap_groups.create(
venue_id="venue_123",
name="New AP Group" # Required
)
# Update an AP group
updated = client.ap_groups.update(
venue_id="venue_123",
id="apgroup_123",
data={"name": "Updated Group"}
)
# Activate a network on an AP group
client.ap_groups.activate_network(
venue_id="venue_123",
ap_group_id="apgroup_123",
network_id="network_123"
)
# Deactivate a network from an AP group
client.ap_groups.deactivate_network(
venue_id="venue_123",
ap_group_id="apgroup_123",
network_id="network_123"
)Many create/update operations return a requestId for async processing. Use the activities resource to poll for completion:
# Create a venue (may return requestId)
result = client.venues.create(name="New Venue", address={"country": "United States"})
request_id = result.get("requestId")
if request_id:
# Poll until completion
activity = client.activities.wait_for_completion(
request_id=request_id,
timeout=300.0, # 5 minutes
poll_interval=2.0 # Poll every 2 seconds
)
# Check activity status
if activity.get("status") == "SUCCESS":
print("Operation completed successfully")
else:
print(f"Operation failed: {activity.get('message')}")
# Or get activity status directly
activity = client.activities.get(request_id="request_123")For endpoints not covered by resources, use the low-level HTTP methods:
# GET request
result = client.get("/custom/endpoint", params={"key": "value"})
# POST request
result = client.post("/custom/endpoint", json={"data": "value"})
# PUT request
result = client.put("/custom/endpoint", json={"data": "value"})
# DELETE request
result = client.delete("/custom/endpoint")The client raises specific exceptions for different error scenarios:
from ruckus_one import (
RuckusOneClient,
RuckusOneAuthenticationError,
RuckusOneNotFoundError,
RuckusOnePermissionError,
RuckusOneValidationError,
RuckusOneAPIError,
RuckusOneConnectionError,
)
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
)
try:
venue = client.venues.get(id="venue_123")
except RuckusOneNotFoundError:
print("Venue not found")
except RuckusOneAuthenticationError:
print("Authentication failed - check your tenant_id, client_id, and client_secret")
except RuckusOnePermissionError:
print("Permission denied")
except RuckusOneValidationError as e:
print(f"Validation error: {e.errors}")
except RuckusOneAPIError as e:
print(f"API error: {e.status_code} - {e.message}")
except RuckusOneConnectionError:
print("Connection error - check your network connection")The client includes automatic retry logic with exponential backoff for connection errors:
# Retry is enabled by default
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret",
enable_retry=True,
max_retries=3
)
# Disable retry for specific operations
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret",
enable_retry=False
)The client supports configurable logging to help debug API interactions. You can set the log level in two ways:
# Set log level when creating the client
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret",
log_level="DEBUG"
)from ruckus_one import set_log_level
# Set log level for all clients
set_log_level("DEBUG")
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
)DEBUG- Detailed information including request/response bodies, headers, and status codesINFO- General informational messagesWARNING- Warning messages (default)ERROR- Error messages onlyCRITICAL- Critical errors only
When DEBUG level is enabled, you'll see detailed information about each API request:
DEBUG:ruckus_one.client:GET https://api.ruckus.cloud/api/venues
DEBUG:ruckus_one.client:Query parameters: {}
DEBUG:ruckus_one.client:Request headers: {'Authorization': 'Bearer ***', 'Content-Type': 'application/json'}
DEBUG:ruckus_one.client:Response status: 200
DEBUG:ruckus_one.client:Response headers: {'Content-Type': 'application/json', 'Content-Length': '1234'}
DEBUG:ruckus_one.client:Response body: [
{
"id": "venue_123",
"name": "Main Office"
}
]
For security, sensitive data is automatically masked in logs:
- Authorization tokens:
Bearer abc123→Bearer *** - API keys and other sensitive headers are masked
- Request/response bodies are logged as-is (be careful with sensitive data in request bodies)
from ruckus_one import RuckusOneClient, set_log_level
# Enable debug logging
set_log_level("DEBUG")
client = RuckusOneClient(
region="us",
tenant_id="your_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret"
)
# All API calls will now log detailed information
venues = client.venues.all()# List all venues
venues = client.venues.all()
# Find venue by name
venue = client.venues.get(name="Main Office")
# Create a new venue with address validation
new_venue = client.venues.create(
name="Branch Office",
address={
"street": "456 Branch St",
"city": "London",
"zipCode": "SW1A 1AA",
"country": "United Kingdom" # Full country name required
}
)
# Update venue
updated = client.venues.update(
new_venue["id"],
{"address": {"city": "Manchester"}}
)
# Delete venue
client.venues.delete(new_venue["id"])# Get venue first
venue = client.venues.get(name="Main Office")
# List all APs in venue
aps = client.aps.list(venue_id=venue["id"])
# Get specific AP by serial
ap = client.aps.get_by_serial(
venue_id=venue["id"],
serial_number="ABC123"
)
# Create a new AP (name and serialNumber are required)
new_ap = client.aps.create(
venue_id=venue["id"],
name="R550 Access Point", # Required
serialNumber="ABC123", # Required
description="Office AP"
)
# Get AP details (includes operational status)
ap = client.aps.get_by_serial(
venue_id=venue["id"],
serial_number="ABC123"
)
print(f"AP Status: {ap.get('connectionStatus')}")
print(f"IP Address: {ap.get('ipAddress')}")
# Move AP to different venue
target_venue = client.venues.get(name="Branch Office")
client.aps.move(
venue_id=venue["id"],
serial_number="ABC123",
target_venue_id=target_venue["id"]
)
# Collect and query LLDP neighbors
# The AP needs to actively scan for neighbors first
result = client.aps.collect_neighbors(
venue_id=venue["id"],
serial_number="ABC123",
neighbor_type="LLDP_NEIGHBOR" # or "RF_NEIGHBOR"
)
# Wait a moment for the scan to complete, then query
import time
time.sleep(2) # Wait for scan to complete
neighbors = client.aps.get_lldp_neighbors(
venue_id=venue["id"],
serial_number="ABC123"
)
for neighbor in neighbors:
print(f"Neighbor: {neighbor.get('deviceName')} on {neighbor.get('interface')}")
# Get or update AP network settings
settings = client.aps.get_network_settings(
venue_id=venue["id"],
serial_number="ABC123"
)
client.aps.update_network_settings(
venue_id=venue["id"],
serial_number="ABC123",
settings={"ipType": "STATIC", "ip": "192.168.1.10", "netmask": "255.255.255.0", "gateway": "192.168.1.1"}
)# Create a Wi-Fi network (name, type, and wlan with ssid are required)
network = client.wifi_networks.create(
name="Guest Network", # Required
type="psk", # Required (one of: aaa, dpsk, guest, hotspot20, open, psk)
ssid="GuestSSID", # Required (will be nested in wlan object)
# ... other configuration
)
# Get venue
venue = client.venues.get(name="Main Office")
# Activate network at venue (creates network-venue association)
result = client.wifi_networks.activate_at_venue(
venue_id=venue["id"],
network_id=network["id"]
)
# List all networks activated at the venue
venue_networks = client.wifi_networks.list_venue_networks(venue_id=venue["id"])
# Deactivate network at venue
# Note: Use the association ID from list_venue_networks or activate_at_venue response
association_id = result.get("id")
if association_id:
client.wifi_networks.deactivate_at_venue(association_id)# Get venue
venue = client.venues.get(name="Main Office")
# Create an AP group (name is required)
ap_group = client.ap_groups.create(
venue_id=venue["id"],
name="Production AP Group" # Required
)
# Get network
network = client.wifi_networks.get(name="Guest Network")
# Activate network on AP group
client.ap_groups.activate_network(
venue_id=venue["id"],
ap_group_id=ap_group["id"],
network_id=network["id"]
)
# List all AP groups in venue
all_groups = client.ap_groups.list(venue_id=venue["id"])# Create a venue (may return requestId for async processing)
result = client.venues.create(
name="New Venue",
address={"country": "United States"}
)
request_id = result.get("requestId")
if request_id:
# Wait for operation to complete
activity = client.activities.wait_for_completion(
request_id=request_id,
timeout=300.0, # 5 minutes
poll_interval=2.0 # Poll every 2 seconds
)
if activity.get("status") == "SUCCESS":
print("Venue created successfully")
venue_id = activity.get("result", {}).get("id")
else:
print(f"Operation failed: {activity.get('message')}")
else:
# Operation completed synchronously
venue_id = result.get("id")The client supports three regions:
"us"→https://api.ruckus.cloud(North America)"eu"→https://api.eu.ruckus.cloud(Europe)"asia"→https://api.asia.ruckus.cloud(Asia)
For MSP (Managed Service Provider) or delegated account scenarios, use the delegated_tenant_id parameter:
client = RuckusOneClient(
region="us",
tenant_id="msp_tenant_id",
client_id="your_client_id",
client_secret="your_client_secret",
delegated_tenant_id="managed_tenant_id" # Sets x-rks-tenantid header
)The client performs pre-validation before making API calls to provide faster, more user-friendly feedback:
- Venues:
name,addressare required for creation - APs:
name,serialNumberare required for creation - Wi-Fi Networks:
name,type, andwlan(withssid) are required for creation - AP Groups:
nameis required for creation
The address.country field must be a full country name. ISO codes are rejected:
# ✅ Valid
client.venues.create(
name="Office",
address={"country": "United States"}
)
# ✅ Valid (case-insensitive)
client.venues.create(
name="Office",
address={"country": "united states"}
)
# ❌ Invalid - ISO alpha-2 code
client.venues.create(
name="Office",
address={"country": "US"} # Raises ValueError
)
# ❌ Invalid - ISO alpha-3 code
client.venues.create(
name="Office",
address={"country": "USA"} # Raises ValueError
)# Run all tests
make tests
# Run with coverage
pytest --cov=ruckus_one --cov-report=htmlmake type-check