Email Preflight API

Catch the send you cannot un-send

The Email Preflight API returns pre-send QA for HTML email as clean JSON.

no credit card1,000 free credits · instant API key · live in 10 seconds
Missing a Email Preflight endpoint, or need a source we don't have yet?Contact us real people · same-day reply.
E
/email-preflight/v1

7 active endpoints, on 1, 2 and 3 credit tiers.

  • POST/email-preflight/v1/audit_html
  • POST/email-preflight/v1/css_support
  • POST/email-preflight/v1/spam_score
  • POST/email-preflight/v1/check_links
  • POST/email-preflight/v1/check_images
  • POST/email-preflight/v1/dns_auth
  • POST/email-preflight/v1/preview_basic

What Email Preflight endpoints does ReefAPI ship?

7 live read endpoints. Read-only data API: no writes, no account actions, no dashboard access on the target site.

7 endpoints

audit_html

2 cr

Full pre-send structure + accessibility + deliverability lint of an HTML email.

required
html
optional
text_part, base_url, subject, check_links

css_support

1 cr

Email-client CSS/HTML compatibility map for the template (Can-I-Email data, MIT).

required
html
optional

spam_score

1 cr

Deterministic spam-SIGNAL score for the email content (caps ratio, image-to-text ratio, spam…

required
html
optional
subject, text_part

check_links

2 cr

Resolve every link in the email through url-resolver (SSRF-guarded).

required
html
optional
base_url

check_images

1 cr

Audit every <img> in the email.

required
html
optional
base_url, check_reachable

dns_auth

1 cr

Sender-domain email-authentication summary (SPF/DKIM/DMARC/MX + deliverability grade) via an…

required
domain
optional
dkim_selector, include_blacklist

preview_basic

3 cr

Basic visual preview.

required
html
optional
width

Every parameter, every allowed value →

Email Preflight API

3 of 7 endpoints, ready to run

View docs ↗

The full preflight: validity, size against the Gmail clipping limit, accessibility, structure, mobile risks, dark-mode hints, tracking pixels, unsubscribe presence and a graded score with ranked recommendations.

2 credits1 required · 3 optional
POST/email-preflight/v1/audit_html
ok6 ms · 5 records · sample
{
  "ok": true,
  "meta": {
    "api": "email-preflight",
    "endpoint": "audit_html",
    "mode": "live",
    "latency_ms": 6.1,
    "record_count": 5,
    "cache_hit": false
  },
  "data": {
    "html_validity": {
      "parse_ok": true,
      "warnings": []
    },
    "size": {
      "bytes": 83,
      "gmail_clip_risk": false,
      "limit_bytes": 102000
    },
    "accessibility": {
      "images_total": 1,
      "missing_alt": 1,
      "missing_alt_srcs": [
        "a.png"
      ],
      "issues": [
        "1 image(s) without alt text",
        "no lang attribute on the document (screen-reader language hint)"
      ]
    },
    "structure": {
      "has_doctype": false,
      "has_table_layout": false,
      "uses_div_layout": false,
      "heading_count": 1,
      "script_tags": 0,
      "link_count": 1,
      "image_count": 1
    },
    "mobile_width_risks": [
      "no <meta name=viewport> (responsive scaling on mobile)"
    ],
    "dark_mode": {
      "has_meta_color_scheme": false,
      "hints": [
        "no <meta name=color-scheme> — clients may auto-invert your colors"
      ]
    },
    "tracking_pixels": [],
    "external_assets": [],
    "unsubscribe": {
      "list_unsubscribe_header_present": false,
      "has_unsubscribe_link": false,
      "has_unsubscribe_text": false
    },
    "text_part": {
      "present": false,
      "recommended": true
    },
    "recommendations": [
      {
        "severity": "error",
        "code": "MISSING_ALT",
        "message": "1 image(s) lack alt text — add descriptive alt for accessibility + when images are blocked."
      },
      {
        "severity": "error",
        "code": "NO_UNSUB",
        "message": "No unsubscribe link/text found — required by CAN-SPAM/CASL and critical for deliverability."
      },
      {
        "severity": "warning",
        "code": "NO_DOCTYPE",
        "message": "No <!DOCTYPE> — declare one (XHTML 1.0 Transitional is the email-safe default) for predictable rendering."
      }
    ],
    "score": {
      "value": 46,
      "grade": "F"
    }
  }
}
Real response, fetched from the live endpoint with the parameters on the left — trimmed to the first few rows, with seller names left out. Press Try it for the untrimmed response.

How the Email Preflight API works

Email Preflight is a normal ReefAPI surface — the same four rules that hold for every other engine on the key.

01
Authenticate
x-api-key header

No OAuth app, no request signing, no per-site account. One key covers all 184 engines.

02
Call
POST /email-preflight/v1/…

Every route is a POST with a JSON body. Parameters are validated against the published schema before anything is charged.

03
Pay
1 or 2 or 3 credits per call

Credits, not seats. Failed and blocked calls are never charged, and cache hits cost nothing.

04
Read
{ ok, data, meta, error }

One envelope everywhere. meta carries latency_ms, record_count and the endpoint that answered.

The gate between the campaign tool and the send button

An email is the one artefact that cannot be rolled back. The failures are mundane — a clipped body, an unresolved link, images with no alt text for the clients that block them.

01audit_html
POST/email-preflight/v1/audit_html
{"html": "…", "subject": "Your June statement"}

Returns ranked recommendations with a severity and a code on each, so a build can fail on errors and warn on the rest.

02check_links
POST/email-preflight/v1/check_links
{"html": "…", "base_url": "https://newsletter.example.com"}

Resolves the links, which is the check that needs the network and therefore the one worth running separately.

The HTML audit is deterministic and finished in single-digit milliseconds, so it can sit in a pre-send hook without anyone noticing it is there.

request
curl -X POST https://api.reefapi.com/email-preflight/v1/audit_html \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"html":"<html><body><img src='https://x.test/a.png'><a href='https://x.test'>hi</a></body></html>"}'
response envelope
{
  "ok": true,
  "data": { … },
  "meta": {
    "api": "email-preflight",
    "endpoint": "audit_html",
    "mode": "live",
    "latency_ms": …,
    "record_count": …
  },
  "error": null
}

Three email engines, three different questions

email-preflight, email-health and validate look adjacent and answer different things: the message, the sending domain, and one recipient address. The overlap is real and narrow, and it is worth knowing exactly where it is. Every figure below is from live calls made against the same test template, github.com and [email protected].

What you are askingEngine and actionMeasured
Is this HTML broken before I send itemail-preflight audit_html4 images, 3 missing alt, one table at width=700 flagged for mobile, one tracking pixel, score 74 grade C
Which CSS will break in Outlookemail-preflight css_supportposition, display:flex and border-radius all high_risk across 12 clients, from a bundled Can-I-Email snapshot v1.0.4 dated 2026-05-19
Does the copy read as spamemail-preflight spam_score7.3 verdict likely_spam over 7 rules; a plain rewrite of the same message scored 0.0 clean_signal
Do the links and images resolveemail-preflight check_links / check_images4 links: 1 ok, 1 broken (real 404), 2 unverifiable; 4 images: 3 missing alt, 1 over http, 2 without dimensions
Is my sending domain authenticated, fastemail-preflight dns_authgithub.com scored 90.0 grade A; returns 9 keys (domain, score, spf, dkim, dmarc, mx, summary, issues, source)
Is my sending domain authenticated, in fullemail-health checkidentical score object, 90.0 grade A, but 14 keys adding bimi, mta_sts, tls_rpt, dnssec, dane and the blacklist detail
Is this one address deliverablevalidate email[email protected] returned valid true, deliverable true, score 95, has_mx true and the MX exchange itself
What will it look likeemail-preflight preview_basicreturned available:false with reason "web-capture engine is not reachable for rendering"; the other actions are unaffected

dns_auth is a documented wrapper, not a second implementation: its own payload names source "email-health inner-call" and reused_engine "email-health". It drops the six standalone section objects but keeps their scores in score.breakdown (mx 20, spf 22, dmarc 22, dkim 16, blacklist 10, mta_sts 4, dnssec 3, tls_rpt 2, bimi 1, totalling 100) and their booleans in summary. Call dns_auth for a pre-send go/no-go, email-health when you need the records themselves.

What is checked locally and what needs the network

Measured on deliberately bad HTML and on a plain message. The scoring row explains what the number is and is not.

The spam score names the rules, not just the total

Each triggered rule comes back with its identifier, its weight and a plain-English description — consecutive exclamation marks, capitals ratio, link-to-text balance. The threshold convention is stated in the response, so the number means something specific rather than being a proprietary scale you have to calibrate by trial and error.

Recommendations carry a severity and a machine code

Findings are returned as ranked items with an error or warning severity and a stable code such as MISSING_ALT, alongside the human message. That is what makes this usable as a CI gate: fail on errors, report warnings, and match on codes rather than on prose.

The checks are the ones that actually bite

Body size against the Gmail clipping limit, images without alt text for the clients that block images by default, table versus div layout, mobile width risks, dark-mode hints, tracking pixels, external assets and whether an unsubscribe link and header are present. These are the failure modes that survive every template system.

The HTML audit never leaves the building

Structure, accessibility, size and spam signals are computed from the markup alone and returned in single-digit milliseconds. Only link and image checking needs the network, and those are separate actions, so a pre-send hook can run the fast checks on every send and the slow ones on demand.

Against us: this is not a rendering preview

It reports CSS support and structural risks against known client behaviour; it does not screenshot your email in thirty clients. If your question is 'does this look right in Outlook 2016', this narrows the list of likely problems but does not replace looking.

What people build with Email Preflight

The jobs this data is most often used for.

7

endpoints

1/2/3

credits per call

01

ESPs and marketing tools call audit_html to catch broken markup and clipping risk before a send.

02

QA pipelines use spam_score and check_links to flag deliverability and broken-link problems.

03

Accessibility checks use audit_html to ensure images have alt text and the email is readable.

What Email Preflight data costs

The cheapest call here is 1 credit, so $15/mo (Pro) buys 10,000 of them — $1.50 per 1,000 credits. Credits roll over and never expire, and failed or blocked calls are not charged.

Full pricing →
$0.67–$1.50 / 1,000 credits
  • 1,000 free credits on signup, no card
  • One key, all 184 APIs, one credit pool
  • Failed and blocked calls are never charged
  • Credits roll over and never expire

Call it in two lines

Sign up, get 1,000 credits and one key that works on every engine. Then this is the whole protocol.

curl
curl -X POST https://api.reefapi.com/email-preflight/v1/audit_html \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"html":"<html><body><img src='https://x.test/a.png'><a href='https://x.test'>hi</a></body></html>"}'
python
import requests

r = requests.post(
    "https://api.reefapi.com/email-preflight/v1/audit_html",
    headers={"x-api-key": REEF_KEY},
    json={
  "html": "<html><body><img src='https://x.test/a.png'><a href='https://x.test'>hi</a></body></html>"
},
)
print(r.json()["data"])
FAQ

Have a question? We got answers.

The questions people actually ask before wiring up Email Preflight.

Get a free key →
Should I call dns_auth or email-health?

dns_auth when you want one pre-send verdict, email-health when you want the evidence. Measured on github.com they produced the identical score object, 90.0 grade A with the same nine-section breakdown, but email-health returned 14 top-level keys and dns_auth returned 9. What dns_auth drops is the standalone bimi, mta_sts, tls_rpt, dnssec, dane and blacklist objects; their contribution is still visible in score.breakdown and their booleans in summary. If you need the SPF mechanism tree, the DKIM key bits or the DNSBL zone list, go to email-health.

Why did the same domain grade A on one call and B on the next?

Because include_blacklist defaults to false and the skipped section is still scored. With the default, github.com returned blacklist 10 out of 10 with the note "clean across 15 DNSBLs", summary.blacklisted false, and a total of 90.0 grade A. With include_blacklist true the same domain returned blacklist 6 out of 10 with the note "1 listing(s)", summary.blacklisted true, and 86.0 grade B. The fast default therefore awards a full clean blacklist score without having run the lookup. Pass include_blacklist true before you report a reputation result to anyone.

How is this different from validate?

Different object entirely. validate's email action takes one address and answers whether that mailbox is worth sending to: a measured [email protected] returned syntax_valid true, has_mx true with the exchange github-com.mail.protection.outlook.com, is_disposable false, is_role_account false, is_free_provider false, deliverable true and score 95. email-preflight never looks at a recipient. It takes the message body, or the sending domain, and answers whether the thing you are about to send is well formed. A typical pipeline uses validate on the list, dns_auth once on the sender, and audit_html once per template.

I used alt="" on a decorative image and it was still counted as missing.

Yes, an empty alt attribute counts as missing in both audit_html and check_images. In a measured run the image carrying alt="" appeared in accessibility.missing_alt_srcs and came back from check_images as alt "" with has_alt false. That is stricter than the HTML specification, where an empty alt is the correct marking for a decorative image. If you use that convention, subtract those images from missing_alt yourself before acting on the MISSING_ALT recommendation.

unsubscribe.list_unsubscribe_header_present is always false. Why?

Because List-Unsubscribe is an SMTP header and audit_html only ever sees the HTML body, so there is nowhere for it to be true. In our runs it was false while has_unsubscribe_link and has_unsubscribe_text were both true from an in-body link. Treat that field as a checklist reminder for your sending code rather than a finding about the template, and read the two has_unsubscribe_* fields for what the HTML actually contains.

What does verdict "unverifiable" mean on a link?

That we could not prove the link is dead, so we refuse to call it broken. Of four links in a measured template, one returned verdict ok, one returned verdict broken with a real HTTP 404, and two returned unverifiable: one with reason "host does not resolve" and error_code NOT_FOUND, the other reporting a resolution timeout against a slow target, with the response spelling out that a timeout is not proof the link is broken, and error_code INTERNAL. The response note spells out the rule: broken means a real status of 400 or above from the target, while anti-bot responses, timeouts and transport failures all land in unverifiable. Do not fail a campaign on unverifiable.

check_links reported status 206 for a link that works. Is that right?

Yes. Reachability checks use a ranged request, so a healthy target commonly answers 206 Partial Content rather than 200. A measured github.com link returned status 206 with verdict ok and redirect_count 0, and a measured image returned reachable true with http_status 206. Key on verdict for links and on reachable for images rather than testing status == 200 yourself. Images that were never attempted come back with reachable null, which is not the same as false.

Is the spam score a prediction that I will land in spam?

No, and the response says so in threshold_note: "score >= 5.0 ~ likely spam (SpamAssassin convention); this is a deterministic content SIGNAL, not an inbox-placement prediction." It is rule-based and repeatable. A deliberately awful test message scored 7.3 with verdict likely_spam over 7 rules including FREE_MONEY 1.5, CAPS_HEAVY 1.5 at a measured 36 percent all-caps words, URGENCY 1.0, MANY_EXCLAIM 1.0 and IMG_RATIO 1.0 for 4 images against 25 words; a plain rewrite scored 0.0 with verdict clean_signal and zero rules. Note that audit_html does not include this block, so spam_score is a separate call.

What is the Email Preflight API?

Email Preflight API is a ReefAPI endpoint group for email preflight It returns live JSON through POST requests under /email-preflight/v1.

Is the Email Preflight API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. Email Preflight calls use the same shared credit balance as every other ReefAPI engine.

Do I need an Email Preflight login or account?

No login to Email Preflight is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the Email Preflight data?

The page example is captured from a live audit_html call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the Email Preflight API use?

Email Preflight actions currently cost 1-3 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.

Can I call Email Preflight from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call email-preflight actions with the same key, credit pool and JSON envelope used by normal REST requests.

18 Developer Tools APIs on the same key

One key, one credit pool, one response envelope. If you are pulling Email Preflight, you are one call away from the rest of the category — no second contract, no second integration.

Need something this API does not do?

Name the endpoint, the field, or a source we do not carry yet. We ship new APIs every week and you would be first to get the key. Real people read every message and reply the same day.

0/4000

No account needed · we reply from [email protected]

Try it on your own data before you pay anything

The call above is the real endpoint, not a recording. A free key gives you 1,000 credits, the other 183 APIs, and the same envelope everywhere.

Endpoints, parameters and credit costs on this page are read from the live catalog and cannot drift from what the API accepts. Field notes were captured on 2026-08-30.