When working with different API’s via PowerShell, you may encounter the need to have a rate limit and as such I’ve built two functions to do this.
Code
Function New-RateLimitWaitTime
{
[cmdletbinding()]
param (
[System.TimeSpan]$MinimumDelay = [System.TimeSpan]::FromSeconds(1),
[System.TimeSpan]$TimeFrame = [System.TimeSpan]::FromHours(1),
[int]$RatePerTimeFrame = 1000
)
BEGIN { }
PROCESS
{
$props = @{
'MinimumDelay' = $MinimumDelay;
'TimeFrame' = $TimeFrame;
'AvailableTime' = $TimeFrame.TotalMilliseconds;
'DefaultCost' = $TimeFrame.TotalMilliseconds / $RatePerTimeFrame;
'Wait' = $MinimumDelay;
'FirstUse' = [DateTime]::Now;
'LastUse' = [DateTime]::Now;
'RatePerTimeFrame' = $RatePerTimeFrame
}
$obj = New-Object -TypeName PSObject -Property $props
$obj.PSObject.TypeNames.Insert(0, 'Custom.RateLimit')
}
END
{
Write-Output $obj
}
}
Function Get-RateLimitWaitTime
{
[cmdletbinding()]
param (
[parameter(mandatory = $true)]
[PSObject]$RateLimit
)
BEGIN
{
}
PROCESS
{
$TimeCost = $RateLimit.DefaultCost
$RateLimit.Wait = $RateLimit.MinimumDelay
$Compare = [System.TimeSpan]::FromTicks($RateLimit.FirstUse.AddMilliseconds($RateLimit.TimeFrame.TotalMilliseconds).Ticks - (get-date).Ticks)
Write-Verbose "`$Compare = $($Compare)"
if ($Compare.TotalMilliseconds -lt 0)
{
Write-Verbose "Time elasped setting AvailableTime to $($RateLimit.TimeFrame.TotalMilliseconds)"
$RateLimit.AvailableTime = $RateLimit.TimeFrame.TotalMilliseconds
$RateLimit.FirstUse = [DateTime]::Now
}
if (($RateLimit.AvailableTime - $TimeCost) -lt 0)
{
$RateLimit.Wait = [System.TimeSpan]::FromTicks($RateLimit.FirstUse.AddMilliseconds($RateLimit.TimeFrame.TotalMilliseconds).Ticks - (get-date).Ticks)
$TimeCost = 0
}
$RateLimit.LastUse = [DateTime]::Now
$RateLimit.AvailableTime -= $TimeCost
}
END
{
Write-Verbose "WaitTime = $($RateLimit.Wait), Using MS = $($TimeCost), MsToUseInQueue = $($RateLimit.AvailableTime), TimeFrame = $($RateLimit.TimeFrame), MaxRatePerTimeFrame = $($RateLimit.RatePerTimeFrame), TimeFrame LastUsed = $($RateLimit.LastUse)"
Write-Output $RateLimit
}
}
Usage
#creating a new rate limit object, this instance we are creating will allow 100 requests per minute with a limit of 1500 per hour as such the minimum delay is 600 ms
$RateLimit = New-RateLimitWaitTime -RatePerTimeFrame 1500 -TimeFrame ([System.TimeSpan]::FromMinutes(60)) -MinimumDelay ([System.TimeSpan]::FromMilliseconds(600))
for ($i = 1; $i -le 10; $i++) {
$RateLimit = Get-RateLimitWaitTime($RateLimit)
Start-Sleep -Milliseconds $RateLimit.Wait.TotalMilliseconds
Write-Host "$($i)"
}