<?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: locaihost data</title>
    <description>The latest articles on DEV Community by locaihost data (@locaihost_data).</description>
    <link>https://dev.to/locaihost_data</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%2F4175321%2F5eda2421-ae2c-4ac1-9c9b-b0d056b0eb42.png</url>
      <title>DEV Community: locaihost data</title>
      <link>https://dev.to/locaihost_data</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/locaihost_data"/>
    <language>en</language>
    <item>
      <title>How the top 1,000 websites handle email security in 2026: DMARC, SPF, BIMI and who hosts their email</title>
      <dc:creator>locaihost data</dc:creator>
      <pubDate>Sat, 10 Oct 2026 14:47:39 +0000</pubDate>
      <link>https://dev.to/locaihost_data/how-the-top-1000-websites-handle-email-security-in-2026-dmarc-spf-bimi-and-who-hosts-their-email-4g59</link>
      <guid>https://dev.to/locaihost_data/how-the-top-1000-websites-handle-email-security-in-2026-dmarc-spf-bimi-and-who-hosts-their-email-4g59</guid>
      <description>&lt;p&gt;Anyone can put any domain in the From line of an email. Three DNS records decide whether the receiving server believes it. SPF lists the servers allowed to send for a domain. DMARC tells receivers what to do when a message fails those checks: nothing (&lt;code&gt;p=none&lt;/code&gt;), send it to spam (&lt;code&gt;p=quarantine&lt;/code&gt;) or refuse it (&lt;code&gt;p=reject&lt;/code&gt;). BIMI then lets a domain that enforces DMARC show its logo in the inbox.&lt;/p&gt;

&lt;p&gt;Since February 2024, Google and Yahoo have required DMARC from anyone sending them bulk mail, so most large senders have published something. We wanted to know what they published. On 10 October 2026 we looked up the email DNS of the 1,000 most popular websites on the Tranco list (list &lt;code&gt;8PX5V&lt;/code&gt;) and recorded their policies and their mail hosts.&lt;/p&gt;

&lt;p&gt;Five findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;85% publish DMARC, but only 69% enforce it.&lt;/strong&gt; 84.5% of the 1,000 domains have a DMARC record. 69.4% set it to quarantine or reject for all mail, and 47.4% reject outright. The other 13.6% publish &lt;code&gt;p=none&lt;/code&gt;, which only monitors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rank matters a little.&lt;/strong&gt; 62% of the top 100 reject spoofed mail, against 46% of domains ranked 101–1,000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SPF is almost universal and usually strict.&lt;/strong&gt; 89.6% publish SPF. Of those, 53.8% end it with &lt;code&gt;-all&lt;/code&gt; (hard fail) and 40.4% with &lt;code&gt;~all&lt;/code&gt; (soft fail).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BIMI has taken off; MTA-STS hasn't.&lt;/strong&gt; 21.3% publish a BIMI record, against only 4.5% for MTA-STS, which forces encrypted delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google hosts more of this mail than anyone.&lt;/strong&gt; Of the 875 domains that accept mail, 37.3% use Google Workspace and 17.4% Microsoft 365. Proofpoint filters 9.0% before the mail reaches the mailbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DMARC and SPF by rank
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Top 100&lt;/th&gt;
&lt;th&gt;101–1,000&lt;/th&gt;
&lt;th&gt;All&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DMARC record&lt;/td&gt;
&lt;td&gt;87.0%&lt;/td&gt;
&lt;td&gt;84.2%&lt;/td&gt;
&lt;td&gt;84.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DMARC enforced&lt;/td&gt;
&lt;td&gt;76.0%&lt;/td&gt;
&lt;td&gt;68.7%&lt;/td&gt;
&lt;td&gt;69.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DMARC &lt;code&gt;p=reject&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;62.0%&lt;/td&gt;
&lt;td&gt;45.8%&lt;/td&gt;
&lt;td&gt;47.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SPF record&lt;/td&gt;
&lt;td&gt;93.0%&lt;/td&gt;
&lt;td&gt;89.2%&lt;/td&gt;
&lt;td&gt;89.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BIMI record&lt;/td&gt;
&lt;td&gt;27.0%&lt;/td&gt;
&lt;td&gt;20.7%&lt;/td&gt;
&lt;td&gt;21.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MTA-STS policy&lt;/td&gt;
&lt;td&gt;11.0%&lt;/td&gt;
&lt;td&gt;3.8%&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;"Enforced" means quarantine or reject, applied to 100% of mail. Rank is the position among the 1,000 domains we kept, in Tranco order.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Across all 1,000 domains, the DMARC policy split was 474 at reject, 235 at quarantine and 136 at &lt;code&gt;none&lt;/code&gt;. 155 had no record. Few hedged: only 15 of the 845 records (1.8%) applied their policy to less than 100% of mail. 24 (2.8%) rejected spoofs of the main domain but used a weaker policy for its subdomains.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;p=none&lt;/code&gt; record is usually a first step. A domain watches the reports, finds its legitimate senders, then tightens the policy. Most of the 136 &lt;code&gt;p=none&lt;/code&gt; domains are doing that: 85% of them ask for aggregate reports. Some very large names are still there, though, including &lt;code&gt;samsung.com&lt;/code&gt; and &lt;code&gt;yandex.ru&lt;/code&gt;, and Microsoft's consumer mail domains &lt;code&gt;outlook.com&lt;/code&gt;, &lt;code&gt;live.com&lt;/code&gt; and &lt;code&gt;msn.com&lt;/code&gt;. For consumer mailbox domains, &lt;code&gt;p=none&lt;/code&gt; is often deliberate. Mailing lists and forwarding break authentication, and a strict policy would bounce real mail. A company domain doesn't have that excuse.&lt;/p&gt;

&lt;p&gt;The SPF split matters less than it looks. Under DMARC, a receiver judges a message by the DMARC result, so &lt;code&gt;-all&lt;/code&gt; versus &lt;code&gt;~all&lt;/code&gt; mostly matters to receivers that don't check DMARC. 37 domains (4.1% of those with SPF) have no &lt;code&gt;all&lt;/code&gt; of their own and hand over to another record with &lt;code&gt;redirect=&lt;/code&gt;, and 15 use the neutral &lt;code&gt;?all&lt;/code&gt;. None of the 1,000 used &lt;code&gt;+all&lt;/code&gt;, which would let any server send as the domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who hosts the mail
&lt;/h2&gt;

&lt;p&gt;Of the 1,000 domains, 875 have an MX record, so they accept mail. The rest are mostly link shorteners, download hosts and developer infrastructure.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mailbox host&lt;/th&gt;
&lt;th&gt;Domains&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Workspace&lt;/td&gt;
&lt;td&gt;326&lt;/td&gt;
&lt;td&gt;37.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft 365&lt;/td&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;17.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Own mail servers&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;td&gt;16.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unrecognised MX host&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;12.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway, mailbox hidden&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;9.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon WorkMail / SES&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tencent Exmail&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alibaba Mail, Yandex 360&lt;/td&gt;
&lt;td&gt;6 each&lt;/td&gt;
&lt;td&gt;0.7% each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zoho Mail&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other named providers (12)&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;3.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Base: the 875 domains that accept mail. "Own mail servers" means MX hosts under the domain's own name. Google's and Microsoft's own domains count under their own services.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The rows that are not Google or Microsoft deserve a closer look. "Own mail servers" means the MX host is under the domain's own name, as with &lt;code&gt;apple.com&lt;/code&gt; and &lt;code&gt;paypal.com&lt;/code&gt;. Most of the "unrecognised" group is the same thing one step removed. &lt;code&gt;imdb.com&lt;/code&gt; receives mail on &lt;code&gt;amazon.com&lt;/code&gt; servers, &lt;code&gt;webex.com&lt;/code&gt; on &lt;code&gt;cisco.com&lt;/code&gt; servers and &lt;code&gt;vk.com&lt;/code&gt; on &lt;code&gt;mail.ru&lt;/code&gt; servers. Together, the two groups make up 28.8% of the domains that accept mail.&lt;/p&gt;

