<?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: Hudson Enterprises</title>
    <description>The latest articles on DEV Community by Hudson Enterprises (@hudsonenterprises).</description>
    <link>https://dev.to/hudsonenterprises</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%2Fuser%2Fprofile_image%2F4007061%2F39d90fcd-26bf-4d4a-83a5-1e5cfc8dd804.png</url>
      <title>DEV Community: Hudson Enterprises</title>
      <link>https://dev.to/hudsonenterprises</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hudsonenterprises"/>
    <language>en</language>
    <item>
      <title>Query SEC filings from inside Claude Desktop — Filingrail is now MCP-enabled</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 12:34:20 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/query-sec-filings-from-inside-claude-desktop-filingrail-is-now-mcp-enabled-1dnk</link>
      <guid>https://dev.to/hudsonenterprises/query-sec-filings-from-inside-claude-desktop-filingrail-is-now-mcp-enabled-1dnk</guid>
      <description>&lt;p&gt;Filingrail now ships a first-party MCP server on PyPI: &lt;code&gt;pip install filingrail-mcp&lt;/code&gt;. One install, one config block, and Claude Desktop — or Cursor, or Continue, or any MCP-compatible client — can query SEC filings as tools. No glue code.&lt;/p&gt;

&lt;p&gt;That's worth naming directly. Most SEC-data APIs ship a REST endpoint and stop. You write the agent integration yourself: parse the response, wire up the tool schema, handle auth headers. Filingrail ships the integration as a maintained package with the same update cadence as the underlying REST API.&lt;/p&gt;

&lt;p&gt;This post covers the setup, what you can ask once it's wired in, and the honest limits. I built both the API and the MCP server — I'll be upfront about that throughout.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post covers a data API that returns SEC-registered financial information. Nothing here is investment advice.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Two ways to wire it in
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1 — &lt;code&gt;pip install filingrail-mcp&lt;/code&gt; (recommended)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install the package, add one block to your Claude Desktop config, restart. Filingrail's endpoints appear as tools. No separate service to run, no background daemon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2 — RapidAPI MCP Playground tab (no local install)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/filingrail" rel="noopener noreferrer"&gt;Filingrail listing on RapidAPI&lt;/a&gt; has an MCP tab that generates a ready-to-paste config block. Same endpoints, same auth, zero install step.&lt;/p&gt;

&lt;p&gt;Either path gives Claude the same tools. Pick the one that fits your setup.&lt;/p&gt;




&lt;h3&gt;
  
  
  Setup — the &lt;code&gt;pip install&lt;/code&gt; path
&lt;/h3&gt;

&lt;p&gt;You'll need Python 3.10+ and a RapidAPI key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Subscribe to Filingrail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/filingrail" rel="noopener noreferrer"&gt;Filingrail RapidAPI listing&lt;/a&gt; and subscribe. Free tier is 50 calls/day, no credit card. Copy your &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; from the RapidAPI dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Install the server&lt;/strong&gt;&lt;br&gt;
&lt;/p&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;filingrail-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add Filingrail to your Claude Desktop config&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On macOS: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;&lt;br&gt;
On Windows: &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filingrail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filingrail-mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"RAPIDAPI_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_rapidapi_key_here"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Restart Claude Desktop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Filingrail's endpoints appear as available tools. Your key lives in the &lt;code&gt;env&lt;/code&gt; block — you don't pass it per-call; the server handles the &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; and &lt;code&gt;X-RapidAPI-Host&lt;/code&gt; headers for you.&lt;/p&gt;

&lt;p&gt;The same config pattern works in Cursor and Continue — point their MCP config at &lt;code&gt;filingrail-mcp&lt;/code&gt; the same way.&lt;/p&gt;




&lt;h3&gt;
  
  
  What you can ask once it's wired in
&lt;/h3&gt;

&lt;p&gt;All seven v1.0 endpoints are available as tools. Here's what that looks like in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Company search&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find the CIK for Berkshire Hathaway"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/search/companies&lt;/code&gt;. Over 8,200 SEC-registered issuers are indexed by ticker, CIK, or name fragment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Financials&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What was Apple's operating margin for the most recent quarter? Show me the source filing."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/companies/AAPL/financials&lt;/code&gt;, pulls the normalized income statement from the most recent 10-Q or 10-K, and surfaces the &lt;code&gt;meta.source_filing_url&lt;/code&gt; — a direct &lt;code&gt;sec.gov&lt;/code&gt; link to the filing the numbers came from. Every response carries it.&lt;/p&gt;

&lt;p&gt;The difference between "Claude says revenue was $X" and "Apple's 10-Q filed 2025-10-31 says revenue was $X — here's the filing" is meaningful if you need the primary source, not the model's recall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Financial history&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Pull Amazon's quarterly revenue for the last 3 years and show me the trend"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/companies/AMZN/financials/history&lt;/code&gt; with &lt;code&gt;period=Q&amp;amp;limit=12&lt;/code&gt;. Multiple periods per call, spanning the XBRL history available for each company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filings stream&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"List the most recent 10-K filings for Microsoft"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/filings/recent&lt;/code&gt;. Filterable by CIK, form type, and date range. Each record includes the accession number and a &lt;code&gt;filing_url&lt;/code&gt; pointing to the document on &lt;code&gt;sec.gov&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insider trades&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What Form 4 transactions has Nvidia's CEO filed in the last 6 months?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/companies/NVDA/insider-trades&lt;/code&gt;. Continuously refreshed from SEC EDGAR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8-K material events&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me every 8-K with item code 5.02 — executive changes — that Tesla has filed"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/companies/TSLA/8k-events&lt;/code&gt;. Item codes are preserved as structured fields (1.01 for material agreements, 2.01 for acquisitions, 5.02 for director/officer changes, etc.), so Claude can filter and reason over them in the same conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13F institutional holdings&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What are Berkshire Hathaway's top 10 equity positions by dollar value?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls &lt;code&gt;/v1/institutions/{cik}/13f-holdings&lt;/code&gt;. Returns positions in whole-dollar USD. Continuously updated from EDGAR filings.&lt;/p&gt;




&lt;h3&gt;
  
  
  The &lt;code&gt;meta.source_filing_url&lt;/code&gt; field
&lt;/h3&gt;

&lt;p&gt;Every endpoint — financials, insider trades, 8-K events, 13F holdings — returns a &lt;code&gt;meta.source_filing_url&lt;/code&gt;. It's the direct &lt;code&gt;sec.gov&lt;/code&gt; URL for the filing that produced the data in that response.&lt;/p&gt;

&lt;p&gt;In an MCP context this matters: when Claude answers a financial question, it can attach the source filing as a citation in the same response. Research workflows that need the answer &lt;em&gt;and&lt;/em&gt; the primary source get both without a second lookup.&lt;/p&gt;




