<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Script Notes]]></title><description><![CDATA[Script Notes]]></description><link>https://glaucco.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 12 Nov 2026 08:24:00 GMT</lastBuildDate><atom:link href="https://glaucco.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[PowerShell 5.1 fixes for a JSON API]]></title><description><![CDATA[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…]]></description><link>https://glaucco.hashnode.dev/powershell-5-1-fixes-for-a-json-api</link><guid isPermaLink="true">https://glaucco.hashnode.dev/powershell-5-1-fixes-for-a-json-api</guid><category><![CDATA[Powershell]]></category><category><![CDATA[api]]></category><category><![CDATA[json]]></category><dc:creator><![CDATA[Glaucco]]></dc:creator><pubDate>Sun, 08 Nov 2026 02:39:16 GMT</pubDate><content:encoded><![CDATA[<p>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 <code>x-access-key</code> 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.</p>
<h2>1. Decode the body as UTF-8 yourself</h2>
<p><code>Invoke-RestMethod</code> 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:</p>
<pre><code class="language-powershell">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 { $_ }
}
</code></pre>
<p>The <code>ForEach-Object { $_ }</code> at the end is the second fix hiding in there: <code>ConvertFrom-Json</code> in 5.1 hands back a JSON array as a single object, so <code>.Count</code> and loops quietly misbehave. Piping it through unrolls the list.</p>
<h2>2. Read a field by more than one name</h2>
<p>Once in a while a field came back with a slightly different name. The one I caught was <code>1ltaken_at</code> where I expected <code>taken_at</code>. Instead of trusting one exact key, I read fields through a helper that accepts a few candidates and a short prefix:</p>
<pre><code class="language-powershell">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
}
</code></pre>
<p><code>Campo $post 'taken_at'</code> now finds the value either way, and <code>Campo $post 'id', 'pk'</code> covers endpoints that name the same thing differently.</p>
<h2>3. Walk nested results instead of guessing the path</h2>
<p>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:</p>
<pre><code class="language-powershell">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 }
}
</code></pre>
<p>If the layout changes tomorrow, this keeps working as long as a post still looks like a post.</p>
<h2>About the API itself</h2>
<p>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 <code>/sys/balance</code> 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.</p>
<p>(The helper names are Portuguese because that's what the rest of my scripts use: <code>Campo</code> means field and <code>Midias</code> means media.)</p>
]]></content:encoded></item></channel></rss>