<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SpecStream</title>
    <description>The latest articles on DEV Community by SpecStream (specstream).</description>
    <link>https://dev.to/specstream</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F15284%2F9ef76682-f625-41d0-8f1f-32cf96cb11f3.png</url>
      <title>DEV Community: SpecStream</title>
      <link>https://dev.to/specstream</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/specstream"/>
    <language>en</language>
    <item>
      <title>How to build a lead list of every roofer in a city, past the Google Maps cap</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Fri, 16 Oct 2026 04:54:07 +0000</pubDate>
      <link>https://dev.to/specstream/how-to-build-a-lead-list-of-every-roofer-in-a-city-past-the-google-maps-cap-38hh</link>
      <guid>https://dev.to/specstream/how-to-build-a-lead-list-of-every-roofer-in-a-city-past-the-google-maps-cap-38hh</guid>
      <description>&lt;p&gt;Search Google Maps for "roofers in Columbus, Ohio" and scroll. The list stops after roughly 120 places, sorted the way Google likes: the big outfits with 400 reviews first. Those are the roofers who already have an agency.&lt;/p&gt;

&lt;p&gt;The ones worth calling are further down: rated 5.0 with eight reviews, small enough that the owner picks up. Nobody scrolls to result 121, and Google made sure you can't.&lt;/p&gt;

