Skip to content

Verifying student and academic email addresses worldwide

· 6 min read · Business email

To verify a student email address you need two checks: that the domain belongs to an academic institution, and that the person can actually receive mail there. The first is a domain lookup (TLD rules like .edu and .ac.uk, plus a university domain list for the many countries those rules miss). The second is a one-time code sent to the address. Neither one proves current enrollment, so if you're giving away something valuable, re-verify periodically and accept that some staff and alumni will qualify.

This post covers both checks, the gaps in each, and code for the flow.

Three questions, not one

"Is this a student email?" bundles three questions with very different costs:

Question How to answer it Cost
Is the domain an academic institution? TLD rules plus a domain list Free, milliseconds
Does this person control the mailbox? Send a code or magic link One email
Is this person currently a student? Institution data, documents, or a verification vendor Money or manual review

Most products offering a student discount stop after the second question, and that's reasonable for a discount on a $10/month plan. If the benefit is expensive (free hardware, a large credit), the third question is the one that matters, and email alone can't answer it.

Rule 1: Academic TLD patterns

Many of the world's universities sit under a registry-controlled academic namespace, which makes the domain check almost free:

  • .edu is restricted to US postsecondary institutions. Under EDUCAUSE's policy, new registrants must be institutionally accredited by an agency recognized by the US Department of Education (or be a qualifying system or district office). Registrations from before October 29, 2001 are grandfathered, so a handful of older .edu domains belong to organizations that wouldn't qualify today.
  • .ac.<cc> is used by many countries for academic institutions: ac.uk (run by Jisc), ac.jp, ac.in, ac.nz, ac.za, ac.kr and others.
  • .edu.<cc> is the other common pattern: edu.au, edu.cn, edu.in, edu.sg.
  • .sch.uk covers UK schools, which matters if you serve under-18s.

Two details trip people up. First, the rule has to look at the full hostname, not just the registrable domain: student@cs.harvard.edu and student@ox.ac.uk should both match. Second, the patterns mean different things in different countries. Japan's registry, JPRS, splits academic domains in two: ac.jp is for universities and other schools under the School Education Law, while ed.jp is for institutions mainly serving under-18s (kindergartens, primary and secondary schools). A rule that only knows ac misses Japanese school pupils, which may be exactly what you want.

Rule 2: The countries TLD rules miss

This is the gap most implementations don't notice until a customer complains. Large parts of the academic world don't use an academic second-level domain at all:

  • Germany: tum.de, uni-heidelberg.de, fu-berlin.de
  • Switzerland: ethz.ch, epfl.ch
  • Netherlands: uva.nl, tudelft.nl
  • Canada: utoronto.ca, mcgill.ca, ubc.ca
  • France: many universities use plain .fr domains

A TLD-only check says a student at ETH Zurich isn't a student. For these countries you need a list of institution domains. The most widely used open one is the Hipo university-domains-list, an MIT-licensed JSON file where each entry has a name, country, alpha_two_code, domains and web_pages. Its maintainers are upfront about its limits: it covers "most of the universities of the world", it is community-maintained, and it lists only the main domain, so departmental addresses like user@dept.university.edu need a suffix match rather than an exact one.

Here's a checker that combines both rules:

// world_universities_and_domains.json from github.com/Hipo/university-domains-list
import universities from './world_universities_and_domains.json' with { type: 'json' };

const ACADEMIC_SLD = new Set(['ac', 'edu', 'sch']);

const UNI_DOMAINS = new Map();
for (const u of universities) {
  for (const d of u.domains) UNI_DOMAINS.set(d.toLowerCase(), u.name);
}

export function academicInstitution(email) {
  const host = email.slice(email.lastIndexOf('@') + 1).trim().toLowerCase();
  const labels = host.split('.');

  // 1. Known institution domain, matched on the host or any parent of it.
  for (let i = 0; i < labels.length - 1; i++) {
    const candidate = labels.slice(i).join('.');
    if (UNI_DOMAINS.has(candidate)) {
      return { match: true, rule: 'list', institution: UNI_DOMAINS.get(candidate) };
    }
  }

  // 2. Registry-controlled academic namespaces.
  const tld = labels.at(-1);
  const sld = labels.at(-2);
  if (tld === 'edu') return { match: true, rule: 'tld', institution: null };
  if (labels.length >= 3 && ACADEMIC_SLD.has(sld)) {
    return { match: true, rule: 'tld', institution: null };
  }
  return { match: false };
}

