5 мин чтенияИнженерия

How to Shorten a URL in PowerShell With Invoke-RestMethod

Shorten a URL in PowerShell with one cmdlet, then handle 429s, keep the API key out of the script, and shorten a whole CSV column with ForEach-Object -Parallel.

Marius Voß
DevRel · edge infra
How to shorten a URL in PowerShell: Invoke-RestMethod posting a destination URL to a shortener API and returning the short link as an object

Shortening a URL in PowerShell is one cmdlet. Invoke-RestMethod posts your destination to the shortener's API, sends the API key as a Bearer header, and hands back a parsed object rather than a string of JSON. Nothing to install, on any machine that already has PowerShell.

This is the sysadmin corner of a series that also covers Python, JavaScript, C#, and Go. The endpoint shape and auth model are documented in the free URL shortener API overview; the dashboard route is in the general how-to-shorten-a-url guide.

The Fastest Way: One Invoke-RestMethod Call

$headers = @{
    Authorization  = "Bearer $env:ELIDO_API_KEY"
    'Content-Type' = 'application/json'
}

$body = @{
    destination_url = 'https://example.com/spring-sale?utm_source=newsletter'
} | ConvertTo-Json

$link = Invoke-RestMethod -Method Post `
    -Uri 'https://api.elido.app/v1/links' `
    -Headers $headers `
    -Body $body `
    -TimeoutSec 10

$link.short_url   # https://s.elido.me/ab12cd

Invoke-RestMethod deserialises the response, which is the difference between it and Invoke-WebRequest. There is no ConvertFrom-Json step and no .Content to unwrap: $link is already an object with id and short_url on it.

ConvertTo-Json is not optional. Hand a hashtable straight to -Body and PowerShell form-encodes it, the API sees application/x-www-form-urlencoded, and you get a 400 that reads like a server problem.

One PowerShell-specific surprise: a 401 or 404 is a terminating error here, not a return value. Every other client in this series makes you check a status code; this one makes you catch an exception.

Handle 429s and Keep the Script Re-Runnable

For anything scheduled, three things need adding. A try/catch, because of the throwing behaviour above. A wait that respects Retry-After. And an Idempotency-Key so a retried request returns the original link instead of creating a second one for the same destination.