&lt;p&gt;In front of the mailbox, 124 domains (14.2% of those accepting mail) use a security gateway that filters inbound mail first:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Inbound gateway&lt;/th&gt;
&lt;th&gt;Domains&lt;/th&gt;
&lt;th&gt;Share of the 875&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Proofpoint&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;9.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mimecast&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;2.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cisco Secure Email&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;1.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Five others&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;None detected&lt;/td&gt;
&lt;td&gt;751&lt;/td&gt;
&lt;td&gt;85.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The five others: Broadcom and Trend Micro (3 each), Barracuda (2), Sophos and Hornetsecurity (1 each).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Behind those gateways, SPF identifies the mailbox in 43 of the 124 cases: 24 use Microsoft 365 and 19 Google Workspace. The rest list only the gateway or their own servers in SPF, so the mailbox host stays hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who enforces DMARC
&lt;/h2&gt;

&lt;p&gt;Enforcement differs by mail host. Domains on Google Workspace enforce DMARC more often than those on Microsoft 365. Domains behind a gateway enforce it most often. This is a correlation, not a cause: buying Proofpoint and setting &lt;code&gt;p=reject&lt;/code&gt; both point to a company with a dedicated email-security team.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mail host&lt;/th&gt;
&lt;th&gt;Domains&lt;/th&gt;
&lt;th&gt;DMARC enforced&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gateway, mailbox hidden&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;86.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Workspace&lt;/td&gt;
&lt;td&gt;326&lt;/td&gt;
&lt;td&gt;82.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft 365&lt;/td&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;72.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unrecognised MX host&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;67.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Own mail servers&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;td&gt;66.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No MX (accepts no mail)&lt;/td&gt;
&lt;td&gt;125&lt;/td&gt;
&lt;td&gt;37.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last row is the weakest point. A domain that never sends mail can still be forged, and attackers like such domains because nobody watches them. The standard lock-down is two records: &lt;code&gt;v=spf1 -all&lt;/code&gt; (no server may send) and DMARC &lt;code&gt;p=reject&lt;/code&gt;. Only 31 of the 125 non-mail domains (24.8%) have both. 76 (60.8%) have either no DMARC record or &lt;code&gt;p=none&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By top-level domain, &lt;code&gt;.com&lt;/code&gt; led: 75.0% of its 596 domains enforce DMARC and 51.0% reject. Country-code domains followed at 63.5% (159 domains), then government, education and international bodies at 60.0% (35), and &lt;code&gt;.org&lt;/code&gt; and &lt;code&gt;.net&lt;/code&gt; at 57.0% (128).&lt;/p&gt;

&lt;h2&gt;
  
  
  Who reads the reports
&lt;/h2&gt;

