# Email Verifier + GSTIN/PAN/IFSC Validator (Half Price) (`laudable_instrument/email-verifier-india-validators-half-price`) Actor

Bulk email check: syntax, MX / null-MX DNS, disposable domains, role and free-provider flags, typo hints (no SMTP probing). Also validates Indian phone numbers, GSTIN checksum, PAN and IFSC. $0.445 per 1,000.

- **URL**: https://apify.com/laudable_instrument/email-verifier-india-validators-half-price.md
- **Developed by:** [Unadhi](https://apify.com/laudable_instrument) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Email Verifier + GSTIN / PAN / IFSC / Indian Phone Validator (half price, no SMTP)

This actor cleans a list before you use it. It checks **emails**, **Indian phone numbers**, **GSTINs**, **PANs** and **IFSC codes** in bulk. Everything runs offline or through a DNS lookup. **It never opens an SMTP connection to anyone's mail server**, so it can't get your IP blocklisted or look like spam probing.

**Price: $0.445 per 1,000 values** (`email-verified` / `value-validated` at $0.000445). That is half the most-used Store email verifier's $0.89 per 1,000, which also bills platform usage to the user. Duplicates are removed before charging. Rows that fail a check are still charged, because the check itself is the work.

### What you get

| Type | Checks | Key output fields |
|---|---|---|
| Email | Syntax (practical RFC 5321/5322, IDN → punycode). DNS: MX, null MX (RFC 7505) and NXDOMAIN, with A/AAAA as the implicit MX. Disposable domain (9,185-domain CC0 list, subdomains included). Role mailbox (info@, sales@…). Free provider. Typo hint (gmial.com → gmail.com) | `verdict` (valid / risky / invalid / unknown), `reason`, `mxHosts`, `disposable`, `role`, `freeProvider`, `didYouMean` |
| Phone | libphonenumber: valid for its country, line type (mobile / fixed / toll-free…). Default region IN, and handles `0`- and `91`-prefixed inputs | `e164`, `national`, `international`, `lineType`, `waMe` (mobiles only) |
| GSTIN | 15-character format, state code (01–38, 97, 99), embedded PAN, non-zero entity number, **official mod-36 check character** | `valid`, `state`, `pan`, `panHolderType`, `checksumOk`, `reason` (gives the expected check character) |
| PAN | Format AAAAA9999A plus the 4th-character holder type (C company, P individual, F firm…) | `valid`, `holderType` |
| IFSC | Format AAAA0XXXXXX, with the bank name from the bundled RBI bank list (1,511 codes) | `validFormat`, `bank`, `bankKnown` |

#### What "valid" means (and what it doesn't)

- **Email `valid`** = correct syntax, the domain publishes MX records, it isn't disposable and there's no likely typo. **We don't check that the mailbox exists.** Nobody can do that without SMTP probing, and even SMTP probing is unreliable on catch-all servers and Gmail/Outlook. If you need mailbox-level checks, use an SMTP verifier and pay for it.
- `risky` = disposable domain, an A-record-only domain (no MX), or a likely typo. `unknown` = DNS timeout or SERVFAIL, so run it again later.
- **PAN has no public checksum**, so we check format and holder type only. **IFSC has no checksum**, and we don't confirm offline that the branch exists.
- GSTIN `valid` means the number is well-formed and its check character is correct. It does **not** mean the registration is active, which only the GST portal can confirm.

### Input

Pass `emails`, `phones`, `gstins`, `pans` and `ifscs` as lists. You can also pass a `datasetId`, such as the output of a contact-details scraper run. Set `datasetEmailField` (default `emails`; it accepts strings, lists of strings, or lists of `{email: …}` objects) and optionally `datasetPhoneField`. Set `checkDns: false` for a check with no network use at all.

### Measured (box, 29 Sep 2026)

See `test/RESULTS.md`.

- **Hand-labelled sample of 94 emails:** 94/94 correct verdicts. The reference `python-email-validator` (syntax + MX) got 75/94, missing all 12 disposables and 5 of 6 typos. Role flag: 69/69 (66/69 before 3 words were added).
- **19 real public GSTINs** (TCS, Infosys, Wipro, Reliance, HDFC Bank, L\&T, HUL, Maruti, Airtel, ITC): 19/19 pass.
- **GSTIN mutations:** all 9,975 single-character substitutions were caught (100%), and 224 of 225 adjacent transpositions (99.56%).
- **Speed:** 929 unique emails over 209 domains took 3.1 s wall, 1.38 CPU-s and 76 MB peak RAM.

### Privacy (DPDP Act 2023 / GDPR)

Email addresses and phone numbers are personal data. Only process lists you have a lawful basis for, such as your own customers or leads collected with a notice. The actor sends each email's **domain** (not the address) to public DNS resolvers. It stores results only in your run's dataset. It contacts no mail server and no third-party API.

# Actor input Schema

## `emails` (type: `array`):

Email addresses to check. Duplicates are removed before charging.

## `datasetId` (type: `string`):

Read values from a dataset, e.g. the output of a contact-details scraper run.

## `datasetEmailField` (type: `string`):

Field holding a string, a list of strings, or a list of objects with an 'email' key.

## `datasetPhoneField` (type: `string`):

For example 'phones'. Leave empty to skip phones.

## `maxDatasetItems` (type: `integer`):

0 means all.

## `phones` (type: `array`):

Any format. Numbers without a country code are read in the region below.

## `phoneRegion` (type: `string`):

ISO country code used to read phone numbers written without a + country prefix (e.g. IN, US, GB).

## `gstins` (type: `array`):

15-character GST numbers. Checked for format, state code and the embedded PAN, plus the official check character.

## `pans` (type: `array`):

Format and holder-type check. PAN has no public checksum.

## `ifscs` (type: `array`):

Format check and bank-name lookup, done offline.

## `checkDns` (type: `boolean`):

Turn off for a syntax, disposable and role check only, with no network use.

## `dnsTimeoutSecs` (type: `integer`):

Give up on a DNS (MX) lookup after this many seconds.

## `maxConcurrency` (type: `integer`):

Number of DNS lookups run at the same time.

## Actor input object example

```json
{
  "emails": [
    "help@postman.com",
    "info@example.com",
    "test@mailinator.com"
  ],
  "datasetEmailField": "emails",
  "maxDatasetItems": 0,
  "phones": [
    "+919876543210"
  ],
  "phoneRegion": "IN",
  "gstins": [
    "27AAPFU0939F1ZV"
  ],
  "pans": [
    "ABCDE1234F"
  ],
  "ifscs": [
    "HDFC0000001"
  ],
  "checkDns": true,
  "dnsTimeoutSecs": 5,
  "maxConcurrency": 50
}
```

# Actor output Schema

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

No description

## `runStats` (type: `string`):

No description

# 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 = {
    "emails": [
        "help@postman.com",
        "info@example.com",
        "test@mailinator.com"
    ],
    "phones": [
        "+919876543210"
    ],
    "gstins": [
        "27AAPFU0939F1ZV"
    ],
    "pans": [
        "ABCDE1234F"
    ],
    "ifscs": [
        "HDFC0000001"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("laudable_instrument/email-verifier-india-validators-half-price").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 = {
    "emails": [
        "help@postman.com",
        "info@example.com",
        "test@mailinator.com",
    ],
    "phones": ["+919876543210"],
    "gstins": ["27AAPFU0939F1ZV"],
    "pans": ["ABCDE1234F"],
    "ifscs": ["HDFC0000001"],
}

# Run the Actor and wait for it to finish
run = client.actor("laudable_instrument/email-verifier-india-validators-half-price").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 '{
  "emails": [
    "help@postman.com",
    "info@example.com",
    "test@mailinator.com"
  ],
  "phones": [
    "+919876543210"
  ],
  "gstins": [
    "27AAPFU0939F1ZV"
  ],
  "pans": [
    "ABCDE1234F"
  ],
  "ifscs": [
    "HDFC0000001"
  ]
}' |
apify call laudable_instrument/email-verifier-india-validators-half-price --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,laudable_instrument/email-verifier-india-validators-half-price"
        }
    }
}
```

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/xaiOtp8Mha4f3EHTP/builds/pFfoF48q4NeQaMzyV/openapi.json