&lt;h3&gt;
  
  
  Honest limits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You still need a RapidAPI key.&lt;/strong&gt; &lt;code&gt;filingrail-mcp&lt;/code&gt; is the integration layer, not a separate data source. It calls the same RapidAPI-hosted endpoints. Subscribe, pass your &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; via the &lt;code&gt;env&lt;/code&gt; block, done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limits apply to every tool call Claude makes.&lt;/strong&gt; Free tier is 50 calls/day. A long research session with many back-and-forth questions can burn through that. Budget accordingly or upgrade to Pro ($9/mo for 5,000 calls/month).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not real-time.&lt;/strong&gt; Data is refreshed daily from EDGAR. New filings appear within approximately 6–24 hours of EDGAR acceptance. Yesterday's 8-K disclosures, not this morning's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDGAR only.&lt;/strong&gt; US SEC filings. Non-US issuers (SEDAR, FCA, etc.) are out of scope for v1.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why this is a different abstraction
&lt;/h3&gt;

&lt;p&gt;Most financial data APIs were designed for servers calling servers. The MCP layer shifts how you interact: instead of writing code to call an endpoint and parse the response, you describe what you want and the model routes the tool call, parses the JSON, and reasons over the result in the same context window.&lt;/p&gt;

&lt;p&gt;That works for research-style questions — the ones that aren't really REST queries but aren't pure reasoning either. "What did Berkshire buy or sell in their most recent 13F compared to Q4?" is awkward to write as two sequential API calls stitched together; it's a natural question when Claude has both tools available.&lt;/p&gt;

&lt;p&gt;The citation layer — &lt;code&gt;meta.source_filing_url&lt;/code&gt; on every response — keeps that convenience from floating free of primary sources.&lt;/p&gt;