&lt;p&gt;DMARC lets a domain ask receivers for daily aggregate reports (the &lt;code&gt;rua&lt;/code&gt; tag). 91.5% of DMARC records ask for them. 63.6% send them to an outside service, usually a vendor that turns the XML into dashboards. Counting each domain once per vendor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Report receiver&lt;/th&gt;
&lt;th&gt;Domains&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valimail (&lt;code&gt;vali.email&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proofpoint&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dmarcian&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agari (Fortra)&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mimecast DMARC Analyzer&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Red Sift OnDMARC&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postmark&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;13 domains send their reports to &lt;code&gt;dhs.gov&lt;/code&gt;, the collection point used by US federal civilian agencies. 11 of them are agencies such as &lt;code&gt;nasa.gov&lt;/code&gt; and &lt;code&gt;whitehouse.gov&lt;/code&gt;, and the other two are the companies Box and Dynatrace. Another 22 report to &lt;code&gt;google.com&lt;/code&gt;, all of them Google properties such as &lt;code&gt;youtube.com&lt;/code&gt; and &lt;code&gt;android.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS and CDN
&lt;/h2&gt;

&lt;p&gt;The same lookups show who runs the infrastructure. AWS Route 53 answers DNS for 24.6% of the 1,000 domains. Cloudflare answers for 16.3%, and 15.7% run their own name servers. Akamai answers for 9.4% and NS1 for 4.4%. 31.2% publish a CAA record that limits which certificate authorities may issue for them. Of the 902 domains with a homepage, Cloudflare serves 25.7%, Akamai and Amazon CloudFront 14.1% each, and Fastly 9.0%. We detected no CDN for the other 33.8%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology and caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domains.&lt;/strong&gt; Tranco list &lt;a href="https://tranco-list.eu/list/8PX5V/1000000" rel="noopener noreferrer"&gt;&lt;code&gt;8PX5V&lt;/code&gt;&lt;/a&gt;, generated 9 October 2026 from 30 days (10 September to 9 October) of Chrome UX Report, Farsight, Majestic, Cloudflare Radar and Cisco Umbrella data, as registrable domains. We removed 73 infrastructure host names listed by hand (CDN, API and telemetry domains such as &lt;code&gt;gstatic.com&lt;/code&gt; and &lt;code&gt;akamaiedge.net&lt;/code&gt;). We then dropped any domain that neither served a homepage nor had an MX record: 197 more, mostly CDN and device-telemetry hosts. A homepage behind a bot wall (HTTP 401, 403 or 429) counts as served. The first 1,000 remaining domains, down to Tranco rank 1,272, are the sample. The script lists every exclusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot.&lt;/strong&gt; All lookups ran on 10 October 2026, 14:03–14:40 UTC, from Apify's cloud. DNS records change, so this is one day's picture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What "detected" means.&lt;/strong&gt; A record counts if it exists in public DNS and parses: SPF is a &lt;code&gt;v=spf1&lt;/code&gt; TXT record at the domain, DMARC a &lt;code&gt;v=DMARC1&lt;/code&gt; record at &lt;code&gt;_dmarc&lt;/code&gt;, BIMI a &lt;code&gt;v=BIMI1&lt;/code&gt; record at &lt;code&gt;default._bimi&lt;/code&gt;, MTA-STS a &lt;code&gt;v=STSv1&lt;/code&gt; record at &lt;code&gt;_mta-sts&lt;/code&gt;. We didn't check BIMI logos or certificates, and we didn't fetch MTA-STS policy files. Mail hosts and gateways are matched from MX host names, and from SPF includes when a gateway hides the mailbox. A host we have no rule for is reported as unrecognised, not guessed. Domains with a null MX (RFC 7505) count as having no MX.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-check.&lt;/strong&gt; About an hour after the run, we re-resolved MX, SPF, DMARC and BIMI for all 1,298 domains through public resolvers. The results disagreed on 7 MX answers (all of them null MX, which the two tools count differently), 2 SPF records, 2 DMARC policies and no BIMI records. One domain, &lt;code&gt;indeed.com&lt;/code&gt;, answered our cloud resolver with &lt;code&gt;0.0.0.0&lt;/code&gt; and no MX. It was therefore dropped from the sample, though it does accept mail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible.&lt;/strong&gt; Four Node.js scripts &lt;a href="https://locaihost.org/blog/email-security-top-1000/analysis/email-security-2026-fetch.mjs" rel="noopener noreferrer"&gt;fetch the list&lt;/a&gt;, &lt;a href="https://locaihost.org/blog/email-security-top-1000/analysis/email-security-2026-run.mjs" rel="noopener noreferrer"&gt;run the lookups&lt;/a&gt;, &lt;a href="https://locaihost.org/blog/email-security-top-1000/analysis/email-security-2026.mjs" rel="noopener noreferrer"&gt;compute every figure&lt;/a&gt; (&lt;a href="https://locaihost.org/blog/email-security-top-1000/analysis/results.json" rel="noopener noreferrer"&gt;results.json&lt;/a&gt;) and &lt;a href="https://locaihost.org/blog/email-security-top-1000/analysis/email-security-2026-check.mjs" rel="noopener noreferrer"&gt;run the cross-check&lt;/a&gt;. We use only company domains, and we collect no personal data. Report addresses are reduced to the receiving domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Source: Tranco list 8PX5V (tranco-list.eu); DNS, HTTP and TLS lookups by locaihost data, 10 October 2026.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;The data came from our &lt;a href="https://apify.com/locaihost/domain-intel" rel="noopener noreferrer"&gt;Domain Intel Actor&lt;/a&gt; on Apify, which returns these fields (email host, gateway, SPF, DMARC, BIMI, MTA-STS, DNS, CDN, TLS) for any list of domains. To check your own suppliers, customers or portfolio the same way, it costs $3 per 1,000 domains.&lt;/p&gt;

</description>
      <category>security</category>
      <category>email</category>
      <category>dns</category>
      <category>data</category>
    </item>
    <item>
      <title>Who actually sponsors H-1B workers in 2026, and what they offer to pay</title>
      <dc:creator>locaihost data</dc:creator>
      <pubDate>Sat, 10 Oct 2026 13:56:26 +0000</pubDate>
      <link>https://dev.to/locaihost_data/who-actually-sponsors-h-1b-workers-in-2026-and-what-they-offer-to-pay-591e</link>
      <guid>https://dev.to/locaihost_data/who-actually-sponsors-h-1b-workers-in-2026-and-what-they-offer-to-pay-591e</guid>
      <description>&lt;p&gt;Every H-1B petition starts with a Labor Condition Application (LCA) filed with the US Department of Labor. On it, the employer gives the job title, the worksite and the wage it will pay. The Labor Department publishes these forms every quarter. We combined all of them from 1 October 2023 to 30 June 2026 (fiscal year 2024 to the third quarter of FY2026), along with the PERM green-card filings for the same period. That comes to 1.57 million LCA cases and 374,000 PERM cases from 153,000 employers.&lt;/p&gt;

&lt;p&gt;Five findings stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The largest filers are cutting back, and most of the cut comes from outsourcing firms.&lt;/strong&gt; Comparing like with like (October to June of each year), certified LCAs fell 8.1% in FY2026. Twenty large IT-services firms filed 31.9% fewer and account for a third of the drop, though they filed only 8.7% of FY2025's applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Most sponsors file one application a year.&lt;/strong&gt; Of the 63,545 employers with a certified LCA in FY2025, 51.6% had exactly one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who you work for affects the wage more than the visa does.&lt;/strong&gt; The median offered wage for software-engineer titles was $203,350 at Meta and $112,986 at Tata Consultancy Services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Green-card sponsorship varies widely.&lt;/strong&gt; Intel had 32 PERM certifications for every 100 LCAs. Infosys had fewer than one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Denials are rare: 0.68%.&lt;/strong&gt; That figure says very little, and the reason is explained below.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who files the most
&lt;/h2&gt;

&lt;p&gt;In FY2025, the last complete year, the Labor Department certified 541,597 LCAs, 8.2% more than the 500,329 in FY2024. Of these, 529,432 were for H-1B. The rest were for the country-specific versions: 8,858 E-3 (Australia), 1,940 H-1B1 (Chile) and 1,367 H-1B1 (Singapore).&lt;/p&gt;

&lt;p&gt;The table ranks employers as they appear on the forms. These are legal entities, not corporate groups, so Amazon's four main filing entities are counted separately. Combined, the four filed 22,914 in FY2025.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Employer (legal entity)&lt;/th&gt;
&lt;th&gt;FY2024&lt;/th&gt;
&lt;th&gt;FY2025&lt;/th&gt;
&lt;th&gt;Oct–Jun FY25&lt;/th&gt;
&lt;th&gt;Oct–Jun FY26&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Median offered wage, FY2025&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Amazon.com Services&lt;/td&gt;
&lt;td&gt;14,584&lt;/td&gt;
&lt;td&gt;15,493&lt;/td&gt;
&lt;td&gt;12,630&lt;/td&gt;
&lt;td&gt;13,922&lt;/td&gt;
&lt;td&gt;+10.2%&lt;/td&gt;
&lt;td&gt;$154,600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Cognizant Technology Solutions US&lt;/td&gt;
&lt;td&gt;11,226&lt;/td&gt;
&lt;td&gt;11,090&lt;/td&gt;
&lt;td&gt;8,600&lt;/td&gt;
&lt;td&gt;5,774&lt;/td&gt;
&lt;td&gt;−32.9%&lt;/td&gt;
&lt;td&gt;$109,782&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Ernst &amp;amp; Young U.S.&lt;/td&gt;
&lt;td&gt;9,828&lt;/td&gt;
&lt;td&gt;9,601&lt;/td&gt;
&lt;td&gt;8,161&lt;/td&gt;
&lt;td&gt;5,168&lt;/td&gt;
&lt;td&gt;−36.7%&lt;/td&gt;
&lt;td&gt;$143,517&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;9,491&lt;/td&gt;
&lt;td&gt;9,215&lt;/td&gt;
&lt;td&gt;7,417&lt;/td&gt;
&lt;td&gt;5,159&lt;/td&gt;
&lt;td&gt;−30.4%&lt;/td&gt;
&lt;td&gt;$170,766&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;8,815&lt;/td&gt;
&lt;td&gt;8,800&lt;/td&gt;
&lt;td&gt;7,861&lt;/td&gt;
&lt;td&gt;7,423&lt;/td&gt;
&lt;td&gt;−5.6%&lt;/td&gt;
&lt;td&gt;$184,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Tata Consultancy Services&lt;/td&gt;
&lt;td&gt;9,611&lt;/td&gt;
&lt;td&gt;7,796&lt;/td&gt;
&lt;td&gt;6,491&lt;/td&gt;
&lt;td&gt;2,880&lt;/td&gt;
&lt;td&gt;−55.6%&lt;/td&gt;
&lt;td&gt;$85,634&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Meta Platforms&lt;/td&gt;
&lt;td&gt;5,911&lt;/td&gt;
&lt;td&gt;6,627&lt;/td&gt;
&lt;td&gt;5,717&lt;/td&gt;
&lt;td&gt;5,278&lt;/td&gt;
&lt;td&gt;−7.7%&lt;/td&gt;
&lt;td&gt;$203,350&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Apple&lt;/td&gt;
&lt;td&gt;3,460&lt;/td&gt;
&lt;td&gt;6,283&lt;/td&gt;
&lt;td&gt;4,822&lt;/td&gt;
&lt;td&gt;4,870&lt;/td&gt;
&lt;td&gt;+1.0%&lt;/td&gt;
&lt;td&gt;$175,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Infosys&lt;/td&gt;
&lt;td&gt;5,896&lt;/td&gt;
&lt;td&gt;5,757&lt;/td&gt;
&lt;td&gt;3,876&lt;/td&gt;
&lt;td&gt;4,568&lt;/td&gt;
&lt;td&gt;+17.9%&lt;/td&gt;
&lt;td&gt;$92,581&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Deloitte Consulting&lt;/td&gt;
&lt;td&gt;2,693&lt;/td&gt;
&lt;td&gt;4,255&lt;/td&gt;
&lt;td&gt;3,215&lt;/td&gt;
&lt;td&gt;2,622&lt;/td&gt;
&lt;td&gt;−18.4%&lt;/td&gt;
&lt;td&gt;$138,423&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Certified LCAs by decision date. "Oct–Jun" compares the same nine months of each fiscal year, because the FY2026 data ends on 30 June 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Like-for-like, total certified LCAs fell from 436,875 to 401,397. Most of the drop came early in the year. Compared with a year before, the October–December 2025 quarter was down 22.2%, January–March was down 7.7% and April–June was down 2.3%. A presidential proclamation of 19 September 2025 added a $100,000 payment to certain new H-1B petitions just before that first quarter. (A federal court vacated the guidance implementing that payment on 8 June 2026, and the litigation is ongoing.) The data shows when filings fell but not why, and an LCA is filed before the petition, so these figures can't separate the petitions the payment applied to from those it didn't.&lt;/p&gt;

&lt;p&gt;Change wasn't spread evenly. Of the 68 employers with at least 500 certified LCAs in October–June FY2025, 44 filed fewer in the same months of FY2026. The largest falls were IBM (−63.8%), &lt;a href="https://locaihost.org/visa-sponsors/tata-consultancy-services/" rel="noopener noreferrer"&gt;Tata Consultancy Services&lt;/a&gt; (−55.6%), Qualcomm (−51.8%), LTIMindtree (−47.6%) and HCL America (−44.0%). &lt;a href="https://locaihost.org/visa-sponsors/infosys/" rel="noopener noreferrer"&gt;Infosys&lt;/a&gt; went the other way, up 17.9%. The largest rise was at &lt;a href="https://locaihost.org/visa-sponsors/nvidia/" rel="noopener noreferrer"&gt;Nvidia&lt;/a&gt;, up 71.2% to 2,390, making it the 15th-largest filer so far this year. Grandison Management rose 37.4%. More than half of its FY2025 filings (54.2%) were for physical therapists, with a median offered wage of $77,480. The big filers aren't all in tech.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most sponsors are small
&lt;/h2&gt;

&lt;p&gt;The ten largest filers accounted for 15.7% of certified LCAs in FY2025. The top 100 accounted for 34.0%, and the top 1,000 for 55.5%. At the other end, 32,765 employers filed exactly one certified LCA. Including those that filed two, that's 43,454 employers, or 68.4% of all sponsors, together accounting for 10.0% of filings. The median sponsor filed one.&lt;/p&gt;

&lt;p&gt;For job seekers, this means a company's absence from the top-100 lists doesn't show it won't sponsor. Most sponsors have never been on those lists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Volume and pay by type of employer
&lt;/h2&gt;

&lt;p&gt;To compare kinds of employer, we sorted them into groups. Four groups are lists of named employers we chose by hand. The other two are matched by name: any employer with "university" or "college" in its name, and any with "hospital", "medical center", "health system", "clinic" or "children's". Both methods are crude, and the full lists are in the analysis script.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group (FY2025)&lt;/th&gt;
&lt;th&gt;Employers&lt;/th&gt;
&lt;th&gt;Certified LCAs&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;th&gt;Median offered wage&lt;/th&gt;
&lt;th&gt;Oct–Jun change FY25→FY26&lt;/th&gt;
&lt;th&gt;PERM per 100 LCAs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Big tech (named list)&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;73,351&lt;/td&gt;
&lt;td&gt;13.5%&lt;/td&gt;
&lt;td&gt;$169,000&lt;/td&gt;
&lt;td&gt;−6.3%&lt;/td&gt;
&lt;td&gt;17.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IT services &amp;amp; outsourcing (named list)&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;47,274&lt;/td&gt;
&lt;td&gt;8.7%&lt;/td&gt;
&lt;td&gt;$104,728&lt;/td&gt;
&lt;td&gt;−31.9%&lt;/td&gt;
&lt;td&gt;8.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Universities &amp;amp; colleges (by name)&lt;/td&gt;
&lt;td&gt;1,325&lt;/td&gt;
&lt;td&gt;25,953&lt;/td&gt;
&lt;td&gt;4.8%&lt;/td&gt;
&lt;td&gt;$81,100&lt;/td&gt;
&lt;td&gt;−5.4%&lt;/td&gt;
&lt;td&gt;9.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Big Four &amp;amp; strategy consulting (named list)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;18,899&lt;/td&gt;
&lt;td&gt;3.5%&lt;/td&gt;
&lt;td&gt;$141,000&lt;/td&gt;
&lt;td&gt;−27.7%&lt;/td&gt;
&lt;td&gt;6.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banks &amp;amp; card issuers (named list)&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;11,949&lt;/td&gt;
&lt;td&gt;2.2%&lt;/td&gt;
&lt;td&gt;$150,000&lt;/td&gt;
&lt;td&gt;−4.3%&lt;/td&gt;
&lt;td&gt;15.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hospitals &amp;amp; clinics (by name)&lt;/td&gt;
&lt;td&gt;1,152&lt;/td&gt;
&lt;td&gt;9,099&lt;/td&gt;
&lt;td&gt;1.7%&lt;/td&gt;
&lt;td&gt;$86,080&lt;/td&gt;
&lt;td&gt;−6.8%&lt;/td&gt;
&lt;td&gt;11.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Everyone else&lt;/td&gt;
&lt;td&gt;61,006&lt;/td&gt;
&lt;td&gt;355,072&lt;/td&gt;
&lt;td&gt;65.6%&lt;/td&gt;
&lt;td&gt;$114,213&lt;/td&gt;
&lt;td&gt;−4.7%&lt;/td&gt;
&lt;td&gt;28.7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;PERM per 100 LCAs uses FY2024 + FY2025 for both counts.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two groups account for most of this year's fall. The 20 IT-services firms filed 11,776 fewer certified LCAs in October–June than a year earlier, 33.2% of the total decline. The Big Four and two strategy firms filed 4,356 fewer, 12.3% of the decline. Both shares are far larger than the groups' share of filings. The 61,006 employers in "everyone else" fell by 4.7%.&lt;/p&gt;

&lt;p&gt;Universities filed 18.7% more LCAs in FY2025 than in FY2024. Their median offered wage, $81,100, is the lowest of any group, because their most common titles are assistant professor and postdoctoral researcher. Most universities, and the non-profit research bodies linked to them, are exempt from the annual H-1B cap.&lt;/p&gt;

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

&lt;p&gt;These are offered wages: the lower end of the range written on the form, converted to a yearly figure. The job title "Software Engineer" alone had 28,123 certified LCAs from 4,588 employers in FY2025, with a median of $139,000. Across all software-engineer and software-developer titles, there were 105,098 filings with a median of $137,500. The median at each large filer varies far more than that single figure suggests:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Employer&lt;/th&gt;
&lt;th&gt;Software-engineer filings&lt;/th&gt;
&lt;th&gt;25th pct&lt;/th&gt;
&lt;th&gt;Median&lt;/th&gt;
&lt;th&gt;75th pct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Salesforce&lt;/td&gt;
&lt;td&gt;1,082&lt;/td&gt;
&lt;td&gt;$178,743&lt;/td&gt;
&lt;td&gt;$211,686&lt;/td&gt;
&lt;td&gt;$243,862&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://locaihost.org/visa-sponsors/meta-platforms/" rel="noopener noreferrer"&gt;Meta Platforms&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3,670&lt;/td&gt;
&lt;td&gt;$182,000&lt;/td&gt;
&lt;td&gt;$203,350&lt;/td&gt;
&lt;td&gt;$226,485&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://locaihost.org/visa-sponsors/google/" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5,604&lt;/td&gt;
&lt;td&gt;$162,000&lt;/td&gt;
&lt;td&gt;$186,000&lt;/td&gt;
&lt;td&gt;$200,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://locaihost.org/visa-sponsors/microsoft/" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5,700&lt;/td&gt;
&lt;td&gt;$153,700&lt;/td&gt;
&lt;td&gt;$171,258&lt;/td&gt;
&lt;td&gt;$191,461&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon.com Services&lt;/td&gt;
&lt;td&gt;3,632&lt;/td&gt;
&lt;td&gt;$137,259&lt;/td&gt;
&lt;td&gt;$160,000&lt;/td&gt;
&lt;td&gt;$180,710&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JPMorgan Chase&lt;/td&gt;
&lt;td&gt;1,886&lt;/td&gt;
&lt;td&gt;$140,650&lt;/td&gt;
&lt;td&gt;$156,200&lt;/td&gt;
&lt;td&gt;$175,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Walmart Associates&lt;/td&gt;
&lt;td&gt;2,366&lt;/td&gt;
&lt;td&gt;$120,000&lt;/td&gt;
&lt;td&gt;$141,751&lt;/td&gt;
&lt;td&gt;$153,301&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tata Consultancy Services&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;$105,997&lt;/td&gt;
&lt;td&gt;$112,986&lt;/td&gt;
&lt;td&gt;$127,504&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UST Global&lt;/td&gt;
&lt;td&gt;394&lt;/td&gt;
&lt;td&gt;$95,040&lt;/td&gt;
&lt;td&gt;$106,866&lt;/td&gt;
&lt;td&gt;$121,541&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;FY2025 certified LCAs whose title contains "software engineer", "software developer", "software development engineer" or "software engineering", or "SDE". Employers with at least 300 such filings with a wage; the full list of 38 is in the script output.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Titles are less useful than they look at the largest outsourcing firms. At Cognizant, 35.2% of FY2025 filings carry one internal job code, "Manager JC50 - Computer Systems Engineers/Architects". At TCS, 40.9% are simply "Architect", with a median of $84,115. Comparing roles across those firms is close to impossible.&lt;/p&gt;

&lt;p&gt;To find the best-paid jobs that many employers sponsor, we kept only titles with at least 500 FY2025 filings from at least 25 different employers. The top title wasn't in tech:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Job title&lt;/th&gt;
&lt;th&gt;Certified LCAs&lt;/th&gt;
&lt;th&gt;Employers&lt;/th&gt;
&lt;th&gt;Median offered wage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hospitalist&lt;/td&gt;
&lt;td&gt;673&lt;/td&gt;
&lt;td&gt;216&lt;/td&gt;
&lt;td&gt;$275,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Manager&lt;/td&gt;
&lt;td&gt;814&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;td&gt;$198,511&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Software Development Engineer III&lt;/td&gt;
&lt;td&gt;769&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;$198,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering Manager&lt;/td&gt;
&lt;td&gt;561&lt;/td&gt;
&lt;td&gt;292&lt;/td&gt;
&lt;td&gt;$196,888&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staff Software Engineer&lt;/td&gt;
&lt;td&gt;1,901&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;$177,437&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The lowest-paid common titles were teacher ($53,600), market research analyst ($62,900), medical technologist ($63,149) and the various postdoctoral titles ($65,000–$69,926).&lt;/p&gt;

&lt;h2&gt;
  
  
  Who also sponsors green cards
&lt;/h2&gt;

&lt;p&gt;An LCA supports a temporary visa. PERM labor certification is usually the first step toward an employer-sponsored green card. Dividing PERM certifications by LCAs gives a rough measure of how often an employer takes that second step. Across all employers in FY2024–FY2025, the figure was 23 per 100. For the biggest filers (at least 3,000 certified LCAs), it ranged from 32 down to almost none:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Employer&lt;/th&gt;
&lt;th&gt;Certified LCAs FY24–25&lt;/th&gt;
&lt;th&gt;PERM certified FY24–25&lt;/th&gt;
&lt;th&gt;PERM per 100 LCAs&lt;/th&gt;
&lt;th&gt;PERM by year: FY24 / FY25 / FY26 to June&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Intel&lt;/td&gt;
&lt;td&gt;6,524&lt;/td&gt;
&lt;td&gt;2,083&lt;/td&gt;
&lt;td&gt;31.9&lt;/td&gt;
&lt;td&gt;1,148 / 935 / 326&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple&lt;/td&gt;
&lt;td&gt;9,743&lt;/td&gt;
&lt;td&gt;2,645&lt;/td&gt;
&lt;td&gt;27.1&lt;/td&gt;
&lt;td&gt;1,421 / 1,224 / 33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nvidia&lt;/td&gt;
&lt;td&gt;3,185&lt;/td&gt;
&lt;td&gt;853&lt;/td&gt;
&lt;td&gt;26.8&lt;/td&gt;
&lt;td&gt;280 / 573 / 722&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;18,706&lt;/td&gt;
&lt;td&gt;4,660&lt;/td&gt;
&lt;td&gt;24.9&lt;/td&gt;
&lt;td&gt;1,734 / 2,926 / 1,600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;17,615&lt;/td&gt;
&lt;td&gt;1,607&lt;/td&gt;
&lt;td&gt;9.1&lt;/td&gt;
&lt;td&gt;1,607 / 0 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon.com Services&lt;/td&gt;
&lt;td&gt;30,077&lt;/td&gt;
&lt;td&gt;2,130&lt;/td&gt;
&lt;td&gt;7.1&lt;/td&gt;
&lt;td&gt;2,129 / 1 / 56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognizant Technology Solutions US&lt;/td&gt;
&lt;td&gt;22,316&lt;/td&gt;
&lt;td&gt;1,235&lt;/td&gt;
&lt;td&gt;5.5&lt;/td&gt;
&lt;td&gt;1,235 / 0 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ernst &amp;amp; Young U.S.&lt;/td&gt;
&lt;td&gt;19,429&lt;/td&gt;
&lt;td&gt;281&lt;/td&gt;
&lt;td&gt;1.4&lt;/td&gt;
&lt;td&gt;1 / 280 / 568&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infosys&lt;/td&gt;
&lt;td&gt;11,653&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;td&gt;0.7&lt;/td&gt;
&lt;td&gt;87 / 0 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The yearly column shows something the ratio hides. Monthly certifications stopped at several large filers. Google's last month with 50 or more was February 2024, and it has had three in the 28 months since. Amazon.com Services fell below five a month from May 2024 to February 2026 and has risen slowly since (56 in October–June). Cognizant has had none since July 2024 and &lt;a href="https://locaihost.org/visa-sponsors/infosys/" rel="noopener noreferrer"&gt;Infosys&lt;/a&gt; none since January 2024. Because PERM cases take more than a year to decide, a decision gap from 2024 points to a pause in filings around 2023, which matches what Google and Amazon were widely reported to have done that year. The data shows only the gap, not its cause.&lt;/p&gt;

&lt;p&gt;One warning about this measure: a company may file its LCAs under one legal entity and its PERM cases under another. Fidelity Technology Group has 3,847 LCAs and no PERM cases, while FMR LLC, another Fidelity entity, has 778 PERM certifications in FY2024–FY2025. Check every entity a company uses before concluding that it doesn't sponsor green cards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the jobs are
&lt;/h2&gt;

&lt;p&gt;California accounted for 18.7% of FY2025 certified LCAs by worksite, followed by Texas (15.3%), New York (8.3%), Washington (6.3%) and New Jersey (5.0%). Together those five states had 53.6% of filings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why denials tell you nothing
&lt;/h2&gt;

&lt;p&gt;In FY2025, 3,918 LCAs were denied, against 541,597 certified and 31,083 certified then withdrawn. That's a denial rate of 0.68%, and it has been under 1% in each year of the data. An LCA is a statement by the employer: that it will pay at least the required wage, and that hiring the worker won't harm the working conditions of other employees at the site. The Labor Department mostly checks that the form is complete and free of obvious errors. It doesn't decide whether the worker qualifies or whether the job is real. United States Citizenship and Immigration Services (USCIS) decides that later, when it reviews the petition. PERM is checked more closely, and its denial rate was 1.78% in FY2025. Neither rate tells you about visa approvals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology and caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source.&lt;/strong&gt; US Department of Labor, Office of Foreign Labor Certification (OFLC), LCA and PERM disclosure files for FY2024 to FY2026 Q3 (1 October 2023 to 30 June 2026). The data is public domain. We are not affiliated with the Labor Department.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counting.&lt;/strong&gt; Every file was loaded and de-duplicated by case number, keeping the latest decision. Cases are counted in the fiscal year of the decision date. "Certified" excludes certified-then-withdrawn cases. FY2026 covers only nine months, so it is compared only with the same nine months of earlier years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A certified LCA is not a visa.&lt;/strong&gt; One worker can have several LCAs (for example, for a new worksite or an extension), and many LCAs never lead to a petition. LCA counts measure employer filing activity, not people hired or visas approved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offered wage is not pay.&lt;/strong&gt; We use the lower end of any wage range on the form, converted to a yearly figure (2,080 hours a year for hourly wages), and drop values below $10,000 or above $2 million. Bonuses and equity aren't included.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employers are legal entities.&lt;/strong&gt; Names are normalised, so "Google LLC" and "GOOGLE LLC" count as one employer. Separate entities of the same company are not merged, apart from the Amazon total we give above. Groups are our own choices, and the lists are in the script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy.&lt;/strong&gt; Only business fields are read: status, dates, visa class, employer, job title, wage, worksite state and number of positions. No information about workers, employer contacts, attorneys or form preparers is read or published, and employers that file under a person's own name are excluded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible.&lt;/strong&gt; Two Node.js scripts produce every figure here: &lt;a href="https://locaihost.org/blog/h1b-sponsors-2026/analysis/h1b-2026-extract.mjs" rel="noopener noreferrer"&gt;one re-reads the DOL files&lt;/a&gt; and &lt;a href="https://locaihost.org/blog/h1b-sponsors-2026/analysis/h1b-2026.mjs" rel="noopener noreferrer"&gt;the other computes the results&lt;/a&gt; (&lt;a href="https://locaihost.org/blog/h1b-sponsors-2026/analysis/results.json" rel="noopener noreferrer"&gt;results.json&lt;/a&gt;). Their case counts match our published employer index exactly (1,570,055 LCA and 373,878 PERM cases).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing here is legal or immigration advice. For your own case, talk to a qualified immigration attorney.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: US Department of Labor, Office of Foreign Labor Certification, LCA and PERM disclosure data (public domain).&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Each of the 2,000 largest sponsors has a free page at &lt;a href="https://locaihost.org/visa-sponsors/employers/" rel="noopener noreferrer"&gt;locaihost.org/visa-sponsors/employers/&lt;/a&gt; showing its filings by year, the titles it sponsors and the wages it filed. For example, see &lt;a href="https://locaihost.org/visa-sponsors/cognizant-technology-solutions/" rel="noopener noreferrer"&gt;Cognizant&lt;/a&gt; or &lt;a href="https://locaihost.org/visa-sponsors/google/" rel="noopener noreferrer"&gt;Google&lt;/a&gt;. To look up many companies at once, or to check live job listings for sponsorship evidence, the same data is available through an &lt;a href="https://apify.com/locaihost/visa-sponsors" rel="noopener noreferrer"&gt;Apify Actor&lt;/a&gt; at $5 per 1,000 results.&lt;/p&gt;

</description>
      <category>h1b</category>
      <category>data</category>
      <category>immigration</category>
      <category>career</category>
    </item>
    <item>
      <title>Designing Apify Actors that AI agents actually pick (and can afford)</title>
      <dc:creator>locaihost data</dc:creator>
      <pubDate>Sat, 10 Oct 2026 13:21:05 +0000</pubDate>
      <link>https://dev.to/locaihost_data/designing-apify-actors-that-ai-agents-actually-pick-and-can-afford-55m7</link>
      <guid>https://dev.to/locaihost_data/designing-apify-actors-that-ai-agents-actually-pick-and-can-afford-55m7</guid>
      <description>&lt;p&gt;Most Apify Actors are built for a person who opens the Store page, reads the README, fills in a form and clicks &lt;strong&gt;Start&lt;/strong&gt;. That person can scroll, guess and retry when something goes wrong.&lt;/p&gt;

&lt;p&gt;More and more callers are no longer people. An agent in Claude, Cursor or a custom loop connects to the Apify MCP server, searches the Store, reads one short card about your Actor, builds a JSON input from your input schema and runs it with a spending cap. It never sees your README screenshots. It can't ask what "Max results" means. If the run fails, it may simply move on to someone else's Actor.&lt;/p&gt;

&lt;p&gt;I'm Ryan Low. I publish pay-per-event Actors on the Apify Store as &lt;strong&gt;locaihost&lt;/strong&gt;: SEC EDGAR financials and insider trades, careers-page jobs from Greenhouse/Lever/Ashby/Personio/Teamtailor, UK/EU/Canada government tenders, and a couple of others. This post walks through one real agent call against my SEC EDGAR Actor, then the design decisions that make an Actor usable (and affordable) for an agent. All the code below is from the Actors as they ship. I've left out anything I haven't measured myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setup: one command
&lt;/h2&gt;

&lt;p&gt;Apify hosts an MCP server. In Claude Code you add it with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add apify https://mcp.apify.com/ &lt;span class="nt"&gt;-t&lt;/span&gt; http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time you use it, a browser OAuth flow opens and asks you to sign in to Apify, so there is no token to paste. After that the agent has tools such as &lt;code&gt;search-actors&lt;/code&gt;, &lt;code&gt;fetch-actor-details&lt;/code&gt;, &lt;code&gt;call-actor&lt;/code&gt; and &lt;code&gt;get-dataset-items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To pin an agent to specific Actors, you can pass them in the URL instead (&lt;code&gt;https://mcp.apify.com?tools=locaihost/sec-edgar,...&lt;/code&gt;). Each listed Actor then appears as its own tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What the agent actually sees
&lt;/h2&gt;

&lt;p&gt;Before writing anything for agents, I read the source of &lt;a href="https://github.com/apify/apify-mcp-server" rel="noopener noreferrer"&gt;apify-mcp-server&lt;/a&gt;. Three findings shaped everything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;search-actors&lt;/code&gt; is a thin wrapper over the Store search API&lt;/strong&gt; (&lt;code&gt;GET /v2/store?search=...&lt;/code&gt;). The MCP server does no re-ranking of its own, so agent ranking &lt;em&gt;is&lt;/em&gt; Store ranking: relevance plus the quality and popularity signals Apify uses for the Store (reliability, usage, reviews and so on).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The default &lt;code&gt;limit&lt;/code&gt; is 5.&lt;/strong&gt; Agents are told to search with 1–3 keywords like "SEC filings" or "insider trades". Only five cards come back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The card is built from your Actor's description, verbatim&lt;/strong&gt;, plus the input field list. Whatever you wrote in &lt;code&gt;description&lt;/code&gt; in &lt;code&gt;.actor/actor.json&lt;/code&gt; is the pitch an agent reads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I need to be honest about the first two points. My Actors are a few weeks old, with almost no usage. When I ran agent-style queries against the Store API, &lt;strong&gt;none of them appeared in the top 5 for any query I tested.&lt;/strong&gt; A brand-new Actor will not get picked through &lt;code&gt;search-actors&lt;/code&gt; no matter how clean its schema is. Usage builds rank, and rank builds usage, and there's no shortcut I've found.&lt;/p&gt;

&lt;p&gt;So why bother? Because search is only one way in. An agent can still reach a new Actor when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the user names it ("use locaihost/sec-edgar"),&lt;/li&gt;
&lt;li&gt;it is pinned in the MCP URL with &lt;code&gt;?tools=&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;it shows up in a README, a blog post or a published task that the user or agent has already read.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In all three cases the agent skips the search and goes straight to &lt;code&gt;fetch-actor-details&lt;/code&gt; and &lt;code&gt;call-actor&lt;/code&gt;. From there, the input schema, the failure modes and the charge floor decide whether the call works. That part you control on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A real call
&lt;/h2&gt;

&lt;p&gt;Here is the call Claude made against &lt;a href="https://apify.com/locaihost/sec-edgar" rel="noopener noreferrer"&gt;locaihost/sec-edgar&lt;/a&gt;, via the MCP &lt;code&gt;call-actor&lt;/code&gt; tool, when I asked for NVIDIA's recent insider trades:&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;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"locaihost/sec-edgar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&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;"companies"&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;"NVDA"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"modes"&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;"insiderTrades"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxResults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onlyNew"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;"callOptions"&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;"maxTotalChargeUsd"&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.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;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&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;The agent made two good choices on its own. It set &lt;code&gt;maxResults&lt;/code&gt; to the number of rows it actually needed, and it capped the run's total spend at ten cents with &lt;code&gt;maxTotalChargeUsd&lt;/code&gt;. Both of those only work if the Actor respects them, which I come back to below.&lt;/p&gt;

&lt;p&gt;The result, abridged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status:        SUCCEEDED
runtime:       4.24 s
compute units: 0.0012
items:         5
fields (29):   recordType, cik, ticker, companyName, form, filingDate,
               accessionNumber, insiderName, insiderCik, insiderType,
               insiderRole, isDirector, isOfficer, isTenPercentOwner,
               officerTitle, is10b51Plan, securityTitle, transactionDate,
               transactionCode, transactionLabel, acquiredDisposed, shares,
               pricePerShare, value, sharesOwnedAfter, directOrIndirect,
               ownershipNature, filingUrl, scrapedAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five rows, each one a Form 4 transaction, in about four seconds. The field names do most of the explaining. An agent working out "who sold, and how much" filters on &lt;code&gt;acquiredDisposed: "D"&lt;/code&gt; with &lt;code&gt;transactionCode: "S"&lt;/code&gt; and sums &lt;code&gt;value&lt;/code&gt;. It doesn't need a README for that.&lt;/p&gt;

&lt;p&gt;One field deserves a closer look: &lt;strong&gt;&lt;code&gt;insiderName&lt;/code&gt; is null for people.&lt;/strong&gt; Form 4 filings name the individual filer, but the Actor deliberately never outputs a natural person's name. An insider is identified by role (&lt;code&gt;insiderRole: "Officer (CFO)"&lt;/code&gt;, &lt;code&gt;isDirector&lt;/code&gt;, &lt;code&gt;isTenPercentOwner&lt;/code&gt;). Names are output only for organisations such as funds, LLCs and holding companies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Legal-entity markers. A name matching none of these is treated as a person and is never output.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isEntityName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;ENTITY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b(&lt;/span&gt;&lt;span class="sr"&gt;FAMILY|REVOCABLE|IRREVOCABLE|LIVING&lt;/span&gt;&lt;span class="se"&gt;)\b&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ...inside toTradeRecords():&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;isEntityName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nl"&gt;insiderName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;; &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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;insiderType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;entity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;individual&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;insiderRole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roleLabel&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Family and revocable trusts count as people. Reporting-owner addresses are never read at all. For agents this matters more than for human users: an agent will happily copy whatever you return into a CRM, an email or a report. If personal data is never in the dataset, it can never leak from there. I made "business data only" a hard rule for all my Actors (I'm in Singapore, and PDPA is the obvious concern). For insider trading analysis, role and size are what you need anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Input schema: one obvious required field
&lt;/h2&gt;

