Skip to main content

PowerShell 5.1 fixes for a JSON API

Updated 3 min readMarkdown

I still use Windows PowerShell 5.1 for my personal research scripts, mostly because it's already on every machine I touch. When I added Instagram lookups through HikerAPI, the API itself was the easy part: one x-access-key header and plain JSON back. The work was in three small helpers that made the responses behave in 5.1. None of them is specific to HikerAPI, so here they are.

1. Decode the body as UTF-8 yourself

Invoke-RestMethod in 5.1 falls back to ISO-8859-1 when the server doesn't send a charset, and Instagram captions are full of emoji and accented letters. They arrived mangled until I read the raw bytes:

function GetJson($url, $headers) {
  $r = Invoke-WebRequest -UseBasicParsing -Uri $url -Headers $headers
  $ms = New-Object IO.MemoryStream; $r.RawContentStream.CopyTo($ms)
  (([Text.Encoding]::UTF8.GetString($ms.ToArray())) | ConvertFrom-Json) | ForEach-Object { $_ }
}

The ForEach-Object { $_ } at the end is the second fix hiding in there: ConvertFrom-Json in 5.1 hands back a JSON array as a single object, so .Count and loops quietly misbehave. Piping it through unrolls the list.

2. Read a field by more than one name

Once in a while a field came back with a slightly different name. The one I caught was 1ltaken_at where I expected taken_at. Instead of trusting one exact key, I read fields through a helper that accepts a few candidates and a short prefix:

function Campo($o, [string[]]$nomes) {
  if ($null -eq $o) { return $null }
  foreach ($n in $nomes) {
    $p = $o.PSObject.Properties | Where-Object { $_.Name -eq $n -or $_.Name -match ('^\w{0,3}' + [regex]::Escape($n) + '$') } | Select-Object -First 1
    if ($p -and $null -ne $p.Value) { return $p.Value }
  }
  return $null
}

Campo $post 'taken_at' now finds the value either way, and Campo $post 'id', 'pk' covers endpoints that name the same thing differently.

3. Walk nested results instead of guessing the path

Hashtag results come grouped in sections and layouts rather than as a flat list. Rather than hard-coding that shape, I walk the whole object and keep anything that looks like a post:

function Midias($o, $acc) {
  if ($null -eq $o -or $o -is [string] -or $o -is [ValueType]) { return }
  if ($o -is [Array]) { foreach ($x in $o) { Midias $x $acc }; return }
  $nomes = $o.PSObject.Properties.Name
  if ($nomes -contains 'code' -and ($nomes -contains 'like_count' -or $nomes -contains 'media_type')) { $acc.Add($o); return }
  foreach ($p in $o.PSObject.Properties) { Midias $p.Value $acc }
}

If the layout changes tomorrow, this keeps working as long as a post still looks like a post.

About the API itself

I'm happy with HikerAPI for this. You get 100 free requests when you sign up. The entry tier is $0.02 per request, and /sys/balance is free, so my script prints what each call cost. The quirks above took an afternoon to sort out, and since then it has been the dependable part of the pipeline.

(The helper names are Portuguese because that's what the rest of my scripts use: Campo means field and Midias means media.)

No comments yet. Be the first to comment.