function Get-ShortLink {
    param(
        [Parameter(Mandatory)][string] $Destination,
        [int] $Attempts = 3
    )

    # stable key: the same destination always produces the same one
    $sha  = [System.Security.Cryptography.SHA256]::Create()
    $key  = [BitConverter]::ToString(
                $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Destination))
            ).Replace('-', '')

    $headers = @{
        Authorization     = "Bearer $env:ELIDO_API_KEY"
        'Content-Type'    = 'application/json'
        'Idempotency-Key' = $key
    }
    $body = @{ destination_url = $Destination } | ConvertTo-Json

    for ($attempt = 0; $attempt -lt $Attempts; $attempt++) {
        try {
            return (Invoke-RestMethod -Method Post -Uri 'https://api.elido.app/v1/links' `
                        -Headers $headers -Body $body -TimeoutSec 10).short_url
        }
        catch {
            $status = $_.Exception.Response.StatusCode.value__

            if ($status -eq 429) {
                $wait = $_.Exception.Response.Headers['Retry-After']
                if (-not $wait) { $wait = 2 }        # works on 5.1 too; no ?? operator
                Start-Sleep -Seconds ([int]$wait)
            }
            elseif ($status -ge 500) {
                Start-Sleep -Seconds ([math]::Pow(2, $attempt))   # 1s, 2s, 4s
            }
            else {
                throw   # 401, 403, 422: retrying will not help
            }
        }
    }

    throw "shorten failed after $Attempts attempts"
}

On PowerShell 7 you can skip most of the exception archaeology with -SkipHttpErrorCheck, which returns the response object instead of throwing so you can read the API's error body directly. That is worth doing when you are debugging a 422 and want to see which field the API objected to.

PowerShell Invoke-RestMethod posting a destination URL with a Bearer token and Idempotency-Key to the shortener links endpoint and returning a parsed object containing short_url

Keep the Key Out of the Script

$env:ELIDO_API_KEY is the low-effort answer and it is good enough for a scheduled task with its own service account. A key typed into the .ps1 file is not: it lands in source control, in Get-History, and in any transcript logging the estate has switched on.

For anything shared, the SecretManagement module is the better home:

$env:ELIDO_API_KEY = Get-Secret -Name ElidoApiKey -AsPlainText

Rotating a key then means updating one vault entry rather than hunting through scripts on four servers.

Want to run these as written? Create a key on the free plan, set $env:ELIDO_API_KEY, and everything on this page works unchanged.

Shorten a Whole CSV Column

The common real task is not one URL, it is a spreadsheet of them. Four cmdlets and a throttle:

$rows    = Import-Csv .\campaign-urls.csv     # columns: id, destination
$funcDef = ${function:Get-ShortLink}.ToString()

$done = $rows | ForEach-Object -ThrottleLimit 8 -Parallel {
    $function:Get-ShortLink = $using:funcDef   # ship the function into each runspace
    [pscustomobject]@{
        id          = $_.id
        destination = $_.destination
        short_url   = try   { Get-ShortLink -Destination $_.destination }
                      catch { "ERROR: $($_.Exception.Message)" }
    }
}

$done | Export-Csv .\campaign-urls-short.csv -NoTypeInformation

-ThrottleLimit 8 is the whole trick: eight requests in flight no matter whether the file has fifty rows or fifty thousand. Two gotchas come with -Parallel. Each iteration runs in its own runspace, so functions and variables from the caller are not visible unless you pass them in with $using:. And it needs PowerShell 7; on Windows PowerShell 5.1 a plain foreach is the honest answer for a few hundred rows.

Writing errors into the output column instead of throwing keeps the run re-runnable. Because the idempotency key is derived from the destination, re-running the whole file after fixing three rows costs nothing and creates no duplicates.

A four-stage PowerShell pipeline importing a CSV of destinations, calling the shortener API per row with a throttle limit, exporting the short links, and spot-checking the result

Spot-check before anything gets printed or emailed. curl.exe -sI https://s.elido.me/ab12cd on three rows takes ten seconds and catches the case where a column was off by one.

When PowerShell Is the Right Tool

It is the right tool when the data is already in a CSV or Active Directory, when the job runs on a Windows scheduled task, or when the person maintaining it is a sysadmin rather than an application developer. For anything that ships inside an application, the C# version with a typed client and dependency injection is easier to test.

Either way: key from the environment or a vault, an explicit -TimeoutSec, try/catch around the call, and a stable idempotency key on anything that might run twice. The API and SDKs page covers the generated clients, and solutions for developers covers what else the API exposes.

Read the Cornerstone Series

This sits in the engineering cluster. Start with the free URL shortener API guide, then rate limits and idempotency. The live reference is the API docs, and bulk-import short links from a Google Sheet covers the no-code path for the same CSV job.

Частые вопросы

How do I shorten a URL in PowerShell?

Call Invoke-RestMethod with -Method Post against the shortener's links endpoint, passing your API key in an Authorization header and the destination as JSON. The cmdlet parses the response for you, so $link.short_url is available immediately without a ConvertFrom-Json step.

Do I need a module to shorten URLs in PowerShell?

No. Invoke-RestMethod is built into Windows PowerShell 3.0 and every version of PowerShell 7, and a shortener call is a single request. Modules are worth it when you want cmdlet-shaped wrappers across many endpoints rather than one POST.

Why does Invoke-RestMethod throw on a 404 or 401?

Because it treats any 4xx or 5xx as a terminating error, which is the opposite of most HTTP clients. Wrap the call in try/catch, or pass -SkipHttpErrorCheck on PowerShell 7 to get the response object back so you can read the error body the API returned.

How do I keep the API key out of a PowerShell script?

Read it from an environment variable with $env:ELIDO_API_KEY, or store it with the SecretManagement module and fetch it with Get-Secret. A key pasted into a .ps1 file ends up in source control, in the console history, and in any transcript logging that is switched on.

How do I shorten a whole column of URLs from a CSV?

Import-Csv the file, pipe it through ForEach-Object -Parallel with -ThrottleLimit 8, and Export-Csv the result with the short link beside the original. The throttle is what keeps the run under the API rate limit; without it a large file starts every request at once.

Does ForEach-Object -Parallel work in Windows PowerShell 5.1?

No, it needs PowerShell 7 or later. On 5.1 the practical options are a plain foreach loop, which is fine for a few hundred rows, or runspace pools if the batch is large enough to justify the extra code.

Попробуйте Elido

Вставьте URL - получите короткую ссылку

Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.

Бесплатно, без регистрации · 2 в день

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф - без банковской карты.

Теги
how to shorten a url in powershell
powershell url shortener
invoke-restmethod post json
url shortener api powershell
powershell bulk shorten urls
powershell csv api script

Читать дальше