&lt;p&gt;The input schema becomes the agent's tool definition. Mine has eleven fields, but an agent only has to understand one:&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="nl"&gt;"companies"&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Companies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"array"&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;"One per line: a US ticker (AAPL, BRK.B), an SEC CIK number (320193) or a company name (Microsoft). Any company that files with the SEC. Up to 500."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stringList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prefill"&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;"AAPL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MSFT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NVDA"&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"required"&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;"companies"&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;Every other field has a sensible &lt;code&gt;default&lt;/code&gt;: all three modes, the last 90 days, 8 periods, newest filings first. A minimal valid call is &lt;code&gt;{"companies": ["NVDA"]}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note the &lt;strong&gt;&lt;code&gt;prefill&lt;/code&gt;, not &lt;code&gt;default&lt;/code&gt;&lt;/strong&gt;, on the required field. This is the easiest trap to fall into. A required field that also has a &lt;code&gt;default&lt;/code&gt; silently loses its &lt;code&gt;required&lt;/code&gt; flag in the tool schema the agent receives. To the agent, the field looks optional. It then calls the Actor without it, the platform fills in your default, and the agent gets data for AAPL, MSFT and NVDA when it asked about something else. &lt;code&gt;prefill&lt;/code&gt; fills in the form for humans in the console and still shows an example value, but &lt;code&gt;required&lt;/code&gt; survives. &lt;a href="https://blog.apify.com/fixing-actors-invisible-to-agents/" rel="noopener noreferrer"&gt;Apify's own write-up on making Actors visible to agents&lt;/a&gt; puts it bluntly: a required field with a default is always a bug.&lt;/p&gt;