&lt;h3&gt;
  
  
  What's next on the MCP side
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;filingrail-mcp&lt;/code&gt; tracks the REST API. As new endpoints ship (expanded ratio coverage, screening endpoints, broader company coverage), the MCP tools surface them too. Updates arrive through &lt;code&gt;pip install --upgrade filingrail-mcp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you'd rather call the API directly from Python, there's also a typed SDK: &lt;code&gt;pip install filingrail&lt;/code&gt; — sync and async clients, typed dataclasses, all seven endpoints. The MCP server is built on the same client.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Calls/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;1,500 (50/day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mega&lt;/td&gt;
&lt;td&gt;$199/mo&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Subscribe on the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/filingrail" rel="noopener noreferrer"&gt;Filingrail RapidAPI listing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Questions: &lt;code&gt;support@hudsonenterprisesllc.com&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;Hudson Enterprises LLC&lt;/a&gt;, an Indiana software studio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>mcp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I built a browser extension that exports any web table to CSV, Excel, or Markdown — processed entirely on your device</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 12:33:09 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/i-built-a-browser-extension-that-exports-any-web-table-to-csv-excel-or-markdown-processed-1ac2</link>
      <guid>https://dev.to/hudsonenterprises/i-built-a-browser-extension-that-exports-any-web-table-to-csv-excel-or-markdown-processed-1ac2</guid>
      <description>&lt;p&gt;There is a table on almost every page I work with — government data portals, Wikipedia, SaaS analytics dashboards, documentation sites. The standard workflow for getting that data somewhere useful is: select all, copy, paste into a spreadsheet, spend five minutes fixing the formatting. Or pay a monthly subscription for an export format you need twice a week.&lt;/p&gt;

&lt;p&gt;I priced the segmentation as "your problem, not mine" and built a flat one-time alternative. That is the entire reason this exists.&lt;/p&gt;




&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Click the extension icon on any page. The extension reads the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements from the DOM — the same data your browser already downloaded and rendered. You pick which table (the popup lists all detected tables with a mini-preview and their dimensions), pick a format, and the file is written to disk.&lt;/p&gt;

&lt;p&gt;Nothing leaves your device during that process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://sheetjs.com/" rel="noopener noreferrer"&gt;SheetJS (xlsx)&lt;/a&gt;&lt;/strong&gt; handles XLSX generation entirely in the browser. The library is bundled inside the extension — not loaded from a CDN at runtime — so the network tab stays empty when you export. CSV and Markdown are pure JavaScript over the DOM structure, no dependencies.&lt;/p&gt;

&lt;p&gt;License validation (when you activate a key) makes one call to &lt;code&gt;api.lemonsqueezy.com&lt;/code&gt;. That is the only external network request the extension ever makes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Three file formats — why each one
&lt;/h3&gt;

&lt;p&gt;Different consumers want different things from the same table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;When you want it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSV&lt;/td&gt;
&lt;td&gt;Data pipeline, pandas, any spreadsheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XLSX&lt;/td&gt;
&lt;td&gt;Send to a non-technical colleague, need column widths or sheet structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;td&gt;Documentation, GitHub issues, Obsidian notes, PRs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Copy to clipboard is also there for pasting directly into Slack, email, or a text field. That covers the full range of "I have a table on this page, I need it somewhere else."&lt;/p&gt;




&lt;h3&gt;
  
  
  What it handles — and what it doesn't
&lt;/h3&gt;

&lt;p&gt;It handles standard HTML &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements. Most data tables on government sites, Wikipedia, documentation pages, and SaaS dashboards use standard table markup.&lt;/p&gt;

&lt;p&gt;What it does not handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSS grid / flexbox "tables"&lt;/strong&gt; — if there is no &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; element in the DOM, there is nothing to read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtualized tables&lt;/strong&gt; — enterprise dashboards that only render visible rows in the DOM; the extension captures the rendered rows, not the full dataset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages that block extensions&lt;/strong&gt; — &lt;code&gt;chrome://&lt;/code&gt; URLs and the Chrome Web Store itself block extension scripting by design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I document these in the extension's help text. I would rather a user know upfront what to expect than discover it on a page that matters.&lt;/p&gt;




&lt;h3&gt;
  
  
  The trial and price
&lt;/h3&gt;

&lt;p&gt;Seven-day free trial, no account needed — the trial runs locally in the extension's storage. After that, it is $9 one-time. No subscription. All three export formats on day one.&lt;/p&gt;

&lt;p&gt;The extension is live on the &lt;a href="https://chromewebstore.google.com/detail/ngkcgmmeaeaefmhnlenmofamemjkhfcf" rel="noopener noreferrer"&gt;Chrome Web Store&lt;/a&gt;. The product page is at &lt;strong&gt;&lt;a href="https://tableexporter.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;tableexporter.hudsonenterprisesllc.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What I learned building it
&lt;/h3&gt;

&lt;p&gt;A few things that came up during the build worth sharing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SheetJS in a Chrome extension (MV3)&lt;/strong&gt; — bundling &lt;code&gt;xlsx.full.min.js&lt;/code&gt; as a local vendor file is the right call. Loading from a CDN would break the "nothing transmitted" guarantee and also fails under strict CSP headers on some pages. Bundling avoids both problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;activeTab&lt;/code&gt; is enough&lt;/strong&gt; — the extension only needs &lt;code&gt;activeTab&lt;/code&gt;, &lt;code&gt;scripting&lt;/code&gt;, and &lt;code&gt;storage&lt;/code&gt; permissions. No broad host permissions for the table-reading functionality. The only host permission is &lt;code&gt;api.lemonsqueezy.com&lt;/code&gt; for license key activation. That keeps the permission prompt honest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested tables&lt;/strong&gt; — &lt;code&gt;querySelectorAll('table')&lt;/code&gt; catches nested tables too. I skip any &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; whose parent is inside another &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;. Without that filter, a table used for layout inside a data table shows up as a separate entry, which is confusing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtualized tables are a real cliff&lt;/strong&gt; — I initially thought I could handle them with a scroll-and-capture approach. The DOM truly does not contain the full dataset in those cases; the rows are created and destroyed as you scroll. Documenting the limitation honestly turned out to be the right call rather than shipping a partial solution.&lt;/p&gt;




&lt;h3&gt;
  
  
  Try it
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://chromewebstore.google.com/detail/ngkcgmmeaeaefmhnlenmofamemjkhfcf" rel="noopener noreferrer"&gt;Chrome Web Store listing&lt;/a&gt; — seven-day trial, no account.&lt;/p&gt;

&lt;p&gt;Questions: &lt;a href="mailto:support@hudsonenterprisesllc.com"&gt;support@hudsonenterprisesllc.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I built this. Hudson Enterprises LLC is an Indiana software studio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I built a color picker with one permission — because a Featured one got caught exfiltrating visited URLs</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 12:33:08 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/i-built-a-color-picker-with-one-permission-because-a-featured-one-got-caught-exfiltrating-visited-30jb</link>
      <guid>https://dev.to/hudsonenterprises/i-built-a-color-picker-with-one-permission-because-a-featured-one-got-caught-exfiltrating-visited-30jb</guid>
      <description>&lt;p&gt;In April 2026, a widely-used color picker extension — Featured on the Chrome Web Store, 400,000+ users — was caught collecting and transmitting users' visited URLs and referrer data to a remote server (&lt;a href="https://piunikaweb.com/2026/04/03/color-picker-eyedropper-tool-chrome-extension-tracking-browsing-data/" rel="noopener noreferrer"&gt;PiunikaWeb, April 2026&lt;/a&gt;). Not suspected of it — a researcher diffed the extension's code between versions and showed the tracking being added. The extension was still Featured when this story ran.&lt;/p&gt;

&lt;p&gt;That is not a hypothetical about what browser extensions &lt;em&gt;could&lt;/em&gt; do. It happened. It was reported. And it illustrates the problem with color pickers specifically: they're small, trusted, always-on tools that you install once and forget about. Which makes them attractive targets for abuse.&lt;/p&gt;

&lt;p&gt;After reading that story, I looked for an alternative I could actually verify. I couldn't find one I was confident about — so I built one.&lt;/p&gt;




&lt;h2&gt;
  
  
  One permission. Here's the manifest
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One permission. &lt;code&gt;storage&lt;/code&gt; keeps your local palette between sessions. There is no &lt;code&gt;tabs&lt;/code&gt; permission, no &lt;code&gt;activeTab&lt;/code&gt;, no &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt;, no host permissions for ad networks or analytics providers.&lt;/p&gt;

&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; one host permission — &lt;code&gt;https://api.lemonsqueezy.com/*&lt;/code&gt; — for license validation on the paid tier. That call goes out when you activate a paid license key. It verifies the key. It does not send browsing history, installed extensions, tab URLs, or anything about what you're doing. The free eyedropper works with no network calls at all.&lt;/p&gt;

&lt;p&gt;You can verify this yourself. In Chrome: &lt;code&gt;chrome://extensions&lt;/code&gt; → Details on any extension → "Site access" and "Permissions." Cross-check the source. This is what I'd want from any extension I'm running constantly in my browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the eyedropper actually works
&lt;/h2&gt;

&lt;p&gt;The extension uses the browser's native &lt;strong&gt;EyeDropper API&lt;/strong&gt; (&lt;code&gt;window.EyeDropper&lt;/code&gt;), available in Chrome 95+ and part of the W3C spec. Here's the full flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eyeDropper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EyeDropper&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;eyeDropper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// result.sRGBHex — that's the entire data surface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you click the eyedropper button, the browser takes over screen capture — not the extension. The extension receives a single hex value when you click a pixel. It does not take screenshots. It does not maintain persistent access to what's on your screen. It cannot see what tab you're on or what page you're reading.&lt;/p&gt;

&lt;p&gt;This is not a design choice I made. It's how the API works. The browser sandboxes screen access to the moment of selection and hands the extension only the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens after you pick a color:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The hex value is displayed in the popup&lt;/li&gt;
&lt;li&gt;It's added to a local palette stored in &lt;code&gt;chrome.storage.local&lt;/code&gt; — your browser's own storage, not a server&lt;/li&gt;
&lt;li&gt;A WCAG contrast check runs against your palette entries using the relative luminance formula (WCAG 2.1 §1.4.3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What does not happen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No network requests during normal use&lt;/li&gt;
&lt;li&gt;No analytics SDK&lt;/li&gt;
&lt;li&gt;No account system&lt;/li&gt;
&lt;li&gt;No background service worker running while you're not using the popup&lt;/li&gt;
&lt;li&gt;No access to page content, tab URLs, or browsing history&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The WCAG contrast checker
&lt;/h2&gt;

&lt;p&gt;This was the feature I kept not finding in alternatives. When you're picking colors for a UI, you need to know whether a foreground/background pair meets WCAG AA (4.5:1 for normal text, 3:1 for large text). The extension calculates this inline as you build your palette — no external call, just the relative luminance formula over two hex values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L = 0.2126 * R + 0.7152 * G + 0.0722 * B
contrast = (L1 + 0.05) / (L2 + 0.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells you pass/fail at both thresholds as you work. Useful if you're picking brand colors, building design tokens, or checking a client's palette for accessibility compliance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm launching
&lt;/h2&gt;

&lt;p&gt;The extension is live on the Chrome Web Store: &lt;code&gt;mmdnjjpnigidljigfkjohldlipdkaomp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The basic eyedropper is free. The paid features — export (PNG, JSON, CSS custom properties) and image palette extraction — unlock for &lt;strong&gt;$9 one-time&lt;/strong&gt;. No subscription. License tied to your browser via Lemon Squeezy, which is the one outbound call mentioned above.&lt;/p&gt;

&lt;p&gt;Demand page with install link and details: &lt;strong&gt;&lt;a href="https://colorpicker.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;colorpicker.hudsonenterprisesllc.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  On the permission question
&lt;/h2&gt;

&lt;p&gt;If you're currently using a color picker extension, open &lt;code&gt;chrome://extensions&lt;/code&gt;, find it, and look at "Permissions" and "Site access." Extensions with &lt;code&gt;tabs&lt;/code&gt; permission can read your open tab URLs. Extensions with &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt; or broad host permissions can inject scripts into every page you load. Permissions don't expose &lt;em&gt;what&lt;/em&gt; an extension does with that access — only what it's allowed to do. Network calls happen within whatever access the extension already has.&lt;/p&gt;

&lt;p&gt;Worth knowing. I'm not saying any specific extension you're using is doing something wrong. I'm saying the April 2026 incident was real, the risk is structural, and the minimum-permission approach I took here is verifiable.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;Hudson Enterprises LLC&lt;/a&gt;. Questions: &lt;a href="mailto:support@hudsonenterprisesllc.com"&gt;support@hudsonenterprisesllc.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I built this and the demand page linked above is mine.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
      <category>privacy</category>
    </item>
    <item>
      <title>I built a PDF editor that runs entirely in your browser — no upload, no account, $9 once</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 11:19:09 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/i-built-a-pdf-editor-that-runs-entirely-in-your-browser-no-upload-no-account-9-once-51lg</link>
      <guid>https://dev.to/hudsonenterprises/i-built-a-pdf-editor-that-runs-entirely-in-your-browser-no-upload-no-account-9-once-51lg</guid>
      <description>&lt;p&gt;Every time I merge a contract PDF with a signature page, I'm handed the same bad choice: use a web tool that uploads my files to a server I know nothing about, or fight with a desktop app that costs $80/year and ships features I don't need.&lt;/p&gt;

&lt;p&gt;I've watched people paste tax returns, medical records, and NDA drafts into browser-based PDF tools without thinking about where those files go. The upload is the product. The free tier exists because documents are valuable.&lt;/p&gt;

&lt;p&gt;So I looked at what the browser can actually do without a server involved. The answer is more than most people assume.&lt;/p&gt;




&lt;h3&gt;
  
  
  What runs where
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://pdf-lib.js.org/" rel="noopener noreferrer"&gt;pdf-lib&lt;/a&gt;&lt;/strong&gt; handles all the actual PDF manipulation — merge, split, extract pages, delete pages, rotate. It's a JavaScript library that runs entirely in the browser. When you click "Merge," your files go from disk into a &lt;code&gt;Uint8Array&lt;/code&gt; in the browser's memory, get processed by pdf-lib, and come back out as a new file downloaded to your disk. The network is never involved in that path.&lt;/p&gt;

&lt;p&gt;The extension itself is minimal on purpose. The service worker does one thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;Click the toolbar icon, get a full-page editor tab. No popup (too cramped for PDF thumbnails), no background process, no telemetry, no account system. The service worker has nothing to phone home to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mozilla.github.io/pdf.js/" rel="noopener noreferrer"&gt;PDF.js&lt;/a&gt;&lt;/strong&gt; renders page thumbnails inline. Before you merge or split, you can see the pages you're working with. That's a significant UX difference from tools where you operate on filenames blindly — especially when page order matters or you're not sure which pages you actually need to extract.&lt;/p&gt;

&lt;p&gt;License validation talks to &lt;strong&gt;Lemon Squeezy's&lt;/strong&gt; API, but only when you enter a key. The processing path is clean — there's no license check inside the merge or split logic, and the extension runs in a 7-day trial before asking for anything.&lt;/p&gt;




&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merge&lt;/strong&gt; — drag PDFs into order, output one combined file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split&lt;/strong&gt; — choose which pages go into which output file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract&lt;/strong&gt; — pull specific pages into a new document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete&lt;/strong&gt; — remove pages by number&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotate&lt;/strong&gt; — rotate individual pages or all pages 90/180/270 degrees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Page thumbnails render inline so you can see what you're working with before committing.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why not just a desktop app
&lt;/h3&gt;

&lt;p&gt;Desktop apps solve the upload problem but create a different one: most serious PDF tools are subscription-based or require expensive one-time purchases. The lightweight free options have narrow feature sets — typically merge-only or split-only, not the full set you need when you're actually working with documents.&lt;/p&gt;

&lt;p&gt;The browser-based approach means it runs on Windows, Mac, and Linux anywhere Chrome runs. One install. No update treadmill — the extension updates through Chrome's normal mechanism, but the processing logic doesn't drift between versions in ways that would break your workflow.&lt;/p&gt;

&lt;p&gt;For documents in the dozens-of-pages range, the processing speed is fine. For 500-page books, a native app will be faster. That's an honest tradeoff worth stating clearly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Where to get it
&lt;/h3&gt;

&lt;p&gt;It's live on the Chrome Web Store: &lt;strong&gt;&lt;a href="https://chromewebstore.google.com/detail/lnckpnjfkagjcagaddlobnnkjmnoapbl" rel="noopener noreferrer"&gt;PDF Toolkit — Offline&lt;/a&gt;&lt;/strong&gt;. There's a bit more detail on the &lt;a href="https://pdftoolkit.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;product page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Price: $9 one-time. No subscription. No account required to use the extension after you enter your license key. The 7-day trial starts on first use, so you can evaluate it on your own documents before buying.&lt;/p&gt;

&lt;p&gt;If you've dealt with the "upload your sensitive document to our server" problem and built your own workaround, I'm curious what that looked like. Drop it in the comments. And if you want to try it, the install link is up above.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;support@hudsonenterprisesllc.com&lt;/code&gt; for anything else — async only.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I built this. The links above go to my product's Chrome Web Store listing and product page. I'm disclosing that because I'd want to know.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Screen a name against the OFAC sanctions list in 3 lines (REST API, free tier)</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 11:19:08 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/screen-a-name-against-the-ofac-sanctions-list-in-3-lines-rest-api-free-tier-23gg</link>
      <guid>https://dev.to/hudsonenterprises/screen-a-name-against-the-ofac-sanctions-list-in-3-lines-rest-api-free-tier-23gg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Read this before the code:&lt;/strong&gt; SanctionRail is one input to your own OFAC compliance program — not a substitute for it, not a sole or final screening control, and not legal or compliance advice. Fuzzy matching produces both false positives and false negatives. You independently review every result and make all blocking, rejection, and reporting determinations. Hudson Enterprises LLC is a software publisher, not a regulated screening provider, law firm, or compliance advisor. Use is governed by the &lt;a href="https://hudsonenterprisesllc.com/legal/sanctionrail-terms" rel="noopener noreferrer"&gt;SanctionRail Terms of Service&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;U.S. businesses handling regulated transactions are required to screen counterparties against the OFAC Specially Designated Nationals (SDN) list. If a transaction involves a listed party, you're required to block it and report it to Treasury.&lt;/p&gt;

&lt;p&gt;The tooling gap is real. OFAC.gov has a search box — browser only, no API, no fuzzy matching. If the name is transliterated differently or the parts are reordered, an exact-string search misses it. Enterprise tools like LexisNexis WorldCompliance and ComplyAdvantage start at $1,000+/month on annual contracts. There's nothing useful in between for a team that needs a screening signal without a six-figure budget.&lt;/p&gt;

&lt;p&gt;This tutorial walks through calling &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/sanctionrail" rel="noopener noreferrer"&gt;SanctionRail&lt;/a&gt; — a REST API that does fuzzy name matching against the OFAC SDN and consolidated sanctions lists. Free tier: 1,500 calls/month, no credit card. I built it; I'll be upfront about that throughout.&lt;/p&gt;

&lt;p&gt;There's also a &lt;a href="https://sanctionrail.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;free web checker&lt;/a&gt; — no account required, type a name and see whether it matches. Good for a manual spot-check before you write any code.&lt;/p&gt;




&lt;h3&gt;
  
  
  Before you integrate: what this is and isn't
&lt;/h3&gt;

&lt;p&gt;This needs to be said plainly before the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What SanctionRail screens against:&lt;/strong&gt; U.S. Treasury OFAC Specially Designated Nationals (SDN) list and the OFAC consolidated (non-SDN) sanctions lists, sourced from treasury.gov and refreshed daily. Approximately 18,959 records across individuals, entities, vessels, and aircraft, plus their indexed aliases and AKAs (&lt;code&gt;total_records_searched&lt;/code&gt; in every response tells you the exact count at query time).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does NOT screen against:&lt;/strong&gt; EU, UN, UK (HM Treasury / OFSI), Canadian (OSFI), or any other non-U.S. sanctions regime. No PEP lists. No adverse-media. If your compliance program requires multi-regime screening, this is not the right tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What fuzzy matching means in practice:&lt;/strong&gt; the API catches transliteration variants, reordered name parts, and partial matches that exact-string search misses. It also means false positives (a legitimate customer whose name resembles a listed party) and false negatives (a listed party whose name is transliterated differently than the indexed alias). You review every match result. You make all blocking and reporting decisions. The API does not make compliance determinations for you.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1 — Set up
&lt;/h3&gt;

&lt;p&gt;A free &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/sanctionrail" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; account → subscribe to SanctionRail on the Free tier → copy your &lt;code&gt;X-RapidAPI-Key&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_key_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python 3.8+ with &lt;code&gt;requests&lt;/code&gt; is all you need for the examples below.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2 — The 3-line version
&lt;/h3&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;requests&lt;/span&gt;

&lt;span class="n"&gt;r&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctionrail.p.rapidapi.com/screen&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;name&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;Vladimir Putin&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;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.85&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;5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;X-RapidAPI-Key&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;YOUR_KEY&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;X-RapidAPI-Host&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;sanctionrail.p.rapidapi.com&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the core call. One endpoint, a ranked list of matches with confidence scores.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Read the response
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vladimir Putin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"list_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"05/11/2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"list_loaded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-14T22:52:21Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_records_searched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18959&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"match_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sdn_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PUTIN, Vladimir Vladimirovich"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sdn_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_via"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PUTIN, Vladimir Vladimirovich"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fields worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_version&lt;/code&gt; and &lt;code&gt;list_loaded_at&lt;/code&gt; — the age of the data at query time. Check this against the current OFAC publication date on treasury.gov for high-stakes transactions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;score&lt;/code&gt; — confidence of the match (0–1). Not a compliance determination.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;matched_via&lt;/code&gt; — whether the match came from the primary name or an indexed alias.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;program&lt;/code&gt; — the sanctions program(s) the record is listed under (e.g. &lt;code&gt;RUSSIA-EO14024&lt;/code&gt;, &lt;code&gt;SDGT&lt;/code&gt;, &lt;code&gt;IRAN&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 4 — Understand the parameters
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;POST /screen&lt;/code&gt; accepts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Param&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Required.&lt;/strong&gt; The name to screen (2–200 chars)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;threshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;0.85&lt;/td&gt;
&lt;td&gt;Minimum confidence score to return a match (0–1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sdn_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scope to &lt;code&gt;Individual&lt;/code&gt;, &lt;code&gt;Entity&lt;/code&gt;, &lt;code&gt;Vessel&lt;/code&gt;, &lt;code&gt;Aircraft&lt;/code&gt;, or &lt;code&gt;Any&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;include_aliases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match against indexed aliases and AKAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Max matches returned (1–100). Must be ≥ 1.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;threshold&lt;/code&gt; parameter is the most operationally significant decision you'll make. 0.85 is the default — a reasonable starting point. Drop it toward 0.70 for higher recall and more false positives to review. Raise it toward 0.95 to cut noise, accepting higher false-negative risk. What the right value is for your program depends on your risk tolerance and review capacity — that's a judgment call for your compliance team, not something this documentation can prescribe.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5 — Screen a batch of names
&lt;/h3&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;requests&lt;/span&gt;

&lt;span class="n"&gt;RAPIDAPI_KEY&lt;/span&gt; &lt;span class="o"&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;RAPIDAPI_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;HEADERS&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;X-RapidAPI-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;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&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;sanctionrail.p.rapidapi.com&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;def&lt;/span&gt; &lt;span class="nf"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sdn_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Any&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctionrail.p.rapidapi.com/screen&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;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sdn_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sdn_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_aliases&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&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;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&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;r&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="n"&gt;names_to_screen&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;Acme Trading Co&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;Kim Jong Un&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;Jane Smith&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;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names_to_screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;match_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_count&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;match_count&lt;/span&gt; &lt;span class="o"&gt;&amp;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;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&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="mi"&gt;0&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="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; match(es) — top score &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&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="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="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&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;)&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  → REQUIRES REVIEW — do not proceed without manual verification&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="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="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: no matches above threshold &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;threshold&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="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;  → No match found; your own compliance process determines next steps&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;&lt;strong&gt;A match does not mean block. No match does not mean clear.&lt;/strong&gt; Those determinations belong to your compliance program and your legal or compliance advisors. The API surfaces whether a name resembles an OFAC-listed party; what you do with that signal is yours.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 6 — Look up a full entity record
&lt;/h3&gt;

&lt;p&gt;When a match returns an &lt;code&gt;sdn_id&lt;/code&gt;, pull the full record — aliases, addresses, citizenships, dates of birth, ID documents, and sanctions programs:&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;sdn_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;35096&lt;/span&gt;  &lt;span class="c1"&gt;# from the match result
&lt;/span&gt;
&lt;span class="n"&gt;r&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="nf"&gt;get&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;https://sanctionrail.p.rapidapi.com/entity/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sdn_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;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&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;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the record as published on the OFAC list. If you're doing manual review on a positive match, start here.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 7 — Check list freshness
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;r&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="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;https://sanctionrail.p.rapidapi.com/list-status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&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;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="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;List version: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list_version&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="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;Loaded at:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list_loaded_at&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="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;Record count: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;record_count&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list refreshes daily from treasury.gov. Because the refresh depends on Treasury's own publication timing, newly-designated parties may not appear immediately. For high-stakes transactions, cross-reference &lt;code&gt;list_version&lt;/code&gt; against the current OFAC publication date at &lt;a href="https://sanctions.ofac.treas.gov" rel="noopener noreferrer"&gt;sanctions.ofac.treas.gov&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What this is not
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not a compliance program.&lt;/strong&gt; SanctionRail is one input to your own OFAC compliance program — not a substitute for it and not a final screening control. Your program must include review procedures, documentation, and blocking/reporting determinations that no API can make for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not multi-regime.&lt;/strong&gt; U.S. OFAC SDN and consolidated lists only. EU, UN, UK (OFSI), and other regimes are out of scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not PEP or adverse-media.&lt;/strong&gt; No politically-exposed-person lists, no adverse-media screening.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not legal or compliance advice.&lt;/strong&gt; If you're building OFAC compliance into a regulated product, work with a qualified attorney or compliance professional.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Calls/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BASIC&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;1,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PRO&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ULTRA&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEGA&lt;/td&gt;
&lt;td&gt;$199/mo&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No credit card on the free tier. Subscribe at the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/sanctionrail" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt;. Every call needs &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; and &lt;code&gt;X-RapidAPI-Host: sanctionrail.p.rapidapi.com&lt;/code&gt; — that's the full auth story.&lt;/p&gt;

&lt;p&gt;Enterprise tools (LexisNexis WorldCompliance, ComplyAdvantage, Refinitiv World-Check) cover multiple global regimes + PEP + adverse-media at $1,000+/month on annual contracts. SanctionRail is narrower — U.S. OFAC only — at a fraction of the cost. If you need multi-regime coverage, budget for the enterprise tier; if U.S. OFAC is the specific gap, this fills it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Questions or issues
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;support@hudsonenterprisesllc.com&lt;/code&gt; — same-business-day response, async only. Include your RapidAPI key prefix (first 8 chars), the endpoint, and the request body.&lt;/p&gt;

&lt;p&gt;For list data questions — a suspected outdated record, an incorrect alias — we can point you to the originating OFAC record on treasury.gov so you can verify against the source.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;Hudson Enterprises LLC&lt;/a&gt;. Use governed by the &lt;a href="https://hudsonenterprisesllc.com/legal/sanctionrail-terms" rel="noopener noreferrer"&gt;SanctionRail Terms of Service&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>fintech</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Find catastrophic-backtracking regexes before they ship (free checker + API)</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Thu, 09 Jul 2026 10:20:36 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/find-catastrophic-backtracking-regexes-before-they-ship-free-checker-api-1kce</link>
      <guid>https://dev.to/hudsonenterprises/find-catastrophic-backtracking-regexes-before-they-ship-free-checker-api-1kce</guid>
      <description>&lt;p&gt;A regex that works correctly under normal inputs can pin a CPU at 100% when a crafted input triggers catastrophic backtracking. The attack class is CWE-1333 — ReDoS, regular expression denial of service. The runtime doesn't matter: Node.js, Python, Java, Ruby, all use backtracking-based engines. What matters is whether the pattern itself creates an exponentially growing search space on a non-matching input.&lt;/p&gt;

&lt;p&gt;This tutorial walks through what catastrophic backtracking looks like, how to detect it before it ships, and how to wire the check into a CI/CD gate so it runs automatically on every changeset.&lt;/p&gt;

&lt;p&gt;Everything here runs against &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/redoscan1" rel="noopener noreferrer"&gt;ReDoScan&lt;/a&gt; — a REST API I built for this specific job. There's also a &lt;a href="https://redoscan.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;free web checker&lt;/a&gt; if you want to paste a regex without writing code. I built both; I'll be upfront about that throughout.&lt;/p&gt;




&lt;h3&gt;
  
  
  What catastrophic backtracking actually is
&lt;/h3&gt;

&lt;p&gt;Most regex engines work by trying every possible path through the pattern when a match fails. For safe patterns the search space is small — the engine finds a dead-end quickly and moves on. For certain constructs the search space grows exponentially with input length, and a crafted non-matching input can force the engine to explore it all.&lt;/p&gt;

&lt;p&gt;Three constructs reliably cause this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested quantifiers&lt;/strong&gt; — &lt;code&gt;(a+)+&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The inner &lt;code&gt;a+&lt;/code&gt; can match one or more &lt;code&gt;a&lt;/code&gt;s. The outer &lt;code&gt;+&lt;/code&gt; lets the group repeat. For input &lt;code&gt;aaaaaX&lt;/code&gt;, the engine tries every way to distribute the &lt;code&gt;a&lt;/code&gt;s across iterations before concluding there's no match. The number of combinations is exponential in the length of the input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overlapping alternation&lt;/strong&gt; — &lt;code&gt;(a|a)*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Both branches match the same input. The engine explores every combination of which branch matched which character.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefix overlap&lt;/strong&gt; — &lt;code&gt;(a|ab)*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The two branches share a prefix. On a non-matching input the engine can't prune the search tree early.&lt;/p&gt;

&lt;p&gt;A pattern like &lt;code&gt;^(\w+)+$&lt;/code&gt; — common in username validators — is nested quantifiers in production clothes. &lt;code&gt;\w+&lt;/code&gt; expands to &lt;code&gt;[a-zA-Z0-9_]+&lt;/code&gt;. Same exponential behavior. Input &lt;code&gt;aaaaaaaaaaaaaaaaaaaaaaaaaaaaX&lt;/code&gt; will hang the thread.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 0 — Try the free checker
&lt;/h3&gt;

&lt;p&gt;Paste any regex at &lt;a href="https://redoscan.hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;&lt;code&gt;redoscan.hudsonenterprisesllc.com&lt;/code&gt;&lt;/a&gt;. No account needed. Static analysis runs and returns a risk badge (&lt;code&gt;safe&lt;/code&gt; / &lt;code&gt;low&lt;/code&gt; / &lt;code&gt;medium&lt;/code&gt; / &lt;code&gt;high&lt;/code&gt; / &lt;code&gt;critical&lt;/code&gt;) with the findings in plain English.&lt;/p&gt;

&lt;p&gt;Paste &lt;code&gt;(a+)+$&lt;/code&gt;. It comes back &lt;code&gt;high&lt;/code&gt; with the finding: "Nested quantifier inside a quantified group. Triggers exponential backtracking on long non-matching inputs."&lt;/p&gt;

&lt;p&gt;The API adds an optional dynamic timing layer for deeper checks — covered in Step 3.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1 — Set up the API
&lt;/h3&gt;

&lt;p&gt;Free tier on RapidAPI: 1,500 scans/month, no credit card. Subscribe at the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/redoscan1" rel="noopener noreferrer"&gt;ReDoScan listing&lt;/a&gt; and your &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; appears in the dashboard within seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_key_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2 — Scan one pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://redoscan1.p.rapidapi.com/scan"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Host: redoscan1.p.rapidapi.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"pattern": "(a+)+$", "dynamic": false}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(a+)+$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overall_risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_redos_vulnerable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"static_findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rule_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nested-quantifier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nested quantifier inside a quantified group. Triggers exponential backtracking on long non-matching inputs."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"syntax_valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scan_time_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;overall_risk&lt;/code&gt; gives you the 5-level classification. &lt;code&gt;is_redos_vulnerable&lt;/code&gt; is the boolean gate for automated pipelines. &lt;code&gt;static_findings&lt;/code&gt; tells you which rule fired and where in the pattern — useful when you need to explain the finding in a PR comment or a security ticket.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Add dynamic timing (optional, deeper)
&lt;/h3&gt;

&lt;p&gt;Static rules catch the canonical constructs in sub-millisecond time — the right default for a batch CI gate. Dynamic timing feeds the pattern adversarial inputs of increasing length, measures how runtime scales, and classifies the growth curve (safe / polynomial / exponential). It catches some patterns that static rules miss.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://redoscan1.p.rapidapi.com/scan"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Host: redoscan1.p.rapidapi.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"pattern": "(a+)+$", "dynamic": true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response adds a &lt;code&gt;dynamic_finding&lt;/code&gt; object with per-length timing data (&lt;code&gt;timings_ms&lt;/code&gt;) and the growth classification (&lt;code&gt;growth_classification&lt;/code&gt;). Use dynamic on a final audit pass or for a single suspicious pattern you want to confirm. For a 200-pattern batch in CI, keep &lt;code&gt;"dynamic": false&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4 — Wire it into a CI gate
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/scan-batch&lt;/code&gt; endpoint takes up to 200 patterns in one request — the right tool for a pre-merge gate. Extract every regex literal from a changeset, batch-scan them, fail the build on &lt;code&gt;high&lt;/code&gt; or &lt;code&gt;critical&lt;/code&gt;.&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;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&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;RAPIDAPI_KEY&lt;/span&gt; &lt;span class="o"&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;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# These would come from static analysis of the changed files
&lt;/span&gt;&lt;span class="n"&gt;patterns&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;^[a-z]+$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# safe
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(a+)+&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# high — nested quantifier
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(&lt;/span&gt;&lt;span class="se"&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="c1"&gt;# high — same pattern, common in validators
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;-]+@[a-zA-Z0-9.&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# email — likely safe
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;r&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://redoscan1.p.rapidapi.com/scan-batch&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;patterns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;X-RapidAPI-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;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&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;redoscan1.p.rapidapi.com&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;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&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="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="c1"&gt;# Fail on high or critical
&lt;/span&gt;&lt;span class="n"&gt;high_risk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overall_risk&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;high&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&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;high_risk&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;FAIL: &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;high_risk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; high-risk regex(es) detected:&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;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;high_risk&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;  Pattern: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pattern&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="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;  Risk:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;overall_risk&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="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;x&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;static_findings&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="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;  Finding: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;description&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="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&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="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;PASS: &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;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; patterns scanned, none high-risk or critical&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;One thing worth noting: invalid patterns come back with &lt;code&gt;"syntax_valid": false&lt;/code&gt; rather than erroring the whole batch. A malformed pattern won't stop the other 199 from being checked.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5 — Validate against the known-evil corpus
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET /known-evil&lt;/code&gt; is a public endpoint — no API key required. It returns a curated corpus of canonical evil patterns, real-world CVE patterns, and safe baselines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://redoscan1.p.rapidapi.com/known-evil"&lt;/span&gt; | python &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit this before wiring any gate into production. If a pattern you know is vulnerable comes back &lt;code&gt;safe&lt;/code&gt;, something is misconfigured. The corpus also works as a test fixture for anyone building their own tooling on top of the API.&lt;/p&gt;




&lt;h3&gt;
  
  
  What this is not
&lt;/h3&gt;

&lt;p&gt;Honest caveats, because someone will ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not a full SAST suite.&lt;/strong&gt; It does one thing — ReDoS / regex denial-of-service detection. No SQL injection, no XSS, no secrets. Use a full SAST platform for that; this is the ReDoS check you run without licensing one per seat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a guarantee.&lt;/strong&gt; &lt;code&gt;safe&lt;/code&gt; means no static rule fired and — if dynamic was enabled — no pathological growth was observed on adversarial inputs. It does not mean the pattern is invulnerable to every possible input. Real-world CVE patterns can carry non-obvious vulnerabilities that static rules and timing tests both miss. Your own application security review still applies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a regex linter.&lt;/strong&gt; It scores denial-of-service risk, not style or correctness. &lt;code&gt;[A-Za-z]+&lt;/code&gt; can be written as &lt;code&gt;\p{L}+&lt;/code&gt; — ReDoScan doesn't care about that. The score is about whether the pattern can be weaponized against your server.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Scans/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BASIC&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;1,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PRO&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ULTRA&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEGA&lt;/td&gt;
&lt;td&gt;$199/mo&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No credit card on the free tier. Subscribe at the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/redoscan1" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt;. Every call needs &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; and &lt;code&gt;X-RapidAPI-Host: redoscan1.p.rapidapi.com&lt;/code&gt; — that's the full auth story.&lt;/p&gt;




&lt;h3&gt;
  
  
  Questions or issues
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;support@hudsonenterprisesllc.com&lt;/code&gt; — same-business-day response, async only. Include your RapidAPI key prefix (first 8 chars), the endpoint, and the request body if you're reporting an issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;Hudson Enterprises LLC&lt;/a&gt;, an Indiana software studio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>Pull any public company's SEC financials in 3 lines of Python (with source filing URL)</title>
      <dc:creator>Hudson Enterprises</dc:creator>
      <pubDate>Sun, 28 Jun 2026 23:31:36 +0000</pubDate>
      <link>https://dev.to/hudsonenterprises/pull-any-public-companys-sec-financials-in-3-lines-of-python-with-source-filing-url-1icm</link>
      <guid>https://dev.to/hudsonenterprises/pull-any-public-companys-sec-financials-in-3-lines-of-python-with-source-filing-url-1icm</guid>
      <description>&lt;p&gt;Every number a company reports to the SEC is public. The bottleneck isn't access — EDGAR is free and open. The bottleneck is the parser, the XBRL normalizer, the rate-limit governor, and the deduplication layer you have to write before you get to the one number you actually wanted.&lt;/p&gt;

&lt;p&gt;This tutorial skips all of that. By the end you'll have working Python that returns Apple's income statement, balance sheet, and cash flow from a live 10-Q — plus the exact &lt;code&gt;sec.gov&lt;/code&gt; URL for every number, so you can verify any figure at its source.&lt;/p&gt;

&lt;p&gt;Everything here runs against &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/filingrail" rel="noopener noreferrer"&gt;Filingrail&lt;/a&gt;, a REST API on RapidAPI that normalizes SEC EDGAR filings into clean JSON. Free tier: 50 calls/day, no credit card. I built it; the tutorial is honest about what it can and can't do.&lt;/p&gt;




&lt;h3&gt;
  
  
  What you need
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.8+ with &lt;code&gt;requests&lt;/code&gt; (&lt;code&gt;pip install requests&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A free RapidAPI account → subscribe to Filingrail on the Free tier → copy your &lt;code&gt;X-RapidAPI-Key&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_key_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No EDGAR User-Agent configuration, no local XBRL parser, no rate-limit governor to wire up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prefer a typed client over raw HTTP?&lt;/strong&gt; There's an official Python SDK:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;filingrail
&lt;/code&gt;&lt;/pre&gt;



&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;filingrail&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Filingrail&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Filingrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_rapidapi_key&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;financials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source_filing_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Sync and async clients, typed dataclasses, all seven endpoints. &lt;a href="https://pypi.org/project/filingrail/" rel="noopener noreferrer"&gt;PyPI page&lt;/a&gt; has the full reference. The rest of this tutorial uses plain &lt;code&gt;requests&lt;/code&gt; so the HTTP shape stays visible — but the SDK is the shorter path for production code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 1 — Resolve a ticker to its SEC CIK
&lt;/h3&gt;

&lt;p&gt;The SEC's Central Index Key (CIK) is the canonical identifier for every registered issuer. Filingrail's search endpoint handles the ticker-to-CIK lookup, so you don't maintain a mapping file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://filingrail.p.rapidapi.com/v1/search/companies?q=apple&amp;amp;limit=3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Host: filingrail.p.rapidapi.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | python &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response (abbreviated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cik"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;320193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AAPL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Apple Inc."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sic_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sic_description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state_of_inc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cik"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-03T11:20:39"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source_filing_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source_filing_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note on the nulls: &lt;code&gt;sic_code&lt;/code&gt;, &lt;code&gt;exchange&lt;/code&gt;, and &lt;code&gt;state_of_inc&lt;/code&gt; are sparsely populated in the index — the search response's &lt;code&gt;meta.source_filing_url&lt;/code&gt; is also null because the company record (not a filing) is the source. You'll see that field populated on every financial, insider-trade, and 8-K response.&lt;/p&gt;

&lt;p&gt;8,005 SEC-registered issuers are indexed. Search accepts a ticker symbol, a CIK number, or a name fragment. Postgres trigrams handle fuzzy matching — &lt;code&gt;"appel"&lt;/code&gt; finds Apple.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2 — Pull the financials
&lt;/h3&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;requests&lt;/span&gt;

&lt;span class="n"&gt;RAPIDAPI_KEY&lt;/span&gt; &lt;span class="o"&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;RAPIDAPI_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;BASE_URL&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://filingrail.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&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;X-RapidAPI-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;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&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;filingrail.p.rapidapi.com&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;resp&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="nf"&gt;get&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="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/companies/AAPL/financials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&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;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&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="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&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="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Source filing: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;meta&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_filing_url&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="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;Filed:         &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;meta&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_filing_date&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;meta.source_filing_url&lt;/code&gt; field is the one worth understanding. It's the direct URL to the SEC filing that produced the data in this response — not a database reference, a live &lt;code&gt;sec.gov&lt;/code&gt; link. Every response carries it. If you're building anything where a user might ask "where does this number come from," you have the answer in the response envelope.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Print the headline numbers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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;n&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000&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="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&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="s"&gt;M&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&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;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;body&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;stype&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statement_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;period_end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line_items&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;stype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&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;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stype&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;income_statement&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Revenue:          &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;revenue&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="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;  Operating income: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;operating_income&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="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;  Net income:       &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;net_income&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="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;  Diluted EPS:      &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;items&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;eps_diluted&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;—&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="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;stype&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;balance_sheet&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Total assets:     &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;total_assets&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="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;  Cash + equiv.:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;cash_and_equivalents&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="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;  Long-term debt:   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;long_term_debt&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="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;stype&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cash_flow&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Operating CF:     &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;operating_cash_flow&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="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;  Capex:            &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;capex&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three statements, headline fields, source filing URL. Swap &lt;code&gt;AAPL&lt;/code&gt; for &lt;code&gt;MSFT&lt;/code&gt;, &lt;code&gt;NVDA&lt;/code&gt;, &lt;code&gt;BRK-B&lt;/code&gt;, or any of the 8,005 tickers in the index.&lt;/p&gt;




&lt;h3&gt;
  
  
  What the response shape looks like
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/v1/companies/{ticker}/financials&lt;/code&gt; returns the most recent 10-K or 10-Q filing. The data is backed by 1.85M+ rows of XBRL-normalized financial data going back to 2006.&lt;/p&gt;

&lt;p&gt;The ~30 canonical fields per statement are normalized across the multi-tag drift that makes raw XBRL painful. The same economic concept — revenue, for instance — gets tagged as &lt;code&gt;us-gaap:Revenues&lt;/code&gt;, &lt;code&gt;us-gaap:SalesRevenueNet&lt;/code&gt;, or &lt;code&gt;us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax&lt;/code&gt; depending on the filer and the year. Filingrail resolves that before it reaches your response.&lt;/p&gt;




&lt;h3&gt;
  
  
  Getting historical quarters
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/financials/history&lt;/code&gt; returns a time series. Up to 20 periods per call (roughly 5 years quarterly):&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;resp&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="nf"&gt;get&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="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/companies/AAPL/financials/history&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;period&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;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;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;8&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&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;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;history&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statement_type&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;income_statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line_items&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="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="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;period_end&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;  revenue=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fmt_usd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;revenue&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Monitoring the filings stream
&lt;/h3&gt;

&lt;p&gt;If you want to watch what a company files — rather than pull normalized data — use &lt;code&gt;/v1/filings/recent&lt;/code&gt;. 237,000+ filings indexed, filterable by CIK, form type, or date range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Most recent filings for Apple, 10-Ks only&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://filingrail.p.rapidapi.com/v1/filings/recent?cik=320193&amp;amp;form_type=10-K&amp;amp;limit=5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Host: filingrail.p.rapidapi.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each record includes the filing date, accession number, and a &lt;code&gt;filing_url&lt;/code&gt; pointing directly to the document on &lt;code&gt;sec.gov&lt;/code&gt;. The same source-traceability, just at the filing level rather than the data level.&lt;/p&gt;




&lt;h3&gt;
  
  
  What this is not
&lt;/h3&gt;

&lt;p&gt;Honest caveats, because senior developers will ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not real-time.&lt;/strong&gt; New filings appear within ~24 hours of EDGAR acceptance. Sub-minute polling would violate EDGAR's fair-access policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not Bloomberg.&lt;/strong&gt; No intraday prices, no options chains, no non-US issuers, no analyst estimates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a research platform.&lt;/strong&gt; No AI summaries, no commentary. Structured data from SEC filings, traced back to source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a replacement for EdgarTools&lt;/strong&gt; if you're already running your own pipeline. EdgarTools is excellent and free. Filingrail is the option for developers who'd rather pay $9/month than maintain the plumbing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The other endpoints
&lt;/h3&gt;

&lt;p&gt;Beyond financials, v1.0 includes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;th&gt;Volume&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/companies/{ticker}/insider-trades&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Form 4 transactions&lt;/td&gt;
&lt;td&gt;63,000+ rows, daily refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/companies/{ticker}/8k-events&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Material events with SEC item codes (1.01, 2.01, 5.02, etc.)&lt;/td&gt;
&lt;td&gt;7,500+ rows, daily refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/institutions/{cik}/13f-holdings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Form 13F positions in whole-dollar USD&lt;/td&gt;
&lt;td&gt;222,019 holdings across 66 managers incl. Berkshire Hathaway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;meta.source_filing_url&lt;/code&gt; is present on every response across all endpoints.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;p&gt;Free tier: 50 calls/day, no credit card. Subscribe on the &lt;a href="https://rapidapi.com/hudson-enterprises-llc-hudson-enterprises-llc-default/api/filingrail" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt; and get your key within seconds.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Calls/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;1,500 (50/day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mega&lt;/td&gt;
&lt;td&gt;$199/mo&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;RapidAPI handles auth — every call needs &lt;code&gt;X-RapidAPI-Key&lt;/code&gt; and &lt;code&gt;X-RapidAPI-Host: filingrail.p.rapidapi.com&lt;/code&gt;. That's the full auth story.&lt;/p&gt;




&lt;h3&gt;
  
  
  Questions or issues
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;support@hudsonenterprisesllc.com&lt;/code&gt; — same-business-day response. Include the endpoint, parameters, and error response if applicable.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://hudsonenterprisesllc.com" rel="noopener noreferrer"&gt;Hudson Enterprises LLC&lt;/a&gt;, an Indiana software studio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
      <category>fintech</category>
    </item>
  </channel>
</rss>