&lt;p&gt;So here's the list I wanted:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Roofers in Columbus, Ohio, rated 4.5 or better with fewer than 20 Google reviews, with their phone number and website&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here's how to get every match as a CSV with about 80 lines of Python, then the same thing as an n8n workflow and as a question to your agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;You need an API key from &lt;a href="https://specstream.ai" rel="noopener noreferrer"&gt;specstream.ai&lt;/a&gt; (under &lt;strong&gt;Agents &amp;amp; API&lt;/strong&gt;). New accounts get $10 of work free, no card.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.specstream.ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Roofers in Columbus, Ohio, rated 4.5 or better with fewer than 20 &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Google reviews, with their phone number and website&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;HOW_MANY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;MAX_CENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;  &lt;span class="c1"&gt;# never approve more than $10
&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPECSTREAM_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# retries are safe: the approval below carries a fixed key
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# 1. Read the sentence. Free.
&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/parse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read as:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canonical_sentence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Get the price. Also free.
&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requested_units&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HOW_MANY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Costs at most $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_CENTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Over budget. Ask for fewer.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Approve that price. The only call that spends.
&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/executions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confirm_max_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;idempotency_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roofers-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;find_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# 4. Wait for the run to end, then read every page of rows.
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queued&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;planning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cursor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;next_cursor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="c1"&gt;# 5. One CSV row per roofer, with the page it came from.
&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# workspace buttons, not facts
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roofers-columbus.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cells&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;column_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provenance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provenance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;absence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;display&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; roofers in roofers-columbus.csv (list &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPECSTREAM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
python roofers.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What came back
&lt;/h2&gt;

&lt;p&gt;The run took about two minutes. The quote said $7.50 at most: 30¢ a roofer, because every row carries a rating, a review count, a phone and a website. A plain list with just phone and website is 10¢ a row; 25 HVAC companies in Columbus quoted at $2.50.&lt;/p&gt;

&lt;p&gt;Ten of the 25 rows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Reviews&lt;/th&gt;
&lt;th&gt;Phone&lt;/th&gt;
&lt;th&gt;Website&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blue Sky Roofing Columbus&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(380) 286-0409&lt;/td&gt;
&lt;td&gt;blueskyroofing.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ohio Metal Roofing&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;(740) 751-8131&lt;/td&gt;
&lt;td&gt;metalroofohio.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Columbus Commercial Roofing&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;(614) 392-8717&lt;/td&gt;
&lt;td&gt;columbuscommercialroofingco.net&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MasterRoof Contracting Columbus&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;(380) 444-9147&lt;/td&gt;
&lt;td&gt;masterroofcontracting.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rain Dog Roofing of Columbus&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;(614) 330-7887&lt;/td&gt;
&lt;td&gt;raindogroofing.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top Roofing &amp;amp; Windows&lt;/td&gt;
&lt;td&gt;4.9&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;(614) 310-4521&lt;/td&gt;
&lt;td&gt;toproofingwindows.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RX Roofing&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;(614) 569-6977&lt;/td&gt;
&lt;td&gt;rxroofing.org&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Walker Roofing &amp;amp; Construction&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;(614) 802-5428&lt;/td&gt;
&lt;td&gt;walkerroofingandconstruction.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ace Roofing&lt;/td&gt;
&lt;td&gt;4.9&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;(614) 457-1111&lt;/td&gt;
&lt;td&gt;roofingace.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grants Roofing Company&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;(614) 905-2183&lt;/td&gt;
&lt;td&gt;grantsroofingohio.com&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every one of the 25 is rated 4.6 or better with between 1 and 17 reviews. The CSV also has the street address where Maps lists one, and a &lt;strong&gt;Source&lt;/strong&gt; column with the page each row was read from, so you can click any row and see it for yourself.&lt;/p&gt;

&lt;p&gt;No emails in this list, because I didn't ask for them. Add "and their email" to the sentence and you get the ones each roofer published. Where a business didn't publish one, the cell stays empty. Nothing is guessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this gets past the cap
&lt;/h2&gt;

&lt;p&gt;The 120-place limit belongs to a single Maps search. SpecStream doesn't run one search. It keeps looking where local businesses are listed until it has the number you asked for, up to 10,000, and every row it hands back fits the whole sentence. Those are the only rows you pay for.&lt;/p&gt;

&lt;p&gt;That changes what the sentence can be. "Roofers in Columbus" is a search. "Roofers in Columbus rated 4.5 or better with fewer than 20 reviews" is a list nobody sells, and it's the one an agency wants.&lt;/p&gt;

&lt;p&gt;Swap the sentence and the script still works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"HVAC companies in Columbus, Ohio, with their phone number and website"&lt;/li&gt;
&lt;li&gt;"Plumbers in Dayton, Ohio, with their phone number and website"&lt;/li&gt;
&lt;li&gt;"Web design agencies in Austin, Texas, with their website, phone number and email"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keep it current
&lt;/h2&gt;

&lt;p&gt;A list of businesses with fewer than 20 reviews goes stale on its own: some of them hit 20. And new roofers open every month. To get only the new ones, quote an &lt;code&gt;extend&lt;/code&gt; on the list you already have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parent_find_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requested_units&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Approve it the same way. You're billed only for businesses the list doesn't hold yet. When we asked for 5 more web design agencies in Austin on a list of 10, all 5 were new. To have this happen on its own, open the list in the workspace and turn on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Without code: n8n
&lt;/h2&gt;

&lt;p&gt;The same five steps are an n8n workflow: a form for the trade and the city, the price check against your maximum, the wait loop, and a Google Sheet at the end. It uses only core nodes (HTTP Request, Code, Google Sheets), so it runs on n8n Cloud. Import it from the &lt;a href="https://n8n.io/creators/specstream/" rel="noopener noreferrer"&gt;SpecStream templates on n8n.io&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Or ask your agent
&lt;/h2&gt;

&lt;p&gt;SpecStream is also a remote MCP server at &lt;code&gt;https://api.specstream.ai/mcp&lt;/code&gt; (OAuth, or your API key as a Bearer token). Add it to Claude, ChatGPT, Cursor or VS Code and ask in plain words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every roofer in Columbus rated 4.5 or better with fewer than 20 Google reviews, with phone and website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent reads the request back, shows you the price and waits for your yes before anything runs. The &lt;a href="https://specstream.ai/docs/mcp" rel="noopener noreferrer"&gt;MCP guide&lt;/a&gt; has the setup for each client.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;10¢, 30¢ or $1 a match, depending on how much each row has to carry, and you see the maximum before you approve it. A run that finds nothing is free. The $10 you start with covers both lists above, to the cent. Rates are on the &lt;a href="https://specstream.ai/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you build lists for local outreach: what's the condition that makes a lead worth calling for you? Reviews, opening date, no website, a specific neighborhood? Tell me in the comments and I'll run one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We build SpecStream. The rows above are from a real run on October 15, 2026 (list &lt;code&gt;fnd_0j95yw0w8ypsqqhvj3k9dern02&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>mcp</category>
      <category>leadgeneration</category>
    </item>
    <item>
      <title>FDIC failed bank list 2026: six banks closed this year, and the 25 latest as a list you can sort</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Thu, 15 Oct 2026 14:07:21 +0000</pubDate>
      <link>https://dev.to/specstream/fdic-failed-bank-list-2026-six-banks-closed-this-year-and-the-25-latest-as-a-list-you-can-sort-4e80</link>
      <guid>https://dev.to/specstream/fdic-failed-bank-list-2026-six-banks-closed-this-year-and-the-25-latest-as-a-list-you-can-sort-4e80</guid>
      <description>&lt;p&gt;Six US banks have failed so far in 2026. The last one, Nano Banc in Irvine, California, closed on September 25 and Sunwest Bank took its deposits. That's more failures than in any full year from 2018 through 2025, and the FDIC's &lt;a href="https://www.fdic.gov/bank-failures/failed-bank-list" rel="noopener noreferrer"&gt;failed bank list&lt;/a&gt; won't tell you so: it publishes failures one row at a time and leaves the counting to you.&lt;/p&gt;

&lt;p&gt;We copied three rows from that page and let SpecStream read the rest. &lt;a href="https://specstream.ai/workspace/shared/BfjMCn64al-fpbxCemdQO6Coc-PNHEiilAP6rZUzMH4?src=devto" rel="noopener noreferrer"&gt;The 25 most recent failures are here, no account needed.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The latest ten
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bank&lt;/th&gt;
&lt;th&gt;City&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Closed&lt;/th&gt;
&lt;th&gt;Deposits went to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nano Banc&lt;/td&gt;
&lt;td&gt;Irvine&lt;/td&gt;
&lt;td&gt;California&lt;/td&gt;
&lt;td&gt;September 25, 2026&lt;/td&gt;
&lt;td&gt;Sunwest Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tioga-Franklin Savings Bank&lt;/td&gt;
&lt;td&gt;Philadelphia&lt;/td&gt;
&lt;td&gt;Pennsylvania&lt;/td&gt;
&lt;td&gt;August 21, 2026&lt;/td&gt;
&lt;td&gt;Second Federal Savings and Loan Association of Philadelphia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small Business Bank&lt;/td&gt;
&lt;td&gt;Lenexa&lt;/td&gt;
&lt;td&gt;Kansas&lt;/td&gt;
&lt;td&gt;July 17, 2026&lt;/td&gt;
&lt;td&gt;The Farmers State Bank of Oakley, Kansas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kentland Federal Savings and Loan Association&lt;/td&gt;
&lt;td&gt;Kentland&lt;/td&gt;
&lt;td&gt;Indiana&lt;/td&gt;
&lt;td&gt;July 10, 2026&lt;/td&gt;
&lt;td&gt;Kentland Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community Bank and Trust - West Georgia&lt;/td&gt;
&lt;td&gt;LaGrange&lt;/td&gt;
&lt;td&gt;Georgia&lt;/td&gt;
&lt;td&gt;May 1, 2026&lt;/td&gt;
&lt;td&gt;Anchor Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metropolitan Capital Bank &amp;amp; Trust&lt;/td&gt;
&lt;td&gt;Chicago&lt;/td&gt;
&lt;td&gt;Illinois&lt;/td&gt;
&lt;td&gt;January 30, 2026&lt;/td&gt;
&lt;td&gt;First Independence Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Santa Anna National Bank&lt;/td&gt;
&lt;td&gt;Santa Anna&lt;/td&gt;
&lt;td&gt;Texas&lt;/td&gt;
&lt;td&gt;June 27, 2025&lt;/td&gt;
&lt;td&gt;Coleman County State Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pulaski Savings Bank&lt;/td&gt;
&lt;td&gt;Chicago&lt;/td&gt;
&lt;td&gt;Illinois&lt;/td&gt;
&lt;td&gt;January 17, 2025&lt;/td&gt;
&lt;td&gt;Millennium Bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The First National Bank of Lindsay&lt;/td&gt;
&lt;td&gt;Lindsay&lt;/td&gt;
&lt;td&gt;Oklahoma&lt;/td&gt;
&lt;td&gt;October 18, 2024&lt;/td&gt;
&lt;td&gt;First Bank &amp;amp; Trust Co.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Republic First Bank dba Republic Bank&lt;/td&gt;
&lt;td&gt;Philadelphia&lt;/td&gt;
&lt;td&gt;Pennsylvania&lt;/td&gt;
&lt;td&gt;April 26, 2024&lt;/td&gt;
&lt;td&gt;Fulton Bank, National Association&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the 25 say
&lt;/h2&gt;

&lt;p&gt;The list runs from the Farmers and Merchants State Bank of Argonia, Kansas, closed October 13, 2017, to Nano Banc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2026 is the busy year.&lt;/strong&gt; Six failures through September: Metropolitan Capital Bank &amp;amp; Trust in Chicago (January), Community Bank and Trust - West Georgia (May), Kentland Federal Savings and Loan in Indiana and Small Business Bank in Lenexa, Kansas (a week apart in July), Tioga-Franklin Savings Bank in Philadelphia (August) and Nano Banc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The quiet years were very quiet.&lt;/strong&gt; No failures at all in 2018, 2021 or 2022. Two each in 2024 and 2025.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2023 is the one everyone remembers,&lt;/strong&gt; and the list shows why in three rows: Silicon Valley Bank on March 10, Signature Bank two days later, First Republic on May 1, taken by JPMorgan Chase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kansas has the most failures on the list:&lt;/strong&gt; four, all small-town or suburban banks. Chicago has three, more than any other city. Philadelphia has two.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every failure has a buyer.&lt;/strong&gt; In all 25, another bank took the deposits, so the last column doubles as a list of who buys failed banks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Page one of 24
&lt;/h2&gt;

&lt;p&gt;The FDIC's page shows 25 failures at a time, and its full history runs 24 pages back to 2000. Teaching reads one page, so this list is page one: the 25 most recent. When the next failure posts, read the page again and the newest bank is in the preview; the list you already have stays as it was.&lt;/p&gt;

&lt;p&gt;If you want every failure since 2000, the FDIC offers the whole table as a CSV on the same page. Import that into the workspace (up to 20,000 rows) and the rest works the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  How three rows became 25
&lt;/h2&gt;

&lt;p&gt;Teaching SpecStream a page takes three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The page's address.&lt;/strong&gt; One public page, up to 2 MB and 5,000 records. Pages behind a sign-in aren't opened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three to eight rows copied from it,&lt;/strong&gt; with a header: bank name, city, state, closing date, acquiring institution. Copy each value the way the page shows it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The column that names each record.&lt;/strong&gt; Here, the bank.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It reads the rest of the page into the same columns and shows you a preview before anything is saved. Teaching a page costs nothing, and neither does reading it again.&lt;/p&gt;

&lt;p&gt;The same move worked on &lt;a href="https://dev.to/specstream/maryland-warn-notices-2026-all-108-layoffs-as-a-list-you-can-sort-27cm"&gt;Maryland's WARN layoff notices&lt;/a&gt;: three rows, 108 notices back. Any public page that lays its records out as a table is a list waiting to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deposit and treasury teams&lt;/strong&gt; check counterparties against it before moving money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fintech compliance teams&lt;/strong&gt; watch for their partner banks, and for the banks that tend to buy failed ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporters and analysts&lt;/strong&gt; count by year and state without retyping a table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community bank boards and acquirers&lt;/strong&gt; see which banks took on failed franchises, and where.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once it's a list, the rest of the workspace works on it: put the banks on a map by city (placing addresses is free), ask for each acquirer's website and headquarters (that part is a find, priced per match, with the price shown before it runs), or send it to a Google Sheet, Airtable, HubSpot or Slack.&lt;/p&gt;

&lt;p&gt;Teaching a page is free in the &lt;a href="https://specstream.ai/workspace/import?src=devto" rel="noopener noreferrer"&gt;SpecStream workspace&lt;/a&gt;: the page's address, three rows, and the column that names each record.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The rows above are from the FDIC's page as read on October 15, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>fintech</category>
      <category>data</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Selling wholesale to bike shops: 91 in Colorado rated 4.5+, on a map, from one sentence</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Thu, 15 Oct 2026 13:46:12 +0000</pubDate>
      <link>https://dev.to/specstream/selling-wholesale-to-bike-shops-91-in-colorado-rated-45-on-a-map-from-one-sentence-80h</link>
      <guid>https://dev.to/specstream/selling-wholesale-to-bike-shops-91-in-colorado-rated-45-on-a-map-from-one-sentence-80h</guid>
      <description>&lt;p&gt;If you sell a product into bike shops, your best week of the quarter is a road trip: a car full of samples, a shop every hour, a buyer who can hold the thing. The worst part is the night before, with forty tabs of map results, trying to tell the shop that's still open and well-liked from the one that closed in 2023, then typing addresses into a route one at a time.&lt;/p&gt;

&lt;p&gt;Here's the night before, done from one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;100 bike shops in Colorado rated 4.5 or better on Google, with phone number and website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It came back with 91. &lt;a href="https://specstream.ai/workspace/shared/-YCXVGEAYsLc15VTzNnaCdrHSlo-P5rAfymg9G4x-Ws?src=devto" rel="noopener noreferrer"&gt;The whole list is here, no account needed.&lt;/a&gt; Every row has the shop's name, address, rating and review count, phone and website.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first ten
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shop&lt;/th&gt;
&lt;th&gt;Address&lt;/th&gt;
&lt;th&gt;Rating · reviews&lt;/th&gt;
&lt;th&gt;Phone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Adventure Cycling in Aurora&lt;/td&gt;
&lt;td&gt;4361 S Parker Rd, Aurora, CO 80015&lt;/td&gt;
&lt;td&gt;4.6 · 390&lt;/td&gt;
&lt;td&gt;(303) 699-2514&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sun &amp;amp; Ski Sports&lt;/td&gt;
&lt;td&gt;761-A Anemone Trail, Dillon, CO 80435&lt;/td&gt;
&lt;td&gt;4.8 · 331&lt;/td&gt;
&lt;td&gt;(970) 409-3347&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-Culture Cyclery&lt;/td&gt;
&lt;td&gt;129 G St, Salida, CO 81201&lt;/td&gt;
&lt;td&gt;4.8 · 192&lt;/td&gt;
&lt;td&gt;(719) 539-5329&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Country Pedaler Bicycles &amp;amp; Ski&lt;/td&gt;
&lt;td&gt;205 Fifth St, Castle Rock, CO 80104&lt;/td&gt;
&lt;td&gt;4.8 · 214&lt;/td&gt;
&lt;td&gt;(303) 688-6775&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kind Bikes and Skis&lt;/td&gt;
&lt;td&gt;295 Main Street, Edwards, CO 81632&lt;/td&gt;
&lt;td&gt;4.5 · 94&lt;/td&gt;
&lt;td&gt;(970) 926-1260&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excel Sports&lt;/td&gt;
&lt;td&gt;2045 32nd St, Boulder, CO 80301&lt;/td&gt;
&lt;td&gt;4.5 · 153&lt;/td&gt;
&lt;td&gt;(303) 444-6737&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sports Plus&lt;/td&gt;
&lt;td&gt;1055 S Gaylord St, Denver, CO 80209&lt;/td&gt;
&lt;td&gt;4.6 · 137&lt;/td&gt;
&lt;td&gt;(303) 777-6613&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sun &amp;amp; Ski Sports&lt;/td&gt;
&lt;td&gt;218 Beaver Creek Pl Ste 9635, Avon, CO 81620&lt;/td&gt;
&lt;td&gt;4.5 · 253&lt;/td&gt;
&lt;td&gt;(970) 445-3105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freeride Bike Shop&lt;/td&gt;
&lt;td&gt;1800 Colorado Blvd Ste 5, Idaho Springs, CO 80452&lt;/td&gt;
&lt;td&gt;5 · 30&lt;/td&gt;
&lt;td&gt;(720) 608-0718&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old Town Bike Shop&lt;/td&gt;
&lt;td&gt;426 S Tejon St, Colorado Springs, CO 80903&lt;/td&gt;
&lt;td&gt;4.7 · 307&lt;/td&gt;
&lt;td&gt;(719) 475-8589&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's in the 91
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;41 towns.&lt;/strong&gt; Denver has 13 shops on the list and Colorado Springs 10. Boulder, Golden and Littleton have 4 each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The mountains matter.&lt;/strong&gt; Avon has 5, Edwards and Leadville 3 each, Dillon and Idaho Springs 2 each: a run of well-rated shops along I-70 that a Front Range-only plan would miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bar is high and most clear it easily.&lt;/strong&gt; 37 of the 91 are rated 4.9 or 5.0. The median shop has 175 reviews; Wheat Ridge Cyclery has 1,965.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You asked for 100 and got 91 because only 91 met the 4.5 bar. You pay for the 91.&lt;/p&gt;

&lt;h2&gt;
  
  
  The list is a map first
&lt;/h2&gt;

&lt;p&gt;A stockist list in a spreadsheet makes you do the geography in your head. In the workspace, any list with addresses opens as a map, and placing the addresses costs nothing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pins or heat.&lt;/strong&gt; Pins to pick shops, heat to see where they bunch up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Color by any column.&lt;/strong&gt; Color by rating and the 5.0 shops stand out from the 4.5s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The visible area is the filter.&lt;/strong&gt; Zoom to the stretch you'll drive and the table shrinks to the shops on screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A route.&lt;/strong&gt; Pick up to 11 stops and open the route in Google Maps on your phone. Denver to Golden to Idaho Springs to Dillon to Avon is one day's drive with five buyers in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KML for the rep.&lt;/strong&gt; Export the list as KML or GeoJSON and it opens in Google Earth or My Maps with every pin and its phone number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shared link above shows the list as a table. Run the sentence in your own workspace and the map, the route and the exports come with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it current
&lt;/h2&gt;

&lt;p&gt;Shops open, close and change hands. Save the sentence and it runs again on the schedule you pick, from hourly to yearly, bringing in what changed since the last run, in the same columns. It can land in a Google Sheet, HubSpot, Salesforce or your inbox. Free keeps one list current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the sentence, not a filter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Another state or a metro:&lt;/strong&gt; "...bike shops within 50 miles of Portland, Oregon..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Another category:&lt;/strong&gt; "...running stores in Texas rated 4.5 or better...", "...toy stores in New England..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More per row:&lt;/strong&gt; "...with the owner's name and the brands they carry." More facts per row move a list to a higher rate; you see it before anything runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;A list with these columns is the 10¢ kind: the 91 rows above cost $9.10, and you see that maximum before you approve it. Your first $10 of work is free, once, no card. A run that finds nothing costs nothing. &lt;a href="https://specstream.ai/pricing?src=devto" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The rows above are from a real run on October 15, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>sales</category>
      <category>maps</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Maryland WARN notices 2026: all 108 layoffs as a list you can sort</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Thu, 15 Oct 2026 08:23:04 +0000</pubDate>
      <link>https://dev.to/specstream/maryland-warn-notices-2026-all-108-layoffs-as-a-list-you-can-sort-27cm</link>
      <guid>https://dev.to/specstream/maryland-warn-notices-2026-all-108-layoffs-as-a-list-you-can-sort-27cm</guid>
      <description>&lt;p&gt;Maryland publishes every layoff notice it gets on &lt;a href="https://labor.maryland.gov/employment/warn.shtml" rel="noopener noreferrer"&gt;one web page&lt;/a&gt;. It's a table, it's public, it's current, and you can't do anything with it. No sort, no filter, no download. To answer "which county lost the most jobs this year," you scroll, copy, paste into a spreadsheet, fix the columns, and do it again when the next notice lands.&lt;/p&gt;

&lt;p&gt;So we copied three rows instead and let SpecStream read the rest. &lt;a href="https://specstream.ai/workspace/shared/PmmY7o64mBHllxTabdSeN1xeKa33VVWlKYIgZbcf_LQ?src=devto" rel="noopener noreferrer"&gt;The whole list is here: all 108 notices, no account needed.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 108 notices say
&lt;/h2&gt;

&lt;p&gt;From January 2 to October 7, 2026, Maryland received 108 WARN notices covering 7,137 workers. The typical notice is small: the median is 39 workers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Montgomery County leads by a distance.&lt;/strong&gt; 29 notices and 2,305 workers, about a third of the state's total. Baltimore County is next with 12 notices and 959 workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The biggest single notice is a closure.&lt;/strong&gt; Diamond Transportation Services in Prince George's County, 571 workers, filed May 13.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closures are common.&lt;/strong&gt; 42 of the 108 are plant closures; most of the rest are mass layoffs with no recall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some employers file again and again.&lt;/strong&gt; Amentum and General Dynamics Information Technology filed four notices each this year, Saks three, Leidos two.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The busiest stretch was early.&lt;/strong&gt; March had 18 notices, the most of any month.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The ten largest
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;County&lt;/th&gt;
&lt;th&gt;Workers&lt;/th&gt;
&lt;th&gt;Notice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Diamond Transportation Services, Inc.&lt;/td&gt;
&lt;td&gt;Prince George's&lt;/td&gt;
&lt;td&gt;571&lt;/td&gt;
&lt;td&gt;May 13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Republic National Distributing Company, LLC&lt;/td&gt;
&lt;td&gt;Howard County&lt;/td&gt;
&lt;td&gt;318&lt;/td&gt;
&lt;td&gt;Apr 23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amentum&lt;/td&gt;
&lt;td&gt;Anne Arundel County&lt;/td&gt;
&lt;td&gt;317&lt;/td&gt;
&lt;td&gt;Aug 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EchoStar Corporation (Germantown EXP facility)&lt;/td&gt;
&lt;td&gt;Montgomery County&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;Jul 24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZeniMax Online Studios&lt;/td&gt;
&lt;td&gt;Baltimore County&lt;/td&gt;
&lt;td&gt;213&lt;/td&gt;
&lt;td&gt;Jul 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon - MAV2 facility&lt;/td&gt;
&lt;td&gt;Montgomery County&lt;/td&gt;
&lt;td&gt;184&lt;/td&gt;
&lt;td&gt;Jan 28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aramark Campus, LLC&lt;/td&gt;
&lt;td&gt;Baltimore County&lt;/td&gt;
&lt;td&gt;174&lt;/td&gt;
&lt;td&gt;Oct 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBRE Government &amp;amp; Defense Services&lt;/td&gt;
&lt;td&gt;Montgomery County&lt;/td&gt;
&lt;td&gt;174&lt;/td&gt;
&lt;td&gt;Apr 29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZeniMax Media Inc.&lt;/td&gt;
&lt;td&gt;Montgomery County&lt;/td&gt;
&lt;td&gt;166&lt;/td&gt;
&lt;td&gt;Jul 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sibley- Suburban Home Health Agency, Inc. d/b/a Potomac Home Health Care and Potomac Home Support, Inc.&lt;/td&gt;
&lt;td&gt;Montgomery County&lt;/td&gt;
&lt;td&gt;163&lt;/td&gt;
&lt;td&gt;May 6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the full list, the type column is the state's own text, kept as it was typed: "Mass Layoff - No Recall" shows up in three spellings, and nine notices carry a note in the same cell ("Performing work at..."). A list read from a page keeps the page's words. That's the point: every row is what Maryland published, not our summary of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How three rows became 108
&lt;/h2&gt;

&lt;p&gt;The WARN Act makes employers with 100 or more workers give 60 days' notice before a plant closing or mass layoff, and each state decides how to publish what it gets. Maryland's choice is an HTML table. That's the easy case.&lt;/p&gt;

&lt;p&gt;Teaching SpecStream a page takes three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The page's address.&lt;/strong&gt; One public page, up to 2 MB and 5,000 records. Pages behind a sign-in aren't opened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three to eight rows copied from it,&lt;/strong&gt; with a header: notice date, company, location, county, employees, type. Copy each value the way the page shows it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The column that names each record.&lt;/strong&gt; Here, the company.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It reads the rest of the page into the same columns and shows you a preview before anything is saved. Teaching a page costs nothing, and neither does reading it again. When the next notice posts, read the page again and you get a new preview; the list you already have stays as it was.&lt;/p&gt;

&lt;p&gt;The same three rows work on any page that lays its records out the same way. &lt;a href="https://specstream.ai/workspace/shared/BfjMCn64al-fpbxCemdQO6Coc-PNHEiilAP6rZUzMH4?src=devto" rel="noopener noreferrer"&gt;The FDIC's failed-bank list&lt;/a&gt; went the same way: three rows copied, 25 bank failures back, from Nano Banc in Irvine in September 2026 to the Farmers and Merchants State Bank of Argonia in 2017, each with city, state, closing date and the bank that took the deposits.&lt;/p&gt;

&lt;p&gt;It's not for every state. If yours publishes WARN notices as PDFs or a spreadsheet download, this isn't the tool for that file. If it publishes a web page with a table, it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with it
&lt;/h2&gt;

&lt;p&gt;A list of layoffs is a list of people about to look for work and companies about to change shape. Recruiters call the affected employers' teams. Workforce boards plan sessions by county. Reporters track who's cutting and where. Commercial real estate brokers watch the closures.&lt;/p&gt;

&lt;p&gt;Once the notices are a list, the rest of the workspace works on them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On a map by county.&lt;/strong&gt; Placing the addresses on the map is free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With more facts per row.&lt;/strong&gt; Ask for each company's website or its other locations; that part is a find, priced per match, with the price shown before it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where you work.&lt;/strong&gt; Send it to a Google Sheet, Airtable, HubSpot or Slack, or download CSV or Excel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Teaching a page is free in the &lt;a href="https://specstream.ai/workspace/import?src=devto" rel="noopener noreferrer"&gt;SpecStream workspace&lt;/a&gt;: the page's address, three rows, and the column that names each record.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The rows above are from the state's page as read on October 15, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>webscraping</category>
      <category>data</category>
      <category>datascience</category>
    </item>
    <item>
      <title>A list of MCP servers (100 active ones), and why your agent needs a list, not ten pages</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Thu, 15 Oct 2026 03:27:10 +0000</pubDate>
      <link>https://dev.to/specstream/a-list-of-mcp-servers-100-active-ones-and-why-your-agent-needs-a-list-not-ten-pages-4bi8</link>
      <guid>https://dev.to/specstream/a-list-of-mcp-servers-100-active-ones-and-why-your-agent-needs-a-list-not-ten-pages-4bi8</guid>
      <description>&lt;p&gt;Ask an agent for "every MCP server on GitHub with more than 500 stars that was pushed to in the last month" and watch what happens. It searches. It opens ten pages. It reads ten READMEs, hits the context limit around the time it gets interesting, and hands you a tidy list of ten. You asked for all of them.&lt;/p&gt;

&lt;p&gt;That isn't the agent being lazy. It was given page tools, and a page tool returns pages. Reading them, remembering them and turning them into rows is the agent's job, one page at a time, on a meter that runs per call.&lt;/p&gt;

&lt;p&gt;A list tool takes the sentence and returns the rows. Here is one, asked over MCP:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;100 MCP servers on GitHub with more than 500 stars, pushed to in the last month, with their star count and language&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://specstream.ai/workspace/shared/ylRQkZQDtwgsX9DXAh0CpYO1-yIWWPYZGAm0smP9VAQ?src=devto" rel="noopener noreferrer"&gt;The 100 rows are here, no account needed.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten of the 100
&lt;/h2&gt;

&lt;p&gt;Star counts and push dates are as of the run on October 8, 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Last push&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;codebase-memory-mcp&lt;/td&gt;
&lt;td&gt;46,159&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;2026-10-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-mcp&lt;/td&gt;
&lt;td&gt;37,933&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;github-mcp-server&lt;/td&gt;
&lt;td&gt;33,449&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mcp-toolbox&lt;/td&gt;
&lt;td&gt;16,624&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma-Context-MCP&lt;/td&gt;
&lt;td&gt;15,964&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;2026-10-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DesktopCommanderMCP&lt;/td&gt;
&lt;td&gt;9,968&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows-MCP&lt;/td&gt;
&lt;td&gt;8,308&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mcp-atlassian&lt;/td&gt;
&lt;td&gt;5,979&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;2026-09-19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mcp-grafana&lt;/td&gt;
&lt;td&gt;3,535&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;arxiv-mcp-server&lt;/td&gt;
&lt;td&gt;3,202&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;2026-10-08&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The whole list, in numbers: 41 TypeScript, 33 Python, 7 Go, 6 JavaScript, and a handful of C#, Java and C. Stars run from 506 to 47,362. Nine repos have more than 10,000 stars and 32 have fewer than 1,000. Seventy-six were pushed to on or after October 1. Every row carries a one-line reason it's on the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the list is wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The biggest repo on it is not a server.&lt;/strong&gt; The top row, &lt;code&gt;agentic-awesome-skills&lt;/code&gt; at 47,362 stars, reads as a skills collection whose description mentions a local MCP. The reading of "MCP server" let it in. Two official SDKs, for Python and TypeScript, got in the same way: they build servers, they aren't servers. So did a Laravel package for building them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Five repos are named just "mcp."&lt;/strong&gt; A name column alone can't tell them apart. The reason on each row names the owner (Microsoft, Cloudflare, Laravel).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of thing you tighten in the sentence, not in a filter you maintain. Add "excluding SDKs and collections of skills or examples" and run it again. When the list is wrong, you say what's wrong in words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page tools and list tools
&lt;/h2&gt;

&lt;p&gt;Neither is better. They answer different questions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You want&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One fact, or one page read properly&lt;/td&gt;
&lt;td&gt;A page tool. Search, fetch, read.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many rows that all meet the same conditions&lt;/td&gt;
&lt;td&gt;A list tool.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The page behind row 147&lt;/td&gt;
&lt;td&gt;A list tool that keeps one per row.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The same list next month, with only what changed&lt;/td&gt;
&lt;td&gt;A list tool that can run on a schedule.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Page tools are priced by the call, and the call count is whatever the agent decides it needs. A list tool quotes the most it can cost before it starts: 10¢, 30¢ or $1 a match depending on how much each row carries, and only rows that meet every part of the request are charged. A run that finds nothing is free.&lt;/p&gt;

&lt;p&gt;There's also a context problem. A long run keeps working until it reaches the count, up to four hours on the largest lists. Your agent's context window runs out long before row 300. The run doesn't depend on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One line of setup
&lt;/h2&gt;

&lt;p&gt;SpecStream is a remote MCP server at &lt;code&gt;https://api.specstream.ai/mcp&lt;/code&gt;. In Claude Code, with an API key from &lt;strong&gt;Agents &amp;amp; API&lt;/strong&gt; in the workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http specstream https://api.specstream.ai/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_SPECSTREAM_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cursor, VS Code, ChatGPT and Perplexity have their own steps in the &lt;a href="https://specstream.ai/docs/mcp/clients?src=devto" rel="noopener noreferrer"&gt;setup guides&lt;/a&gt;, and the &lt;a href="https://specstream.ai/docs/mcp?src=devto" rel="noopener noreferrer"&gt;MCP docs&lt;/a&gt; cover OAuth sign-in if you'd rather skip the key. It's 97 tools; your client finds them with &lt;code&gt;tools/list&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then ask in the sentence above. The agent works through four tools, and you only see the one that spends:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;compile&lt;/code&gt; reads the request into editable parts and shows you anything ambiguous.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quote_capacity_work&lt;/code&gt; returns the price for that exact request. Quotes last 30 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;authorize_capacity_work&lt;/code&gt; runs it. It takes the quote, the maximum you approved and a stable key, so a retry never buys the same list twice.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_find_rows&lt;/code&gt; returns the rows, each with its source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reading and pricing cost nothing. Only the third call spends, and never past the approved maximum.&lt;/p&gt;

&lt;p&gt;A key can be made small. Give it read, prepare or execute scope, limit it to the projects the task needs, and put a spending ceiling and an expiry on it. An agent that can only read can't spend. An API key is not a blank check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents that pay for themselves
&lt;/h2&gt;

&lt;p&gt;Sometimes there's no account and no person to approve. The agent has a wallet and a task. For that case there's a front door that takes a machine payment instead of a key.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;POST https://api.specstream.ai/v1/agent/lists&lt;/code&gt; with &lt;code&gt;{"request": "&amp;lt;the list in plain words&amp;gt;", "count": 100}&lt;/code&gt; (count is 1 to 500).&lt;/li&gt;
&lt;li&gt;You get &lt;code&gt;402 Payment Required&lt;/code&gt;. The body shows how the request was read, the columns and the most it can cost.&lt;/li&gt;
&lt;li&gt;Pay with a card through Stripe's Link agent wallet (MPP), or in USDC on Base, Arbitrum or Polygon with x402. Send the same request again with the payment attached.&lt;/li&gt;
&lt;li&gt;You get a &lt;code&gt;202&lt;/code&gt; with a status URL. The rows are there when the run ends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You pay for the results found. With x402, the agent signs for the most it can cost, nothing moves at signing, and only what the run found is taken when it ends. The longer write-up is in &lt;a href="https://specstream.ai/agents.md?src=devto" rel="noopener noreferrer"&gt;agents.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it current
&lt;/h2&gt;

&lt;p&gt;Repos get abandoned. "Pushed to in the last month" is true on one day and false on another. Save the request and it reruns on a cadence from hourly to yearly, inside a per-run and per-period spending limit. Rechecks cost half. New matches go to Slack, a sheet or a webhook, and the ones that stopped matching are labeled. Your agent can set that up through the same server. &lt;a href="https://specstream.ai/docs/schedules?src=devto" rel="noopener noreferrer"&gt;Schedules docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;You start with $10 of work free, once, no card. After that it's pay as you go from $10, or a plan from $19 a month that turns its full price into work. Same rates everywhere, and the maximum is on screen before anything runs. &lt;a href="https://specstream.ai/pricing?src=devto" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt;. The &lt;a href="https://specstream.ai/docs/api?src=devto" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; have the same flow over HTTP if your agent speaks REST better than MCP.&lt;/p&gt;

&lt;p&gt;If you build agents: which list does yours ask for most that it currently gets back as ten pages? Companies, repos, tenders, papers? Tell me in the comments and I'll run it and post the rows.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We build SpecStream. The rows above are from a real run on October 8, 2026 (list &lt;code&gt;fnd_0nc2sga086d53whbkc7jsgb6je&lt;/code&gt;). This post first appeared on the &lt;a href="https://specstream.ai/blog/list-of-mcp-servers?src=devto" rel="noopener noreferrer"&gt;SpecStream blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>agents</category>
      <category>api</category>
    </item>
    <item>
      <title>Grants.gov alerts that don't miss: every open federal grant tribes can apply for, from one sentence</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Thu, 15 Oct 2026 03:25:35 +0000</pubDate>
      <link>https://dev.to/specstream/grantsgov-alerts-that-dont-miss-every-open-federal-grant-tribes-can-apply-for-from-one-sentence-5dg3</link>
      <guid>https://dev.to/specstream/grantsgov-alerts-that-dont-miss-every-open-federal-grant-tribes-can-apply-for-from-one-sentence-5dg3</guid>
      <description>&lt;p&gt;The morning goes like this. Open the portal. Search "tribal". Sort by close date. Open the first eight results in eight tabs. Find the eligibility paragraph in each. Close seven tabs. Do it all again the next morning, because something new may have posted overnight and the only way to know is to look.&lt;/p&gt;

&lt;p&gt;A saved keyword alert shortens the first half of that. It can't read the eligibility paragraph, which is where the answer lives.&lt;/p&gt;

&lt;p&gt;Here is the list I wanted instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Open federal grants in the United States that tribes and tribal organizations can apply for, closing by December 7, 2026, with the funder, award ceiling, deadline and official link&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One sentence, up to 30 results. It came back with 18. &lt;a href="https://specstream.ai/workspace/shared/JYip6eJ47CXfm3LQFoHcA_JUGHVZ7Au_W2OvwwIhoKI?src=devto" rel="noopener noreferrer"&gt;The whole list is here, no account needed.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten of the 18
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Grant&lt;/th&gt;
&lt;th&gt;Funder&lt;/th&gt;
&lt;th&gt;Ceiling&lt;/th&gt;
&lt;th&gt;Closes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026 National Urban and Community Forestry Challenge Cost Share Grant Program&lt;/td&gt;
&lt;td&gt;Forest Service&lt;/td&gt;
&lt;td&gt;$750,000&lt;/td&gt;
&lt;td&gt;2026-10-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community Forest and Open Space Conservation Program&lt;/td&gt;
&lt;td&gt;Forest Service&lt;/td&gt;
&lt;td&gt;$600,000&lt;/td&gt;
&lt;td&gt;2026-10-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tribal Colleges and Universities Program (TCUP)&lt;/td&gt;
&lt;td&gt;U.S. National Science Foundation&lt;/td&gt;
&lt;td&gt;$3,500,000&lt;/td&gt;
&lt;td&gt;2026-10-14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U.S. Department of Justice FY26 Coordinated Tribal Assistance Solicitation Notice of Funding Opportunity&lt;/td&gt;
&lt;td&gt;Bureau of Justice Assistance&lt;/td&gt;
&lt;td&gt;$5,600,000&lt;/td&gt;
&lt;td&gt;2026-10-15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BJA FY 2026 Tribal Justice Training and Technical Assistance Program&lt;/td&gt;
&lt;td&gt;Bureau of Justice Assistance&lt;/td&gt;
&lt;td&gt;$1,870,000&lt;/td&gt;
&lt;td&gt;2026-10-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OJJDP FY 2026 Community and Schools Youth Drug Prevention Program&lt;/td&gt;
&lt;td&gt;Office of Juvenile Justice Delinquency Prevention&lt;/td&gt;
&lt;td&gt;$750,000&lt;/td&gt;
&lt;td&gt;2026-10-29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BJA FY 2026 Tribal Civil and Criminal Legal Assistance&lt;/td&gt;
&lt;td&gt;Bureau of Justice Assistance&lt;/td&gt;
&lt;td&gt;$600,000&lt;/td&gt;
&lt;td&gt;2026-11-06&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OJJDP FY 2026 Family Treatment Court Program&lt;/td&gt;
&lt;td&gt;Office of Juvenile Justice Delinquency Prevention&lt;/td&gt;
&lt;td&gt;$850,000&lt;/td&gt;
&lt;td&gt;2026-11-16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tribal Broadband Connectivity Program&lt;/td&gt;
&lt;td&gt;DOC NIST ERA&lt;/td&gt;
&lt;td&gt;not published&lt;/td&gt;
&lt;td&gt;2026-11-17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native American Affairs: Fiscal Year 2025 Colorado River Basin Tribal Drought Resiliency Program&lt;/td&gt;
&lt;td&gt;Bureau of Reclamation&lt;/td&gt;
&lt;td&gt;$1,000,000&lt;/td&gt;
&lt;td&gt;2026-11-20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every row links to its official page on grants.gov. The list also carries a one-line reason for each match, so you can see why the Community Forest program is on a list for tribes (it names federally recognized Indian tribes as eligible applicants) without opening it.&lt;/p&gt;

&lt;p&gt;Two things the list shows that a title search wouldn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eight of the 18 titles never say tribal, tribe, Indian or Native.&lt;/strong&gt; The two Forest Service programs and all four OJJDP programs are on the list because of their eligibility text. A keyword on the title walks right past them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is a snapshot.&lt;/strong&gt; This run read the portal on October 8. Seven of the 18 deadlines fall between October 9 and October 15, so a copy downloaded a week later has more than a third of its rows closed or closing. That is the whole case for a list that updates itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Make it yours
&lt;/h2&gt;

&lt;p&gt;The sentence is the only part that changes. Swap the field and the date:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Open federal grants for rural hospitals in the United States closing by March 31, 2027, with the funder and award ceiling"&lt;/li&gt;
&lt;li&gt;"Open federal grants for AI research with an award ceiling, closing by December 31, 2026"&lt;/li&gt;
&lt;li&gt;"Open federal grants that community colleges in Texas can apply for, with the deadline and official link"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you'd rather have it in a script, it's five calls: read the sentence (free), get the price (free), approve that price, wait, read the rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.specstream.ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Open federal grants in the United States that tribes and tribal &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;organizations can apply for, closing by December 7, 2026, with the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;funder, award ceiling, deadline and official link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;HOW_MANY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;MAX_CENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;  &lt;span class="c1"&gt;# never approve more than $5
&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPECSTREAM_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# retries are safe: the approval carries a fixed key
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;reading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/parse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read as:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canonical_sentence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requested_units&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HOW_MANY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Costs at most $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_CENTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Over budget. Ask for fewer.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/executions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confirm_max_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;idempotency_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grants-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;find_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queued&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;planning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cursor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;next_cursor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="n"&gt;skip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# workspace buttons, not facts
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grants.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cells&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;column_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;absence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;display&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; grants in grants.csv (list &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;find_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPECSTREAM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
python grants.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need an API key from &lt;a href="https://specstream.ai?src=devto" rel="noopener noreferrer"&gt;specstream.ai&lt;/a&gt; (under &lt;strong&gt;Agents &amp;amp; API&lt;/strong&gt;). New accounts get $10 of work free, no card. The &lt;a href="https://specstream.ai/docs/api?src=devto" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; have the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop checking
&lt;/h2&gt;

&lt;p&gt;A script that runs once is the portal ritual with extra steps. The part that retires the ritual is the schedule.&lt;/p&gt;

&lt;p&gt;Open the list in the workspace and click &lt;strong&gt;Watch for new ones&lt;/strong&gt; under its name. It opens a schedule that looks for more matches every week; change the cadence anywhere from hourly to yearly, and set a maximum per run and per period so a schedule can never spend past what you meant. Then choose where new matches go: the inbox, a Slack channel, a Google Sheet or HubSpot, or your own webhook (&lt;code&gt;monitor.found&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Only new and changed grants are sent. The 18 you already have stay where they are. Rechecks of kept grants cost half the rate, and the ones that stop matching are labeled, so a grant that stops fitting does not sit on your sheet looking fine. Details are in the &lt;a href="https://specstream.ai/docs/schedules?src=devto" rel="noopener noreferrer"&gt;schedules docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That is what replaces the eight tabs: a sheet that gets a new row when a grant that fits you posts, and a label when one stops fitting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;10¢, 30¢ or $1 a match, depending on how many facts each row has to carry, and you see the maximum before you approve anything. A run that finds nothing is free. You pay for the 18 that matched, not the 30 you were willing to take. You start with $10 of work free, once, no card. Rates are on the &lt;a href="https://specstream.ai/pricing?src=devto" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you write grants for a living: what is the eligibility sentence you end up reading eight tabs to find? Tell me in the comments and I'll run it and post the list.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We build SpecStream. The rows above are from a real run on October 8, 2026 (list &lt;code&gt;fnd_c945x7jpcmcwn1c592334xk904&lt;/code&gt;). This post first appeared on the &lt;a href="https://specstream.ai/blog/grants-gov-alerts-for-tribes?src=devto" rel="noopener noreferrer"&gt;SpecStream blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>grants</category>
      <category>automation</category>
      <category>nonprofit</category>
      <category>api</category>
    </item>
    <item>
      <title>How we let AI agents spend money without a blank check</title>
      <dc:creator>Tarek Etman</dc:creator>
      <pubDate>Tue, 13 Oct 2026 21:07:44 +0000</pubDate>
      <link>https://dev.to/specstream/how-we-let-ai-agents-spend-money-without-a-blank-check-18gp</link>
      <guid>https://dev.to/specstream/how-we-let-ai-agents-spend-money-without-a-blank-check-18gp</guid>
      <description>&lt;p&gt;If you give an agent an API key on a funded account, you've given it a card. Most APIs are fine with that: they bill per call, and a loop that runs all night shows up on next month's invoice.&lt;/p&gt;

&lt;p&gt;We couldn't ship it that way. SpecStream turns one sentence ("critical vulnerabilities published this month, with the affected product and the fixed version") into a list of matches from the live web, and you pay per match. One badly worded request from an agent can mean thousands of rows nobody wanted.&lt;/p&gt;

&lt;p&gt;So in our API the key isn't the permission. A price is. Nothing that spends money runs until someone approves a specific price for a specific request, with a ceiling in cents. Here's the whole flow in four calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.specstream.ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CEILING_CENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPECSTREAM_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/parse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Critical vulnerabilities published this month. Include the affected &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
         &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product and the fixed version.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;runnable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read as:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canonical_sentence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ast_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requested_units&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CEILING_CENTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Price is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; cents. Not approving.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;request_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical-vulnerabilities-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/capacity/executions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confirm_max_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_cents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;idempotency_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/finds/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;find_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;partial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canceled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expired&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;display&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cells&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cells&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provenance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;observed_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;SPECSTREAM_API_KEY&lt;/code&gt; to a key from &lt;strong&gt;Agents &amp;amp; API&lt;/strong&gt; in the workspace, and give the key a spending limit while you're there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four calls
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Read&lt;/strong&gt; (&lt;code&gt;POST /v1/parse&lt;/code&gt;). The sentence comes back as a structured reading: what every row has to satisfy, which facts to bring back, and a hash of that reading. If the request is too vague to price, &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;needs_edit&lt;/code&gt; and you reword it. Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price&lt;/strong&gt; (&lt;code&gt;POST /v1/capacity/quotes&lt;/code&gt;). A quote for that exact hash: the most it can cost, a signed &lt;code&gt;quote_hash&lt;/code&gt;, and a 30-minute expiry. Also free. An agent can read and price a hundred requests and the balance doesn't move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approve&lt;/strong&gt; (&lt;code&gt;POST /v1/capacity/executions&lt;/code&gt;). The only call that spends. It has to carry the quote's ID and hash, plus &lt;code&gt;confirm_max_cents&lt;/code&gt;, the ceiling you agree to. Change the request and the reading's hash changes, so the old approval no longer covers it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rows&lt;/strong&gt; (&lt;code&gt;GET /v1/finds/{id}/rows&lt;/code&gt;). Every value comes with the URL it was read from and when. Anything that couldn't be confirmed lands in a separate &lt;code&gt;possible&lt;/code&gt; list and isn't billed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three decisions we'd make again
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The spending limit is a backstop, not a yes.&lt;/strong&gt; A key can carry a spending limit, and it can't raise its own. But an account with $50 in it hasn't agreed to every request an agent can think of. Approval is per price, per request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retries carry a key.&lt;/strong&gt; The approval takes an &lt;code&gt;idempotency_key&lt;/code&gt; you choose. If the response gets lost, &lt;code&gt;GET /v1/capacity/requests/{key}&lt;/code&gt; returns the run that already started instead of starting a second one. Agents retry constantly, and this is exactly where they'd pay twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You pay for matches, not attempts.&lt;/strong&gt; A search that finds nothing costs nothing, and unconfirmed rows stay off the bill. Whatever the run doesn't use of the ceiling goes back to the balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  From an agent, over MCP
&lt;/h2&gt;

&lt;p&gt;The same four steps are tools on our remote MCP server at &lt;code&gt;https://api.specstream.ai/mcp&lt;/code&gt; (Streamable HTTP, &lt;code&gt;Authorization: Bearer &amp;lt;key&amp;gt;&lt;/code&gt;): &lt;code&gt;compile&lt;/code&gt;, &lt;code&gt;quote_capacity_work&lt;/code&gt;, &lt;code&gt;authorize_capacity_work&lt;/code&gt; and &lt;code&gt;list_find_rows&lt;/code&gt;. The server tells the model to show the quote and wait for the person's yes before it calls &lt;code&gt;authorize_capacity_work&lt;/code&gt;, so a human agrees to a number, not to whatever the agent decided to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;A match is 10¢ on Standard (up to five facts a row), and new accounts get $10 of work free, no card. Everything else is on the &lt;a href="https://specstream.ai/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. The &lt;a href="https://specstream.ai/docs/api" rel="noopener noreferrer"&gt;API guide&lt;/a&gt; and the &lt;a href="https://specstream.ai/docs/mcp" rel="noopener noreferrer"&gt;MCP guide&lt;/a&gt; have the full schemas.&lt;/p&gt;

&lt;p&gt;How are you capping spend in your own agents: per-call budgets, a prepaid wallet, a person in the loop? We'd like to hear what's holding up in production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We build SpecStream; this is how its API works.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>mcp</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