&lt;p&gt;A few smaller rules I follow in every schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Descriptions under 500 characters.&lt;/strong&gt; Field and Actor descriptions end up in the tool definition and the search card. Long ones get truncated or eat the agent's context. Put the essentials first: what to type, in what format, with one example.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accept what a model is likely to send.&lt;/strong&gt; &lt;code&gt;companies&lt;/code&gt; takes tickers, CIKs &lt;em&gt;or&lt;/em&gt; names. &lt;code&gt;sinceDate&lt;/code&gt; takes &lt;code&gt;2026-07-01&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; &lt;code&gt;"90 days"&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; &lt;code&gt;"6 months"&lt;/code&gt;. A model will try the natural phrasing first, so treat it as valid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arrays stay arrays.&lt;/strong&gt; &lt;code&gt;modes&lt;/code&gt; is an enum array with &lt;code&gt;enumTitles&lt;/code&gt;, not a comma-separated string the agent has to guess the format of.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Bad input should not mean FAILED
&lt;/h2&gt;

&lt;p&gt;What should happen when an agent sends &lt;code&gt;{"companies": ["NVIDIAA"]}&lt;/code&gt;, or a list where every entry is junk?&lt;/p&gt;

&lt;p&gt;My first versions called &lt;code&gt;Actor.fail()&lt;/code&gt; with a clear message. That seemed right until I ran a robustness pass and noticed two problems. A FAILED run counts against the Actor's success rate, which feeds the quality score, which feeds search rank (the same rank agents see). And to an agent, FAILED looks like "this tool is broken", not "you sent a bad ticker".&lt;/p&gt;

