# Bulk DMARC, SPF & DKIM Checker + Email Provider & SaaS Finder (`jtpalms/email-security-checker`) Actor

Check thousands of domains for email security in one run: MX records and mail provider (Google Workspace, Microsoft 365...), SPF with lookup count, DKIM selectors, DMARC policy, BIMI, MTA-STS, TLS-RPT, a 0-100 grade with fixes, and SaaS tools from DNS verification records. USD 1 per 1,000 domains.

- **URL**: https://apify.com/jtpalms/email-security-checker.md
- **Developed by:** [JT Palms](https://apify.com/jtpalms) (community)
- **Categories:** Lead generation, Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 domain checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk DMARC, SPF and DKIM Checker + Email Provider and SaaS Finder

Paste a list of domains (or email addresses, or URLs) and get one row per domain with its email security setup: the mail provider (Google Workspace, Microsoft 365 and others), any security gateway in front of it, SPF with its DNS lookup count, DKIM keys found, the DMARC policy, BIMI, MTA-STS and TLS-RPT, a 0-100 grade with plain-English fixes, and the SaaS tools the domain has verified in DNS (Atlassian, Stripe, DocuSign, Zapier, OpenAI and about 70 more).

Everything comes from public DNS records that the domain owner publishes. Nothing is scraped, no mail server is contacted and no mailbox is checked.

**USD 1 per 1,000 domains**, plus a tiny start fee per run (see Pricing). Invalid inputs and domains that do not exist are free.

### What people use it for

- **MSP and IT security prospecting.** Find prospects on Microsoft 365 or Google Workspace with no DMARC, or DMARC stuck at `p=none`. That is a standard, concrete opener for a security conversation, and the `fixes` column tells you exactly what to offer. Turn on `onlyProblems` to get only the domains that need work.
- **Deliverability pre-flight before a campaign.** Check your own sending domains and your clients' domains before every send: Gmail and Yahoo require SPF, DKIM and DMARC from bulk senders, and SPF breaks silently when it needs more than 10 DNS lookups.
- **Vendor and supplier risk checks.** See which suppliers protect their domain against spoofing (DMARC `reject`) and which could be impersonated in an invoice fraud email.
- **Sales targeting and routing.** Use the mail provider, the sending services in SPF (HubSpot, Salesforce, SendGrid, Mailchimp...) and the DNS-verified SaaS tools as firmographic signals.

### Sample output

Real DNS answers on 2026-10-05 (default settings):

| domain | grade | mailProvider | securityGateway | dmarcPolicy | spfAll | spfLookupCount | dkimSelectorsFound | MTA-STS | TLS-RPT | SaaS found |
|---|---|---|---|---|---|---|---|---|---|---|
| shopify.com | 92 | Google Workspace | | reject | ~all | 4 | google, mail, pm | no | no | 23 |
| microsoft.com | 100 | Microsoft 365 | | reject | -all | 7 | selector2 | yes | yes | 16 |
| github.com | 87 | Microsoft 365 | | quarantine | ~all | 10 | google, selector1, k1, k2, k3, s1, s2, cm | no | no | 16 |
| ford.com | 92 | | Proofpoint | reject | ~all | 2 | k2, k3, s1, s2, dkim | no | no | 28 |
| crawlee.dev | 35 | | | missing | missing | 0 | none found | no | no | 1 |

crawlee.dev gets 35 because it has no DMARC (-30), no SPF (-25) and no DKIM key among the common selectors (-10). It has no MX record, so MTA-STS and TLS-RPT are not counted against it.

One full row (shopify.com; `dnsVerifiedServices` trimmed to 10 of the 23 found):

```json
{
  "input": "shopify.com",
  "domain": "shopify.com",
  "acceptsMail": true,
  "mx": [
    { "host": "aspmx.l.google.com", "priority": 1 },
    { "host": "alt1.aspmx.l.google.com", "priority": 5 },
    { "host": "alt2.aspmx.l.google.com", "priority": 5 },
    { "host": "alt3.aspmx.l.google.com", "priority": 10 },
    { "host": "alt4.aspmx.l.google.com", "priority": 10 }
  ],
  "mailProvider": "Google Workspace",
  "mailProviderSource": "mx",
  "securityGateway": null,
  "spfRecord": "v=spf1 include:_spf.google.com include:mail.zendesk.com include:sendgrid.net ~all",
  "spfCount": 1,
  "spfAll": "~all",
  "spfLookupCount": 4,
  "spfIncludes": ["_spf.google.com", "mail.zendesk.com", "sendgrid.net"],
  "spfServices": ["Google", "Zendesk", "SendGrid"],
  "spfErrors": [],
  "dmarcRecord": "v=DMARC1; p=reject; pct=100; fo=1; rua=mailto:...;ruf=mailto:...",
  "dmarcPolicy": "reject",
  "dmarcSubdomainPolicy": null,
  "dmarcPct": 100,
  "ruaDomains": ["shopify.com"],
  "dmarcAlignment": { "dkim": "relaxed", "spf": "relaxed" },
  "dkimSelectorsFound": ["google", "mail", "pm"],
  "dkimKeys": [
    { "selector": "google", "keyType": "rsa", "bits": 1024 },
    { "selector": "mail", "keyType": "rsa", "bits": 1024 },
    { "selector": "pm", "keyType": "rsa", "bits": null }
  ],
  "bimiPresent": true,
  "bimiLogo": "https://vmc.digicert.com/8833b699-1227-41ee-b185-cc2d9a08e213.svg",
  "bimiVmc": true,
  "mtaStsPresent": false,
  "mtaStsMode": null,
  "tlsRptPresent": false,
  "dnsVerifiedServices": ["Microsoft 365", "Google", "Atlassian", "DocuSign", "Stripe", "Zapier", "Klaviyo", "OpenAI", "Facebook (Meta)", "Apple"],
  "grade": 92,
  "fixes": ["Publish an MTA-STS policy", "Publish a TLS-RPT record"],
  "checkedAt": "2026-10-05T16:59:37.155Z",
  "error": null
}
```

### What it checks

Since February 2024, Gmail and Yahoo Mail require senders of about 5,000 or more messages a day to their users to publish SPF, DKIM and a DMARC record (`p=none` is enough to start), and every sender needs at least SPF or DKIM. These are the records this actor reads:

- **MX and mail provider.** The MX hosts and priorities, matched against a dated table of providers (Google Workspace, Microsoft 365 including the newer `*.mx.microsoft` hosts, Zoho, Proton, Fastmail, Yahoo, GoDaddy, IONOS, OVHcloud and others) and of security gateways (Proofpoint, Mimecast, Barracuda, Cisco Secure Email, Broadcom/Symantec and others). A null MX (`0 .`) means the domain accepts no mail.
- **SPF.** The `v=spf1` record, how many there are (more than one is an error), how it ends (`-all`, `~all`, `?all`, `+all`, or `none`), and the DNS lookups a receiver must make, counted the way RFC 7208 does: `include`, `a`, `mx`, `ptr`, `exists` and `redirect` each cost one, nested includes are followed, loops are caught, and more than 10 is an error. Includes are mapped to sending services (SendGrid, Mailgun, Amazon SES, Mailchimp, HubSpot, Salesforce, Zendesk and others).
- **DMARC.** The `_dmarc` record: policy (`none`, `quarantine`, `reject`), subdomain policy, `pct`, DKIM and SPF alignment, and the domains that receive the aggregate reports (`ruaDomains`, which often names the DMARC vendor).
- **DKIM.** About 27 common selectors are tried (google, selector1 and selector2 for Microsoft 365, k1 to k3, s1 and s2, default, dkim, mail, zoho, protonmail, fm1 to fm3, mandrill, amazonses, mailjet and more) plus any you add. Each key found is reported with its type and size. Revoked keys (empty `p=`) and wildcard answers are not counted.
- **BIMI.** The `default._bimi` record, the logo URL and whether a certificate (VMC) is published.
- **MTA-STS.** The `_mta-sts` record. With `checkMtaStsPolicy` on, the policy file is read to get its mode (enforce, testing or none).
- **TLS-RPT.** The `_smtp._tls` record for TLS failure reports.
- **SaaS from DNS verification records.** TXT records such as `MS=`, `atlassian-domain-verification`, `stripe-verification` or `docusign=` show which tools the company has verified its domain with. Only the tool names are output, never the tokens.

Records are recognised by their version prefix only. Some domains answer every unknown name with a wildcard (shopify.com returns SPF, BIMI and DMARC strings at `_mta-sts.shopify.com`); those strings are correctly ignored.

### The grade

Every domain starts at 100. Points come off for:

| Problem | Points |
|---|---|
| No DMARC record (or an invalid one: several records, or no valid `p=`) | -30 |
| DMARC `p=none` | -15 |
| DMARC `p=quarantine` | -5 |
| No SPF record | -25 |
| More than one SPF record | -15 |
| SPF ends in `+all` | -25 |
| SPF ends in `?all`, or has no `all` at all (same meaning) | -10 |
| SPF needs more than 10 DNS lookups | -10 |
| No DKIM key found among the common selectors | -10 |
| No MTA-STS record | -5 |
| No TLS-RPT record | -3 |
| BIMI | information only |

The lowest possible grade is 2. `fixes` lists what to do, largest deduction first, then information-only notes (MTA-STS still in testing mode, DMARC `pct` under 100, DKIM keys shorter than 1024 bits, a policy file that could not be read).

Two rules keep parked domains fair: a domain with no MX hosts (a null MX, or no MX at all) is not penalised for missing MTA-STS or TLS-RPT, which protect incoming mail; and a domain whose SPF record is exactly `v=spf1 -all` (it authorises no sender) is not penalised for missing DKIM. A correctly parked domain such as example.com scores 100.

### Input

| Field | What it does |
|---|---|
| `domains` | Domains, email addresses or URLs, one per line. Each is reduced to its registered domain with the Public Suffix List (`https://www.python.org/downloads/` becomes `python.org`, `mail.example.co.uk` becomes `example.co.uk`). For an email address only the part after the @ is used. Duplicates are checked once. Invalid entries get a row with an `error` and are free. |
| `maxDomains` | Check at most this many unique domains (default 10,000). Domains past the limit are skipped and not charged. |
| `extraDkimSelectors` | Your own DKIM selector names to try on every domain, for example `custom1` (up to 50). |
| `includeSaasDetection` | On by default. Maps TXT verification records to tool names in `dnsVerifiedServices`. |
| `checkMtaStsPolicy` | Off by default. For domains with an MTA-STS record, one HTTPS request to `https://mta-sts.<domain>/.well-known/mta-sts.txt` to read the mode. |
| `onlyProblems` | Off by default. Output only domains with a grade under 70. Domains with 70 or more are checked but left out and not charged. |
| `concurrency` | Domains checked at the same time (default 20, up to 50). |

### Output fields

| Field | Meaning |
|---|---|
| `input`, `domain` | What you entered (an email address is shown as `(email) domain.com`, never in full) and the registered domain checked. |
| `acceptsMail` | `true` with MX hosts, `false` with a null MX, or with no MX and no A record; `null` with no MX but an A record (mail servers may then try that address, so it is unknown). |
| `mx` | MX hosts with priorities, lowest priority first. |
| `mailProvider`, `mailProviderSource` | The mailbox provider. `mailProviderSource` is `mx` when it comes from the MX hosts, or `spf` when the MX is a security gateway and the SPF record includes Microsoft 365 or Google. `Other` means an MX host not in the table (often self-hosted); `null` means no provider could be named. |
| `securityGateway` | The inbound filtering service in the MX, if any (Proofpoint, Mimecast...). |
| `spfRecord`, `spfCount`, `spfAll` | The SPF record, how many SPF records exist, and how it ends (`-all`, `~all`, `?all`, `+all`, `none`, or `null` without SPF). With `redirect=`, the ending of the redirected record is used. |
| `spfLookupCount`, `spfIncludes`, `spfServices`, `spfErrors` | DNS lookups SPF needs, the include and redirect targets in the domain's own record, the sending services found anywhere in the include tree, and problems (over 10 lookups, loops, missing include targets, several records). |
| `dmarcRecord` | The DMARC record, with every `mailto:` address replaced by `mailto:...`. |
| `dmarcPolicy`, `dmarcSubdomainPolicy`, `dmarcPct`, `dmarcAlignment` | Parsed DMARC settings. `dmarcPct` is `null` when the record has no `pct` tag. Alignment is `relaxed` unless the record says `s` (strict). |
| `ruaDomains` | Domains that receive the aggregate reports (never the addresses). |
| `dkimSelectorsFound`, `dkimKeys` | Selectors with a published key, and each key's type (`rsa` or `ed25519`) and size in bits (`null` when the key cannot be parsed). |
| `bimiPresent`, `bimiLogo`, `bimiVmc` | BIMI record with a logo, the logo URL, and whether a certificate (`a=`) is published. A record with an empty logo (a BIMI opt-out) counts as not present. |
| `mtaStsPresent`, `mtaStsMode` | MTA-STS record found; the policy mode only with `checkMtaStsPolicy`. |
| `tlsRptPresent` | TLS-RPT record found. |
| `dnsVerifiedServices` | SaaS tools from TXT verification records (names only). |
| `grade`, `fixes` | The 0-100 grade and what to fix, in order. |
| `checkedAt`, `error` | When the domain was checked, and why a row has no data (`domain does not exist`, an invalid input, or a DNS failure). |

The run's key-value store also holds a `SUMMARY` record with counts (checked, billed, not existing, DNS failures, left out by `onlyProblems`) and DNS statistics.

### Pricing

| What | Price |
|---|---|
| Domain checked (one row with all checks and a grade) | USD 0.001 (USD 1 per 1,000) |
| Run start | USD 0.00005 per run |
| Invalid input, domain does not exist, DNS failure, domains past `maxDomains`, domains left out by `onlyProblems` | Free |

Worked examples on the free plan:

- The default input (3 domains): 3 x 0.001 + 0.00005 = **USD 0.00305**.
- 1,000 prospect domains: about **USD 1.00**.
- 10,000 domains with `onlyProblems` on, of which 3,200 grade under 70: 3,200 x 0.001 = **USD 3.20** (plus the start fee).

Higher Apify plans get a discount on the per-domain price (10% on Silver, 20% on Gold and above). You can set a maximum cost per run in the run options: the actor checks no more domains than that budget can pay for, stops cleanly when it is reached, and keeps every row written so far.

**Schedule it weekly.** Save your list as a task and schedule it: you will see when a prospect moves from `p=none` to `reject`, when a domain changes mail provider, or when someone breaks their SPF.

### How it queries DNS

- About 35 to 40 small DNS queries per domain, most of them DKIM selector guesses. Answers that many domains share (such as `_spf.google.com`) are cached for the run.
- The container's own resolver answers first. Only after a timeout or a server failure is a query asked once more, after a short back-off, at a public resolver (Cloudflare 1.1.1.1 or Quad9 9.9.9.9, taken in turn). Definite answers (the name does not exist) are never retried.
- All queries share one rate limit (250 per second, at most 64 in flight; the public fallback resolvers get their own lower limit), so 1,000 domains take roughly 2 to 3 minutes.
- Per-query timeout 5 seconds.

### Limits and honesty

- **DKIM selectors cannot be listed.** DNS has no way to ask "which DKIM keys does this domain have", so the actor tries common selector names. "None found" is not proof that a domain has no DKIM; its selector may have a custom name. Add known selectors in `extraDkimSelectors`.
- **Registered domains only.** Subdomains are reduced to their registered domain (`mail.example.com` is checked as `example.com`), so a subdomain with its own SPF or DMARC record is not checked separately.
- **A snapshot.** Each row shows the DNS answers at `checkedAt`. Records change; schedule runs to track them.
- **Provider and SaaS tables are dated** (2026-10-05) and cover the common services. An unknown MX host shows as `Other`; an unknown TXT record is simply not listed.
- **The mail provider comes from the MX hosts.** Behind a gateway it is inferred from SPF only when SPF includes Microsoft 365 or Google; otherwise it stays `null`.
- **DNS failures are free.** If a domain's name servers time out or fail even after the retry, its row says so in `error` and it is not charged; run it again later.

### Privacy

- No mailbox checks, no SMTP connections, no address verification.
- No email address is ever output. Email inputs are reduced to their domain (the `input` column shows `(email) domain.com`), DMARC report addresses are reduced to domains, and any `mailto:` address in a record is masked.
- Verification tokens in TXT records are never output, only the tool names.

### What this does not do

- It reads published DNS records only (plus, if you turn it on, the public MTA-STS policy file). It does not send mail, connect to mail servers, test inbox placement, or verify that an email address exists.
- It does not read DMARC reports or tell you who is sending mail as the domain.
- It does not validate DKIM signatures or SPF results for real messages; it checks the published setup.
- It does not check blacklists or IP reputation.
- It does not visit the domain's website; the only web request is the optional MTA-STS policy file (see Related actors for website checks).

### Related actors

- [Bulk WHOIS Lookup (RDAP): Domain Availability & Expiry Checker](https://apify.com/JTPalms/domain-rdap-lookup): WHOIS lookup for thousands of domains through RDAP, the official successor to WHOIS: available or registered, registrar, creation date,...
- [Company Enrichment From Domain: Tech Stack, Socials, Hiring](https://apify.com/JTPalms/company-profile): Company enrichment from a list of website domains: company name, description, official social links, tech stack (CMS, ecommerce,...
- [Tech Stack Detector: Website Technology Lookup in Bulk](https://apify.com/JTPalms/tech-stack-detector): Website technology lookup in bulk: the CMS, ecommerce platform, analytics, frameworks, CDN, hosting, payment, email and marketing tools...
- [Domain Authority Checker: Backlink Rank from Open Data](https://apify.com/JTPalms/domain-authority-checker): Bulk domain authority checker built on open data: website authority and backlink strength from the Majestic Million (referring subnets...

### FAQ

**Why is "no DKIM found" only -10 points?** Because the actor can only guess selector names. A domain may sign every message with a selector named after a date or a vendor ID.

**Why did a well-known company get a low grade?** The grade measures published protections, not the company. Many large companies still run DMARC at `p=none` or have no MTA-STS.

**Can I check 100,000 domains?** Yes. Raise `maxDomains` (default 10,000) and the run timeout (default 1 hour): at the shared rate limit, 10,000 domains take roughly 25 minutes and 100,000 roughly 4 hours.

**Something missing or wrong?** Open an issue on the actor page with the domain.

# Actor input Schema

## `domains` (type: `array`):

Domains, email addresses or URLs, one per line. Each is reduced to its registered domain (https://www.python.org/downloads/ becomes python.org, bbc.co.uk stays bbc.co.uk). For an email address only the part after the @ is used, and the address is not stored or output. Duplicates are checked once. Invalid entries get a row with an error and are free.

## `maxDomains` (type: `integer`):

Check at most this many unique domains per run. Domains past the limit are skipped and not charged. Keeps a first run cheap.

## `extraDkimSelectors` (type: `array`):

Your own DKIM selector names to test on every domain, in addition to about 27 common ones (google, selector1, selector2, k1, s1, default and so on). Enter just the selector, for example custom1 for custom1.\_domainkey.example.com. Up to 50.

## `includeSaasDetection` (type: `boolean`):

Map the domain's TXT verification records (MS=, atlassian-domain-verification, stripe-verification and about 75 more) to tool names in dnsVerifiedServices. Only names are output, never the verification tokens.

## `checkMtaStsPolicy` (type: `boolean`):

For domains that publish an MTA-STS record, make one HTTPS request to https://mta-sts.<domain>/.well-known/mta-sts.txt to read the policy mode (enforce, testing or none). Off by default; everything else is DNS only.

## `onlyProblems` (type: `boolean`):

Output only domains with a grade under 70. Domains with 70 or more are checked but left out and not charged; only output rows are charged.

## `concurrency` (type: `integer`):

How many domains are checked at the same time (1 to 50). DNS queries are tiny; all queries also share one polite rate limit, so a higher number does not flood any resolver.

## Actor input object example

```json
{
  "domains": [
    "shopify.com",
    "apify.com",
    "example.com"
  ],
  "maxDomains": 100,
  "extraDkimSelectors": [],
  "includeSaasDetection": true,
  "checkMtaStsPolicy": false,
  "onlyProblems": false,
  "concurrency": 20
}
```

# Actor output Schema

## `results` (type: `string`):

One row per domain in the default dataset (JSON, CSV, Excel via the format parameter).

## `summary` (type: `string`):

Counts (checked, billed, not existing, DNS failures, left out by onlyProblems) and DNS query statistics.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "shopify.com",
        "apify.com",
        "example.com"
    ],
    "maxDomains": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("jtpalms/email-security-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "domains": [
        "shopify.com",
        "apify.com",
        "example.com",
    ],
    "maxDomains": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("jtpalms/email-security-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "shopify.com",
    "apify.com",
    "example.com"
  ],
  "maxDomains": 100
}' |
apify call jtpalms/email-security-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jtpalms/email-security-checker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GJJkj7VdvCjmSnWhB/builds/kC9tGa6kaVY7dGgBx/openapi.json
