Email check

Mailbox-level facts about a single address, without running a platform sweep.

Request

POST /v2/email
curl -X POST https://api.digifootprint.dev/v2/email \
  -H "Authorization: Bearer dfp_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email": "someone@example.com"}'
200 OK
{
  "lookupId": "8aff4f3a-c8c0-44ba-a555-2de3657849a3",
  "email": {
    "address": "hello@newco-signup.io",
    "deliverable": true,
    "disposable": false,
    "freeProvider": false,
    "customDomain": true,
    "roleAccount": true,
    "acceptAll": false,
    "domain": {
      "name": "newco-signup.io",
      "tld": ".io",
      "registered": true,
      "createdAt": "2026-09-02T00:00:00.000Z",
      "updatedAt": "2026-09-02T00:00:00.000Z",
      "expiresAt": "2027-09-02T00:00:00.000Z",
      "ageDays": 23,
      "registrar": "NameCheap, Inc.",
      "registeredTo": null,
      "validMx": true,
      "mxHosts": ["mx1.privateemail.com"],
      "spfStrict": false,
      "dmarcEnforced": false,
      "suspiciousTld": false,
      "websiteExists": false
    },
    "breaches": {
      "listed": true,
      "count": 2,
      "firstSeenAt": "2023",
      "lastSeenAt": "2025",
      "records": [
        { "name": "AlienStealerLogs", "date": "2025" },
        { "name": "Naz.API", "date": "2023" }
      ]
    },
    "age": { "firstSeenAt": "2023", "ageDays": 1003, "source": "breach_records", "estimate": true },
    "risk": {
      "score": 20,
      "signals": [
        { "id": "email.fresh_domain", "module": "email", "label": "Domain was registered less than 30 days ago", "points": 20, "severity": "medium" }
      ]
    }
  }
}

What comes back

The email object is the same one POST /v2/lookup returns with {"checks": {"email": true}}. Add "checks": {"riskSignals": true} to the request to get the named signals behind risk.score (plans that include signals).

deliverable means the mailbox itself exists and accepts mail, established over SMTP rather than guessed from the domain. disposable flags throwaway providers. freeProvider marks consumer webmail and customDomain its opposite; roleAccount marks shared addresses like info@ or support@.

domain is the address’s domain from public registration and DNS records: when it was registered (ageDays), by whom, whether it has working mail servers (validMx), strict SPF and enforced DMARC, a TLD commonly used for abuse, and whether a website answers on it. For webmail these describe the provider - a thirty-year-old gmail.com says nothing about an account created yesterday - so read them together with freeProvider. A signup from a custom domain registered three weeks ago is the thing worth catching.

age is how old the address is - not its domain: the earliest dated evidence that it existed, currently the oldest breach record. It is a lower bound (“at least since”), counted from the end of that year or month so it never overstates, and null when there is no dated evidence. It adds no time to the check: the evidence is already in the response.

breaches lists the known data breaches the address appears in: whether it is listed, how many, the first and last year seen, and each breach by name in records. null when the breach source is not available or you sent "checks": {"breaches": false}, never a guessed zero.

Catch-all domains

A catch-all domain accepts every address, so no amount of probing can establish whether one particular mailbox exists. When acceptAll is true, deliverable comes back null rather than true.

Catch-all domain
{
  "deliverable": null,
  "acceptAll": true
}

Reading that null as a pass is the single most common way this kind of check gets misused. It means the question could not be answered, not that the answer was yes.

Speed

A real mailbox typically resolves in two to four seconds, and a repeat check inside the cache window in tens of milliseconds. Addresses that do not exist are slower, because a mail server that intends to refuse an address is under no obligation to do so promptly. That wait is the price of an answer established over SMTP instead of inferred.

Billing

One email check is billed as one email module unit and appears in your history with its own lookupId. To get these fields alongside a full platform sweep instead, use POST /v2/lookup with {"checks": {"email": true}}.