Domain age as a fraud signal: what a new domain tells you at sign-up
A domain registered days ago is a real risk signal at sign-up, not proof of fraud. How to check domain age with RDAP and when to review instead of block.
A domain age check asks one question: how long ago was the domain in this email address registered? A company-looking domain registered three days ago is a real risk signal at sign-up, because throwaway domains made for trial abuse and phishing are almost always new. It isn’t proof of anything, since real startups register their domain the week they launch. Use domain age to decide how closely to look at a sign-up, never as the only reason to reject one.
Why new domains show up in abuse
Once you block disposable inboxes and ask for a work email, the cheapest way around the rule is to register a domain. A few dollars buys acme-trial.io, and with a catch-all mailbox every address on it works: jane@, jane2@, ops@. Each one passes a “custom domain” check, receives your confirmation email and starts a fresh trial.
New domains turn up in three patterns:
- Trial abuse. A domain that looks like a company, registered to farm free trials, credits or seats. It usually has no website or a placeholder, and no SPF or DMARC, because it never sends mail.
- Phishing and impersonation. Attackers register lookalikes such as
acme-billing.ioshortly before a campaign, then sign up for products that send email or host pages for them (invites, shared documents, forms) to borrow those products’ reputation. - Burn and replace. When one domain lands on a blocklist, the next one gets registered. The domain you haven’t seen before is, by definition, not on any list yet. That’s the gap the disposable email problem describes.
In all three, the domain is days or weeks old when it reaches your form. Established companies rarely sign up from a domain registered last week.
Why it isn’t proof
Plenty of legitimate sign-ups come from new domains too:
- A startup registers its domain, sets up Google Workspace and signs up for its tools in the same week.
- A company renames itself, launches a product on its own domain, or spins out a new business.
- An agency registers a domain for a client project and signs up for the tools that project needs.
The signal also misses abuse in the other direction. A domain bought on the aftermarket keeps its original registration date, so a domain registered in 2012 says nothing about who owns it today. Age describes the domain, not the person signing up.
The same number tells different stories depending on what surrounds it:
| Sign-up | Domain age | Other signals | Likely story |
|---|---|---|---|
jane@acme-trial.io |
3 days | No website, no SPF or DMARC | A throwaway for a free trial |
billing@acme-billing.io |
9 days | Parked page, name borrows an existing brand | Impersonation |
founder@newco.ai |
6 days | Google Workspace, DMARC, a real homepage | A startup in its first week |
jane@acme.io |
17 years | Google Workspace, DMARC reject |
An established company |
Only the first two deserve a closer look, and neither deserves an automatic rejection.
What RDAP is
RDAP (Registration Data Access Protocol) is the successor to WHOIS. WHOIS returns free text whose layout differs from one registry to the next, which makes it fragile to parse; RDAP answers over HTTPS with structured JSON, including standard events such as registration, expiration and last changed. For generic top-level domains such as .com, RDAP is now the official source of registration data. Many country-code registries run it too, but not all of them do, and some don’t publish a registration date. Registrant names and contacts are usually redacted, so the dates are the part you can use. A trimmed answer:
{
"objectClassName": "domain",
"ldhName": "acme-trial.io",
"events": [
{ "eventAction": "registration", "eventDate": "2026-10-02T09:14:00Z" },
{ "eventAction": "expiration", "eventDate": "2027-10-02T09:14:00Z" }
]
}
Domain age is today’s date minus the registration event.
How isBusinessEmail gets domain age
A normal check never waits on a registry. Domain age is part of the deep check, which you request with deep=true in the body or query string of /v1/check:
- It needs a secret key. Publishable keys never receive deep enrichment.
- It counts as 5 checks against your daily quota, and an account can run at most 1,000 deep checks a day (rate limits). On a new account with 100 checks a day that’s 20 deep checks; at tier 1, with 1,000 a day, it’s 200.
- It looks up the registrable domain over RDAP and fetches its homepage. Both requests carry the domain only, never the address (privacy and data).
- Without
deep=true,domain_age_daysisnullunless the domain was already enriched.
The fields it fills, from response fields and reason codes:
| Field or reason | Meaning |
|---|---|
domain_age_days |
Days since registration, from RDAP; null when unknown |
is_new_domain |
true when the domain is less than 30 days old |
new_domain |
Reason: registered less than 30 days ago (strong negative) |
young_domain |
Reason: registered less than 180 days ago (negative) |
parked_domain |
Reason: the homepage is a parking or “for sale” page (strong negative) |
no_website |
Reason: no homepage answered within the time limit (negative) |
has_website |
Reason: a homepage answered (weak positive) |
A deep check of the throwaway from the table above comes back like this (trimmed):
{
"domain": "acme-trial.io",
"category": "unknown",
"recommendation": "review",
"is_new_domain": true,
"domain_age_days": 3,
"reasons": ["custom_domain", "no_spf_no_dmarc", "new_domain", "no_website"]
}
These reasons adjust the confidence score of a custom domain. A three-day-old domain with no website and no SPF or DMARC falls below the threshold and comes back unknown: review under the default b2b policy, block under strict (categories and policies). A six-day-old domain on Google Workspace with DMARC and a real homepage usually stays business and allow, which is the right answer for the startup in the table. If age matters to your decision, read is_new_domain and domain_age_days yourself instead of relying on recommendation alone.
null doesn’t mean old. If the registry publishes no date or the lookup times out, domain_age_days is null and is_new_domain is false. Treat that as unknown and decide on the other signals.
When a deep check is worth it
Deep checks cost five times as much and are capped, so spend them where a bad account costs real money or time:
- Trials with expensive resources: AI credits, compute, SMS, seats, data exports. The free trial abuse playbook shows where age fits.
- Leads going to sales, before someone spends an hour on discovery with a domain registered on Tuesday. Lead scoring by email domain shows how to weigh it in a score.
- Applications you approve: partner programs, marketplace sellers, anything with payouts or elevated permissions.
Skip them on newsletter forms, content downloads, waitlists and logins, where a wrong guess costs almost nothing.
Two habits keep the budget small:
- Check first, then go deep. A deep check on a
gmail.comaddress still counts as 5, and a shared provider has no registration date worth paying for. Run a normal check, and repeat it withdeep=trueonly when the category isbusinessorunknown. - Store the registration date, not the age. Save today’s date minus
domain_age_dayswith the account. The age changes every day; the date doesn’t, so you never need to look it up twice.
Putting it into code
Deep checks wait for the registry and the homepage, so they’re slower than a normal check. Run them at trial start, or in a background job right after the account is created and before the expensive features unlock.
const API = 'https://api.isbusinessemail.com/v1/check';
async function check(email, deep = false) {
try {
const res = await fetch(API, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IBE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, policy: 'b2b', deep }),
signal: AbortSignal.timeout(deep ? 6000 : 2500),
});
return res.ok ? await res.json() : null;
} catch {
return null;
}
}
async function screenTrialSignup(email) {
let v = await check(email);
if (!v) return { lane: 'allow', recheck: true }; // fail open
if (v.recommendation === 'block') return { lane: 'block' };
if (['business', 'unknown'].includes(v.category)) v = (await check(email, true)) ?? v;
const codes = v.reasons.map((r) => r.split(':')[0]);
const tenant = v.workspace.google_workspace.detected || v.workspace.microsoft_365.detected;
if (codes.includes('parked_domain')) return { lane: 'review', step: 'manual_approval' };
if (v.is_new_domain && !tenant) return { lane: 'review', step: 'card' };
if (v.is_new_domain) return { lane: 'allow', watch: true };
if (v.recommendation === 'review') return { lane: 'review', step: 'confirm_email' };
return { lane: 'allow' };
}
The same logic as a decision table:
| What the check shows | Lane | What to do |
|---|---|---|
| Disposable, blocked or invalid | block | Age doesn’t matter; the category decides |
| New domain, parked or no website, no SPF or DMARC | review | Card on file or manual approval before trial resources |
| New domain with Google Workspace or Microsoft 365 and a homepage | allow | Watch usage for the first weeks |
young_domain (30 to 180 days), otherwise normal |
allow | A small penalty in a lead score, no extra friction |
domain_age_days is null |
decide on the rest | Don’t treat unknown age as old |
| Older than 180 days, normal setup | allow | Nothing to do |
Review, don’t block
Blocking on age alone turns away founders in their first week, often the customers who would grow with you. A review lane costs a few people a little friction and keeps them:
- Ask for a card before the expensive part of the trial, not before sign-up.
- Hold back costly features, such as bulk exports, API credits or invites to many users, until someone approves the account or a few days of normal usage pass.
- Approve manually on sales-led products. A two-minute look at the homepage and the domain usually settles it.
- Ask one question, such as the company website, and compare it with the email domain.
- Check again later. A domain that was new at sign-up is a month older a month later. Lift the limits when the account looks normal.
Impersonation needs one more step on your side. is_lookalike compares a domain with popular email providers, not with your brand, so compare new domains with your own domain and your customers’ domains yourself. Lookalike and punycode domains covers the pattern.
Next steps
- Pick the forms where a bad account is expensive, and send
deep=truethere only. - Test your review lane with
unknown@test.isbusinessemail.com. It returnsis_new_domain: truewith thenew_domainandparked_domainreasons, needs no key and is never counted (test addresses). - Store
category,domain_age_daysand the registration date on every account, and compare conversion and abuse by age after a few weeks. - Read free trial abuse: the email signals that catch it for the rest of the playbook, then get a free API key.
Frequently asked questions
How do I check when a domain was registered?
Look the domain up over RDAP, the structured successor to WHOIS, and read the date of its registration event. You can query the registry yourself, use a bootstrap service such as rdap.org, or call an API: isBusinessEmail returns domain_age_days on a deep check.
Is a newly registered domain a sign of fraud?
It is a risk signal, not proof. Throwaway domains made for trial abuse and phishing are almost always new, but so are the domains of genuine startups and new projects. Use age to decide how closely to look at a sign-up, not whether to reject it.
How new is too new?
isBusinessEmail flags domains under 30 days old with new_domain and is_new_domain, and domains under 180 days old with young_domain. Many teams review the first group and only lower the score for the second, but the right cut-off depends on what a bad account costs you.
Why is domain_age_days null?
The registration date is only looked up on a deep check, or when the domain was already enriched. Even then it can be null when the registry does not publish a registration date over RDAP or the lookup times out, so treat null as unknown rather than old.
Does a deep check cost more than a normal check?
Yes. A check with deep=true counts as 5 checks against your daily quota, and an account can run at most 1,000 deep checks a day. Deep checks need a secret key and take longer, because they wait for the registry and the domain's homepage.
Should I block sign-ups from newly registered domains?
No. Blocking on age alone turns away founders in their first week. Send new domains to a review lane instead: ask for a card before the expensive part of the trial, hold back costly features, or have someone approve the account.