Handling ControlUp API Rate Limits in Your Scripts

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.

Understanding the ControlUp API Rate Limit

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.

Handling Rate Limits in PowerShell

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
    }
}

Use Case: Deleting Tags from a Large Number of Devices

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

 

Chris Twiest

Chris Twiest works as a Technical Integrations Manager at ControlUp, where he focuses on designing integrations within the ControlUp product and with other technologies to enhance its capabilities and effectiveness. With two decades of experience as a consultant managing, creating, and automating workspace environments, he has a deep understanding of customer workflows. He is passionate about integrating products and data to meet specific customer needs and deliver optimal solutions.