academicInstitution('jo@cs.ethz.ch');  // rule: 'list' (if ethz.ch is in the list)
academicInstitution('jo@ox.ac.uk');    // { match: true, ... }
academicInstitution('jo@gmail.com');   // { match: false }

Check the list first so you get the institution's name when it's known, then fall back to the pattern rule.

The false positive in pattern rules

The sld === 'edu' rule is label-based. It doesn't confirm that edu.<cc> is a registry-controlled academic zone in that country; it only checks the text. If someone registers a domain literally named edu or ac under an open TLD and creates subdomains, me@uni.edu.xyz passes. pingutil's own rule has the same weakness, and so does every simple implementation. If the benefit is worth gaming, restrict the pattern rule to an explicit set of known academic suffixes (ac.uk, edu.au, ac.jp and so on) instead of matching any country.

Rule 3: Prove the person controls the address

A domain check only tells you about the domain. Anyone can type someone@stanford.edu. Send a short-lived code and make the user enter it:

  1. Generate a 6-digit code with a cryptographic RNG (crypto.randomInt(0, 1_000_000) in Node).
  2. Store a hash of it with the user ID, the email, and an expiry of 10 to 15 minutes.
  3. Email it, and allow a small number of attempts (five is plenty) before invalidating.
  4. On success, record verified_at and the institution domain on the user.

University mail systems can be aggressive with filtering, so keep the message plain and from a domain with SPF, DKIM and DMARC set up, and tell users to check quarantine if it doesn't arrive. The confirmation step is what turns a syntactically plausible address into a working one; email validation vs verification covers the distinction in more depth.

Rule 4: Accept that staff and alumni look the same

Academic domains are issued to everyone at the institution: students, faculty, administrators, contractors. Some institutions also let graduates keep an address. The University of Louisville, for example, offers alumni "Email for Life" forwarding, and Kyoto University gives alumni a lifelong address on kyoto-u.jp, a domain no TLD rule would flag as academic.

Some institutions use different subdomains for students (for example a student. or stud. host), but there's no standard, and the convention changes from one university to the next. Don't build logic on it.

The practical answer is time-boxing. Store verified_at and require re-verification every 12 months. Students who've graduated and lost their mailbox drop off naturally; the ones who kept an alumni address get an extra year, which for most discounts is an acceptable leak. If it isn't, you need enrollment proof from the institution or a specialist verification service, and email is no longer the right tool.

Where pingutil fits

pingutil applies the TLD rules (.edu, .ac.<cc>, .edu.<cc>, .sch.<cc>) against the full hostname and returns category: "educational" with company: null. It does not include a university domain list, so ethz.ch and utoronto.ca come back as company. Pair it with the list-based check above and treat a match from either as academic:

const res = await fetch('https://pingutil.com/v1/lookup', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.PINGUTIL_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({ input: email, skip_enrichment: true }),
});
const data = await res.json();

if (data.verdict.is_spam) return { eligible: false, reason: data.verdict.reason };
const local = academicInstitution(email);
const isAcademic = data.category === 'educational' || local.match;
return { eligible: isAcademic, needsCode: isAcademic };

There's more on the category on the educational domains page, and the API reference has the full response shape. Government domains use the same kind of TLD rule with the same blind spots; detecting government email domains covers that side.

A checklist for student discounts

  • Match academic TLD patterns on the full hostname, not only the registrable domain.
  • Add a university domain list for Germany, Switzerland, the Netherlands, Canada, France and anywhere else your users are.
  • Prefer an explicit list of academic suffixes over "any ac or edu label" if the discount is worth gaming.
  • Send a one-time code; never trust a typed address.
  • Store verified_at and re-verify yearly.
  • Block disposable and randomized addresses first. Throwaway inboxes are the cheapest way to farm any per-account benefit, and the same tricks show up in free-trial abuse.

Questions people ask

Does a .edu email address prove someone is a student?
No. It shows the address is issued by a US institution, and a confirmation email shows the person controls it. Staff, faculty and sometimes alumni have addresses on the same domain, so current enrollment needs a separate check.
Which countries use .ac or .edu second-level domains for universities?
Many, including the UK (ac.uk), Japan (ac.jp), Australia (edu.au), China (edu.cn), India (ac.in and edu.in), New Zealand (ac.nz) and South Africa (ac.za). Germany, Switzerland, the Netherlands, France and Canada mostly do not; their universities use ordinary domains like tum.de, ethz.ch or utoronto.ca.
How often should I re-verify a student discount?
Once a year is a common, sensible choice. Students graduate and university accounts are eventually closed or turned into alumni addresses, so a verification from three years ago says little about today.