In two recent blogs, I discussed using our API in PowerShell scripts and how paging helps manage the API’s line limit. If you haven’t seen them yet, you can check them out here:
Now, there’s another important topic to cover: rate limiting. This is crucial because it helps ensure stability, prevents overloading the API, and avoids errors when sending a high volume of requests.
As mentioned in the previous blogs, our API provides high-quality performance data and allows you to modify ControlUp settings. But what happens if you need to update a setting on 1,000 devices, requiring 1,000 API calls?
Like any API, ours has processing limits to ensure stability. You can find the full details on rate limiting in the ControlUp API Rate Limiting documentation.
In summary, the rate limit for our platform is approximately 200 API calls per minute. However, in ControlUp for Desktops, the limit is determined by your license size rather than a fixed number.
If your script exceeds the rate limit, the API will return an HTTP 429 (Too Many Requests) error. To prevent failures, you should implement a CATCH block in your script to detect this error and introduce a wait time before retrying.
For example, in PowerShell, you can catch the error using:
CATCH { If ($_.Exception.Response.StatusCode -eq 429) { Start-Sleep -Seconds 30 # Adjust wait time based on your needs # Retry logic here } }
Our Technical Success Manager, Joseph Stone, recently worked with a customer who accidentally applied a tag to 50,000+ devices and needed a fast way to remove it.
Joseph created a PowerShell script to detect devices with the tags and remove all tags via the API. However, removing tags from 50,000+ devices required 50,000+ API calls, which quickly hit the rate limit.
To handle this, Joseph’s script included a CATCH block for HTTP 429 errors and added a wait time before continuing. While this increased the overall runtime, it ensured the script was completed successfully without manual intervention.
Here is the PowerShell script Joseph has created:
<# .SYNOPSIS This script removes all tags from devices that still have tags assigned to them. .DESCRIPTION The script retrieves all devices with existing tags from the ControlUp API and iterates over them to remove their tags. It includes retry logic to handle API rate limits (HTTP 429) and provides progress updates during execution. .AUTHOR Joseph Stone .VERSION 1.0 .REQUIREMENTS - PowerShell 5.1 or later - ControlUp API access with valid authentication token .NOTES - Ensure the authentication token is valid before running the script. #> # Define API Endpoint and Authentication $baseUrl = "https://api.controlup.com/edge/api" $authHeader = @{ "Authorization" = "Bearer YOUR_API_KEY" "Accept" = "application/json" "Content-Type" = "application/json" } # Function to get only devices that still have tags function Get-DevicesWithTags { try { $devicesUrl = "$baseUrl/devices?export=true&_source[0]=_id&_source[1]=tags" $response = Invoke-RestMethod -Uri $devicesUrl -Headers $authHeader -Method Get return $response.rows | Where-Object { $_.tags -and $_.tags.Count -gt 0 } } catch { Write-Output "Error fetching devices: $($_.Exception.Message)" exit 1 } } # Function to remove all tags from a device with rate limit handling function Remove-TagsFromDevice { param ( [string]$deviceId, [array]$tagsToRemove ) $removeTagsUrl = "$baseUrl/devices/tags" $body = @{ "ids" = @($deviceId) "action" = "delete" "tags" = $tagsToRemove } | ConvertTo-Json -Depth 2 $maxRetries = 5 $retryCount = 0 $success = $false while (-not $success -and $retryCount -lt $maxRetries) { try { Invoke-RestMethod -Uri $removeTagsUrl -Headers $authHeader -Method Post -Body $body | Out-Null $success = $true } catch { if ($_.Exception.Response.StatusCode -eq 429) { # Extract retry-after value from response if available $retryAfter = $_.Exception.Response.Headers["Retry-After"] if (-not $retryAfter) { $retryAfter = 1 } # Default to 1 sec if not specified Write-Host "Rate limit exceeded. Retrying in $retryAfter seconds..." -ForegroundColor Red Start-Sleep -Seconds $retryAfter $retryCount++ } else { Write-Host "Error removing tags from device ID: $deviceId - $($_.Exception.Message)" -ForegroundColor Red break } } } if (-not $success) { Write-Host "Failed to remove tags from device ID: $deviceId after $maxRetries retries." -ForegroundColor Red } } # Main Execution Write-Output "Fetching only devices that still have tags..." $devices = Get-DevicesWithTags $totalDevices = $devices.Count $processedDevices = 0 if ($totalDevices -eq 0) { Write-Output "No devices with tags found. Nothing to clean." exit 0 } foreach ($device in $devices) { Remove-TagsFromDevice -deviceId $device._id -tagsToRemove $device.tags $processedDevices++ $progress = [math]::Round(($processedDevices / $totalDevices) * 100, 2) # Display per-device update Write-Host "Removed tags from device ID: $($device._id) | Tags Removed: $($device.tags -join ', ')" -ForegroundColor Green Write-Host "Progress: $processedDevices out of $totalDevices devices processed ($progress%)" -ForegroundColor Cyan } Write-Output "Tag cleanup completed successfully!"
We hope this information helps you effectively use rate limiting to ensure your scripts finish the tasks you want them to. If you have any questions, feel free to contact Chris.Twiest@ControlUp.com.