&lt;p&gt;So invalid input now ends &lt;strong&gt;SUCCEEDED, with nothing charged and a message that says how to fix it&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unresolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Not found on EDGAR (use the ticker or CIK): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unresolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;refs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Unknown tickers are an input problem, not a crash: exit SUCCEEDED with nothing charged.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUMMARY&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="nx"&gt;since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;modes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;modes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;pushed&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="na"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unresolved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;companies&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;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&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="s2"&gt;`No companies to look up&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unresolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;` — not found on EDGAR: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unresolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Add tickers (AAPL), CIKs (320193) or company names (Microsoft).`&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;The exit message becomes the run's status message, which is part of the run details an agent can read back. An agent that reads "not found on EDGAR: NVIDIAA. Add tickers (AAPL)…" corrects itself on the next call. &lt;code&gt;Actor.fail()&lt;/code&gt; is kept for real failures only: if &lt;em&gt;every&lt;/em&gt; company lookup hit an upstream error, the run fails, because then something really is broken.&lt;/p&gt;

&lt;p&gt;The careers-jobs Actor goes one step further and rejects junk before fetching anything (sentences, markup, 300-character pastes), so prompt-injected garbage in the &lt;code&gt;companies&lt;/code&gt; array never turns into outbound requests or charges.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Pay-per-event charging you can trust
&lt;/h2&gt;

&lt;p&gt;All my Actors use pay-per-event pricing: a tiny start fee plus one event per record. For sec-edgar that's $2 per 1,000 records at the base tier, so the five-row NVIDIA call above cost about a cent. Agents work well with this model: they pay for exactly what they used, and &lt;code&gt;maxTotalChargeUsd&lt;/code&gt; gives them a hard ceiling.&lt;/p&gt;

&lt;p&gt;There is a catch in the SDK. Under pay-per-event, &lt;code&gt;Actor.pushData(items, eventName)&lt;/code&gt; returns a &lt;code&gt;ChargeResult&lt;/code&gt; telling you how many items were charged, and therefore stored. When the user's charge limit is reached mid-batch, the SDK drops the rest. My Actors batch pushes (100 items per API call), and for batches &lt;strong&gt;the returned ChargeResult can undercount or overcount&lt;/strong&gt;. I don't fully understand why, though the client chunking requests internally is the likely cause. That matters because my "only new since last run" mode remembers which items were delivered. Trust a wrong count, and a user either pays for an item they never got or never sees it again.&lt;/p&gt;

&lt;p&gt;The fix is to stop trusting the return value and measure the charging manager's counter before and after. This is the actual push function from the careers-jobs Actor (sec-edgar uses the same one with &lt;code&gt;EVENT = 'record'&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Measure the charged-event counter delta; the per-call ChargeResult is unreliable for batches.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChargingManager&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;isPpe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;charging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPricingInfo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;isPayPerEvent&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;emitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BatchEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isPpe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&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;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;charging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChargedEventCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EVENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EVENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;chargedCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;charging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChargedEventCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EVENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;eventChargeLimitReached&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;charging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateMaxEventChargeCountWithinLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EVENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;seenList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;keys&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;The &lt;code&gt;BatchEmitter&lt;/code&gt; then treats &lt;code&gt;chargedCount&lt;/code&gt; as the truth: only that many items are counted as emitted, only their keys go into the "seen" list, and the run stops cleanly once the limit is hit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chargedCount&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emitted&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onStored&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="nx"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventChargeLimitReached&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stopped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So an item dropped by an agent's ten-cent cap is not marked as seen, and it comes back on the next run. Note that the &lt;code&gt;!isPpe&lt;/code&gt; branch matters too: outside pay-per-event (local runs, for example) the ChargeResult reports zero charged even though everything was stored.&lt;/p&gt;

&lt;p&gt;One more thing to know while testing: a run's &lt;code&gt;chargedEventCounts&lt;/code&gt; and the dataset &lt;code&gt;itemCount&lt;/code&gt; lag a few seconds behind the end of the run. Re-fetch before deciding you've found a billing bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Let small budgets in
&lt;/h2&gt;

&lt;p&gt;Pay-per-event Actors have a pricing setting called &lt;code&gt;minimalMaxTotalChargeUsd&lt;/code&gt;. It's the lowest &lt;code&gt;maxTotalChargeUsd&lt;/code&gt; a caller may set when starting a run. If an agent's budget is below your floor, the run doesn't start.&lt;/p&gt;

&lt;p&gt;Agents tend to use small budgets on purpose. Claude chose $0.10 for a five-row lookup, which is a sensible thing for an agent to do. A floor of a dollar would have blocked that call. When I repriced my Actors I lowered the floor to &lt;strong&gt;$0.05&lt;/strong&gt; on all of them.&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="nl"&gt;"minimalMaxTotalChargeUsd"&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.05&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two related details. First, when changing pay-per-event pricing through the API, you &lt;strong&gt;append&lt;/strong&gt; a new &lt;code&gt;pricingInfos&lt;/code&gt; entry; replacing the existing ones fails. Second, the default run memory is set to 1 GB in &lt;code&gt;actor.json&lt;/code&gt;, because the Actor-start event is charged per GB of memory. Agents rarely override memory, so whatever default you pick is what they pay.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Other guardrails worth having
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A free-plan cap that's documented.&lt;/strong&gt; On Apify's free plan my Actors return at most 100 records per run, and the run log says so. An agent that sees 100 rows and a warning can explain the reason to the user instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect upstream limits.&lt;/strong&gt; sec-edgar sends every request through one global rate limiter (8 requests per second, under SEC's fair-access limit), with a declared User-Agent and backoff when SEC throttles. Agents run in loops; your Actor should not let a loop hammer the source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official, public sources.&lt;/strong&gt; EDGAR data is public domain. That's one less thing an agent (or its user) has to worry about before using the output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A SUMMARY record.&lt;/strong&gt; Every run writes a &lt;code&gt;SUMMARY&lt;/code&gt; key-value record with per-company counts, notes and "not found" lists. An agent that gets zero rows can read &lt;em&gt;why&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;If you want an agent to be able to use your Actor, and afford it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Actor &lt;code&gt;description&lt;/code&gt; starts with a verb, says what comes back, and stays under 500 characters. It is the agent's tool card.&lt;/li&gt;
&lt;li&gt;[ ] One obvious required field, with &lt;code&gt;prefill&lt;/code&gt; and &lt;strong&gt;no &lt;code&gt;default&lt;/code&gt;&lt;/strong&gt;. Everything else optional with sensible defaults.&lt;/li&gt;
&lt;li&gt;[ ] Field descriptions under 500 characters, essentials first, one example each.&lt;/li&gt;
&lt;li&gt;[ ] Accept the natural formats a model will try (tickers &lt;em&gt;and&lt;/em&gt; names, &lt;code&gt;"90 days"&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; ISO dates).&lt;/li&gt;
&lt;li&gt;[ ] Invalid input ends SUCCEEDED, charges nothing and says how to fix it. &lt;code&gt;Actor.fail()&lt;/code&gt; only for real outages.&lt;/li&gt;
&lt;li&gt;[ ] Honour &lt;code&gt;maxResults&lt;/code&gt; and the caller's &lt;code&gt;maxTotalChargeUsd&lt;/code&gt;, and stop cleanly when either is reached.&lt;/li&gt;
&lt;li&gt;[ ] Count charges with &lt;code&gt;getChargedEventCount()&lt;/code&gt; deltas, not the batched &lt;code&gt;pushData&lt;/code&gt; ChargeResult.&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;minimalMaxTotalChargeUsd&lt;/code&gt; low enough for small agent budgets (I use $0.05).&lt;/li&gt;
&lt;li&gt;[ ] No personal data in the output. Identify people by role, not name.&lt;/li&gt;
&lt;li&gt;[ ] Expect &lt;code&gt;search-actors&lt;/code&gt; to ignore you at first. Get in front of agents through names, &lt;code&gt;?tools=&lt;/code&gt; URLs, READMEs and published tasks while usage builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this makes a new Actor show up in an agent's top five. That takes usage, and I don't have much yet. What it does is make sure that when an agent does reach your Actor, the first call works, costs what it should, and gives the agent something it can use.&lt;/p&gt;

&lt;p&gt;More about what I'm building is at &lt;a href="https://locaihost.org" rel="noopener noreferrer"&gt;locaihost.org&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>apify</category>
      <category>ai</category>
      <category>mcp</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
