@mailtester-ninja/client 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -3
- package/dist/index.cjs +31 -5
- package/dist/index.d.cts +51 -2
- package/dist/index.d.ts +51 -2
- package/dist/index.js +31 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MailTester Ninja for JavaScript and TypeScript
|
|
2
2
|
|
|
3
|
-
Official client for the [MailTester Ninja](https://mailtester.ninja) email verification API. Check in real time whether an email address is valid and deliverable: each address is checked against the recipient's mail server, and nothing is stored.
|
|
3
|
+
Official client for the [MailTester Ninja](https://mailtester.ninja) email verification API. Check in real time whether an email address is valid and deliverable: each address is checked against the recipient's mail server, and nothing is stored. With a paid key, `find` also looks up a person's work email from their name and company domain.
|
|
4
4
|
|
|
5
5
|
- Zero dependencies, typed, ESM and CommonJS
|
|
6
6
|
- Node.js 18+, Deno, Bun and browsers (CORS is open)
|
|
@@ -48,14 +48,32 @@ for await (const { email, result, error } of mt.verifyMany(['info@apple.com', 's
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## Find a work email (paid key)
|
|
52
|
+
|
|
53
|
+
`find` calls the Finder API: it tests the most common address formats one by one (first.last, flast, first, firstlast, first_last, f.last) and returns the first one the mail server accepts.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
const r = await mt.find('Jean-Pierre', 'De La Tour', 'example.com');
|
|
57
|
+
if (r.found) console.log(r.email); // an address the mail server just accepted
|
|
58
|
+
else console.log(r.message, r.guess); // e.g. "Catch-All" and the most common format
|
|
59
|
+
console.log(r.permutations); // formats tested, each one counted as a verification
|
|
60
|
+
console.log(r.format, r.confidence); // e.g. "flast" "medium": an initial, a namesake may share it
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Each format tested counts as one verification against your daily limit, even when nothing is found. The search paces itself within the rate of your key.
|
|
64
|
+
- `confidence` tells how sure the address is to belong to this person: `high` when it holds the first and last name, `medium` with an initial, `low` with the first name only. `tested` lists every address checked, with its verdict.
|
|
65
|
+
- On a catch-all domain nothing can be confirmed: `found` is `false` and `guess` holds the most common format.
|
|
66
|
+
- Personal email providers such as gmail.com are not searched (`message` is `Webmail`).
|
|
67
|
+
- A free key is refused with `MailTesterKeyError`; over the rate of your key, `MailTesterRateLimitError` carries `retryAfterSeconds`.
|
|
68
|
+
|
|
51
69
|
## Errors
|
|
52
70
|
|
|
53
71
|
An error is never a verdict: do not drop an address because of it.
|
|
54
72
|
|
|
55
73
|
| Error | When |
|
|
56
74
|
|-------|------|
|
|
57
|
-
| `MailTesterKeyError` | The key is invalid or disabled. |
|
|
58
|
-
| `MailTesterRateLimitError` | A limit was reached (HTTP 429). `retryAfterSeconds` is set on the free tier
|
|
75
|
+
| `MailTesterKeyError` | The key is invalid or disabled, or `find` was called with a free key. |
|
|
76
|
+
| `MailTesterRateLimitError` | A limit was reached (HTTP 429). `retryAfterSeconds` is set on the free tier and by `find`. |
|
|
59
77
|
| `MailTesterError` | Any other HTTP or network error (`status` when there is one). |
|
|
60
78
|
|
|
61
79
|
## Keys and limits
|
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
default: () => index_default
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(index_exports);
|
|
33
|
-
var VERSION = "1.
|
|
33
|
+
var VERSION = "1.1.0";
|
|
34
34
|
var DEFAULT_BASE_URL = "https://happy.mailtester.ninja";
|
|
35
35
|
var FREE_KEY = "free";
|
|
36
36
|
var MailTesterError = class extends Error {
|
|
@@ -90,9 +90,7 @@ var MailTester = class {
|
|
|
90
90
|
get isFree() {
|
|
91
91
|
return this.key === FREE_KEY || this.key.startsWith("mtf_");
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
async verify(email) {
|
|
95
|
-
const url = `${this.baseUrl}/ninja?email=${encodeURIComponent(email.trim())}&key=${encodeURIComponent(this.key)}`;
|
|
93
|
+
async request(url) {
|
|
96
94
|
const controller = new AbortController();
|
|
97
95
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
98
96
|
let res;
|
|
@@ -105,7 +103,11 @@ var MailTester = class {
|
|
|
105
103
|
clearTimeout(timer);
|
|
106
104
|
}
|
|
107
105
|
const text = await res.text();
|
|
108
|
-
|
|
106
|
+
return { res, text, body: parseJson(text) };
|
|
107
|
+
}
|
|
108
|
+
/** Verify one address. Resolves to a verdict, rejects with a MailTesterError when there is none. */
|
|
109
|
+
async verify(email) {
|
|
110
|
+
const { res, text, body } = await this.request(`${this.baseUrl}/ninja?email=${encodeURIComponent(email.trim())}&key=${encodeURIComponent(this.key)}`);
|
|
109
111
|
const message = typeof body?.message === "string" ? body.message : text.trim();
|
|
110
112
|
if (res.status === 429) throw new MailTesterRateLimitError(message, retryAfter(message));
|
|
111
113
|
if (!res.ok) throw new MailTesterError(message || `HTTP ${res.status}`, res.status);
|
|
@@ -113,6 +115,30 @@ var MailTester = class {
|
|
|
113
115
|
if (body.code === "--") throw new MailTesterKeyError(message, res.status);
|
|
114
116
|
return body;
|
|
115
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Find a work email address from a first name, a last name and a company domain (Finder API, paid key).
|
|
120
|
+
* The API tests the most common formats one by one (first.last, flast, first, firstlast, first_last, f.last)
|
|
121
|
+
* and returns the first address the mail server accepts. Each format tested counts as one verification.
|
|
122
|
+
* Resolves to a FindResult (check `found`), rejects with a MailTesterError when the search did not run.
|
|
123
|
+
*/
|
|
124
|
+
async find(first, last, domain) {
|
|
125
|
+
if (this.isFree) {
|
|
126
|
+
throw new MailTesterKeyError("The Finder API needs a paid key: https://mailtester.ninja/verify-emails-unlimited/", 403);
|
|
127
|
+
}
|
|
128
|
+
const qs = new URLSearchParams({ first: first.trim(), last: last.trim(), domain: domain.trim(), key: this.key });
|
|
129
|
+
const { res, text, body } = await this.request(`${this.baseUrl}/find?${qs}`);
|
|
130
|
+
const reason = typeof body?.reason === "string" && body.reason ? body.reason : void 0;
|
|
131
|
+
const message = reason ?? (typeof body?.message === "string" ? body.message : text.trim());
|
|
132
|
+
if (res.status === 429) {
|
|
133
|
+
const wait = Number(res.headers.get("retry-after"));
|
|
134
|
+
throw new MailTesterRateLimitError(message, Number.isFinite(wait) && wait > 0 ? wait : void 0);
|
|
135
|
+
}
|
|
136
|
+
if (res.status === 403 && body) throw new MailTesterKeyError(message, 403);
|
|
137
|
+
if (!res.ok) throw new MailTesterError(message || `HTTP ${res.status}`, res.status);
|
|
138
|
+
if (!body) throw new MailTesterError("Unexpected response from the API", res.status);
|
|
139
|
+
if (body.code === "--") throw new MailTesterKeyError(message, res.status);
|
|
140
|
+
return body;
|
|
141
|
+
}
|
|
116
142
|
/** True only when the address is clearly valid and deliverable (`code` "ok"). */
|
|
117
143
|
async isDeliverable(email) {
|
|
118
144
|
return (await this.verify(email)).code === "ok";
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Official JavaScript and TypeScript client for the MailTester Ninja email verification API.
|
|
3
3
|
* https://mailtester.ninja/api/
|
|
4
4
|
*/
|
|
5
|
-
declare const VERSION = "1.
|
|
5
|
+
declare const VERSION = "1.1.0";
|
|
6
6
|
declare const DEFAULT_BASE_URL = "https://happy.mailtester.ninja";
|
|
7
7
|
/** Keyless free tier: 10 checks a day per IP, one request every 30 seconds. */
|
|
8
8
|
declare const FREE_KEY = "free";
|
|
@@ -27,6 +27,47 @@ interface VerificationResult {
|
|
|
27
27
|
/** Requests made with the key over the last 10 seconds. */
|
|
28
28
|
rate?: number;
|
|
29
29
|
}
|
|
30
|
+
/** Result of a Finder API search (`find`). Check `found` first. */
|
|
31
|
+
interface FindResult {
|
|
32
|
+
/** `ok` found, `ko` not found, `mb` uncertain (catch-all domain, personal email provider, server not checked). */
|
|
33
|
+
code: Verdict;
|
|
34
|
+
/** Accepted, Rejected, Catch-All, Limited, No MX, Disposable, SPAM Block, Timeout, Webmail... */
|
|
35
|
+
message: string;
|
|
36
|
+
/** True when the mail server accepted one of the formats. */
|
|
37
|
+
found: boolean;
|
|
38
|
+
/** The address found, otherwise null. */
|
|
39
|
+
email: string | null;
|
|
40
|
+
/** Pattern of the returned address (email, or guess when not found): first.last, flast, first, firstlast, first_last or f.last. */
|
|
41
|
+
format: string | null;
|
|
42
|
+
/** For an address found: `high` (first and last name in it), `medium` (an initial, a namesake may share it), `low` (first name only). */
|
|
43
|
+
confidence: 'high' | 'medium' | 'low' | null;
|
|
44
|
+
/** Not verified: on a catch-all domain the most common format, for a full mailbox (Limited) that address. */
|
|
45
|
+
guess: string | null;
|
|
46
|
+
first: string;
|
|
47
|
+
last: string;
|
|
48
|
+
/** The normalised domain that was searched. */
|
|
49
|
+
domain: string;
|
|
50
|
+
/** Mail server used for the search. */
|
|
51
|
+
mx: string;
|
|
52
|
+
/** Address formats tested, each one counted as a verification, even when nothing is found. */
|
|
53
|
+
permutations: number;
|
|
54
|
+
/** Every address tested, in order, with its verdict. */
|
|
55
|
+
tested: {
|
|
56
|
+
email: string;
|
|
57
|
+
code: string;
|
|
58
|
+
message: string;
|
|
59
|
+
}[];
|
|
60
|
+
/** Simultaneous connections used by the key. */
|
|
61
|
+
connections?: number;
|
|
62
|
+
/** Daily limit of the key. */
|
|
63
|
+
limit?: number;
|
|
64
|
+
/** Verifications made with the key over the last 10 seconds, permutations included. */
|
|
65
|
+
rate?: number;
|
|
66
|
+
/** Seconds taken by the search. */
|
|
67
|
+
duration?: number;
|
|
68
|
+
/** Why nothing was found, in plain English. Null when found. */
|
|
69
|
+
reason: string | null;
|
|
70
|
+
}
|
|
30
71
|
interface MailTesterOptions {
|
|
31
72
|
/**
|
|
32
73
|
* `free` (no signup), a free token `mtf_...` from https://mailtester.ninja/free-email-verification-api/,
|
|
@@ -72,8 +113,16 @@ declare class MailTester {
|
|
|
72
113
|
constructor(options?: MailTesterOptions | string);
|
|
73
114
|
/** True on the free tier (`free` or a free token), limited to one request every 30 seconds. */
|
|
74
115
|
get isFree(): boolean;
|
|
116
|
+
private request;
|
|
75
117
|
/** Verify one address. Resolves to a verdict, rejects with a MailTesterError when there is none. */
|
|
76
118
|
verify(email: string): Promise<VerificationResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Find a work email address from a first name, a last name and a company domain (Finder API, paid key).
|
|
121
|
+
* The API tests the most common formats one by one (first.last, flast, first, firstlast, first_last, f.last)
|
|
122
|
+
* and returns the first address the mail server accepts. Each format tested counts as one verification.
|
|
123
|
+
* Resolves to a FindResult (check `found`), rejects with a MailTesterError when the search did not run.
|
|
124
|
+
*/
|
|
125
|
+
find(first: string, last: string, domain: string): Promise<FindResult>;
|
|
77
126
|
/** True only when the address is clearly valid and deliverable (`code` "ok"). */
|
|
78
127
|
isDeliverable(email: string): Promise<boolean>;
|
|
79
128
|
/**
|
|
@@ -83,4 +132,4 @@ declare class MailTester {
|
|
|
83
132
|
verifyMany(emails: Iterable<string> | AsyncIterable<string>, options?: VerifyManyOptions): AsyncGenerator<VerifyManyItem>;
|
|
84
133
|
}
|
|
85
134
|
|
|
86
|
-
export { DEFAULT_BASE_URL, FREE_KEY, MailTester, MailTesterError, MailTesterKeyError, type MailTesterOptions, MailTesterRateLimitError, VERSION, type Verdict, type VerificationResult, type VerifyManyItem, type VerifyManyOptions, MailTester as default };
|
|
135
|
+
export { DEFAULT_BASE_URL, FREE_KEY, type FindResult, MailTester, MailTesterError, MailTesterKeyError, type MailTesterOptions, MailTesterRateLimitError, VERSION, type Verdict, type VerificationResult, type VerifyManyItem, type VerifyManyOptions, MailTester as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Official JavaScript and TypeScript client for the MailTester Ninja email verification API.
|
|
3
3
|
* https://mailtester.ninja/api/
|
|
4
4
|
*/
|
|
5
|
-
declare const VERSION = "1.
|
|
5
|
+
declare const VERSION = "1.1.0";
|
|
6
6
|
declare const DEFAULT_BASE_URL = "https://happy.mailtester.ninja";
|
|
7
7
|
/** Keyless free tier: 10 checks a day per IP, one request every 30 seconds. */
|
|
8
8
|
declare const FREE_KEY = "free";
|
|
@@ -27,6 +27,47 @@ interface VerificationResult {
|
|
|
27
27
|
/** Requests made with the key over the last 10 seconds. */
|
|
28
28
|
rate?: number;
|
|
29
29
|
}
|
|
30
|
+
/** Result of a Finder API search (`find`). Check `found` first. */
|
|
31
|
+
interface FindResult {
|
|
32
|
+
/** `ok` found, `ko` not found, `mb` uncertain (catch-all domain, personal email provider, server not checked). */
|
|
33
|
+
code: Verdict;
|
|
34
|
+
/** Accepted, Rejected, Catch-All, Limited, No MX, Disposable, SPAM Block, Timeout, Webmail... */
|
|
35
|
+
message: string;
|
|
36
|
+
/** True when the mail server accepted one of the formats. */
|
|
37
|
+
found: boolean;
|
|
38
|
+
/** The address found, otherwise null. */
|
|
39
|
+
email: string | null;
|
|
40
|
+
/** Pattern of the returned address (email, or guess when not found): first.last, flast, first, firstlast, first_last or f.last. */
|
|
41
|
+
format: string | null;
|
|
42
|
+
/** For an address found: `high` (first and last name in it), `medium` (an initial, a namesake may share it), `low` (first name only). */
|
|
43
|
+
confidence: 'high' | 'medium' | 'low' | null;
|
|
44
|
+
/** Not verified: on a catch-all domain the most common format, for a full mailbox (Limited) that address. */
|
|
45
|
+
guess: string | null;
|
|
46
|
+
first: string;
|
|
47
|
+
last: string;
|
|
48
|
+
/** The normalised domain that was searched. */
|
|
49
|
+
domain: string;
|
|
50
|
+
/** Mail server used for the search. */
|
|
51
|
+
mx: string;
|
|
52
|
+
/** Address formats tested, each one counted as a verification, even when nothing is found. */
|
|
53
|
+
permutations: number;
|
|
54
|
+
/** Every address tested, in order, with its verdict. */
|
|
55
|
+
tested: {
|
|
56
|
+
email: string;
|
|
57
|
+
code: string;
|
|
58
|
+
message: string;
|
|
59
|
+
}[];
|
|
60
|
+
/** Simultaneous connections used by the key. */
|
|
61
|
+
connections?: number;
|
|
62
|
+
/** Daily limit of the key. */
|
|
63
|
+
limit?: number;
|
|
64
|
+
/** Verifications made with the key over the last 10 seconds, permutations included. */
|
|
65
|
+
rate?: number;
|
|
66
|
+
/** Seconds taken by the search. */
|
|
67
|
+
duration?: number;
|
|
68
|
+
/** Why nothing was found, in plain English. Null when found. */
|
|
69
|
+
reason: string | null;
|
|
70
|
+
}
|
|
30
71
|
interface MailTesterOptions {
|
|
31
72
|
/**
|
|
32
73
|
* `free` (no signup), a free token `mtf_...` from https://mailtester.ninja/free-email-verification-api/,
|
|
@@ -72,8 +113,16 @@ declare class MailTester {
|
|
|
72
113
|
constructor(options?: MailTesterOptions | string);
|
|
73
114
|
/** True on the free tier (`free` or a free token), limited to one request every 30 seconds. */
|
|
74
115
|
get isFree(): boolean;
|
|
116
|
+
private request;
|
|
75
117
|
/** Verify one address. Resolves to a verdict, rejects with a MailTesterError when there is none. */
|
|
76
118
|
verify(email: string): Promise<VerificationResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Find a work email address from a first name, a last name and a company domain (Finder API, paid key).
|
|
121
|
+
* The API tests the most common formats one by one (first.last, flast, first, firstlast, first_last, f.last)
|
|
122
|
+
* and returns the first address the mail server accepts. Each format tested counts as one verification.
|
|
123
|
+
* Resolves to a FindResult (check `found`), rejects with a MailTesterError when the search did not run.
|
|
124
|
+
*/
|
|
125
|
+
find(first: string, last: string, domain: string): Promise<FindResult>;
|
|
77
126
|
/** True only when the address is clearly valid and deliverable (`code` "ok"). */
|
|
78
127
|
isDeliverable(email: string): Promise<boolean>;
|
|
79
128
|
/**
|
|
@@ -83,4 +132,4 @@ declare class MailTester {
|
|
|
83
132
|
verifyMany(emails: Iterable<string> | AsyncIterable<string>, options?: VerifyManyOptions): AsyncGenerator<VerifyManyItem>;
|
|
84
133
|
}
|
|
85
134
|
|
|
86
|
-
export { DEFAULT_BASE_URL, FREE_KEY, MailTester, MailTesterError, MailTesterKeyError, type MailTesterOptions, MailTesterRateLimitError, VERSION, type Verdict, type VerificationResult, type VerifyManyItem, type VerifyManyOptions, MailTester as default };
|
|
135
|
+
export { DEFAULT_BASE_URL, FREE_KEY, type FindResult, MailTester, MailTesterError, MailTesterKeyError, type MailTesterOptions, MailTesterRateLimitError, VERSION, type Verdict, type VerificationResult, type VerifyManyItem, type VerifyManyOptions, MailTester as default };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
var VERSION = "1.
|
|
2
|
+
var VERSION = "1.1.0";
|
|
3
3
|
var DEFAULT_BASE_URL = "https://happy.mailtester.ninja";
|
|
4
4
|
var FREE_KEY = "free";
|
|
5
5
|
var MailTesterError = class extends Error {
|
|
@@ -59,9 +59,7 @@ var MailTester = class {
|
|
|
59
59
|
get isFree() {
|
|
60
60
|
return this.key === FREE_KEY || this.key.startsWith("mtf_");
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
async verify(email) {
|
|
64
|
-
const url = `${this.baseUrl}/ninja?email=${encodeURIComponent(email.trim())}&key=${encodeURIComponent(this.key)}`;
|
|
62
|
+
async request(url) {
|
|
65
63
|
const controller = new AbortController();
|
|
66
64
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
67
65
|
let res;
|
|
@@ -74,7 +72,11 @@ var MailTester = class {
|
|
|
74
72
|
clearTimeout(timer);
|
|
75
73
|
}
|
|
76
74
|
const text = await res.text();
|
|
77
|
-
|
|
75
|
+
return { res, text, body: parseJson(text) };
|
|
76
|
+
}
|
|
77
|
+
/** Verify one address. Resolves to a verdict, rejects with a MailTesterError when there is none. */
|
|
78
|
+
async verify(email) {
|
|
79
|
+
const { res, text, body } = await this.request(`${this.baseUrl}/ninja?email=${encodeURIComponent(email.trim())}&key=${encodeURIComponent(this.key)}`);
|
|
78
80
|
const message = typeof body?.message === "string" ? body.message : text.trim();
|
|
79
81
|
if (res.status === 429) throw new MailTesterRateLimitError(message, retryAfter(message));
|
|
80
82
|
if (!res.ok) throw new MailTesterError(message || `HTTP ${res.status}`, res.status);
|
|
@@ -82,6 +84,30 @@ var MailTester = class {
|
|
|
82
84
|
if (body.code === "--") throw new MailTesterKeyError(message, res.status);
|
|
83
85
|
return body;
|
|
84
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Find a work email address from a first name, a last name and a company domain (Finder API, paid key).
|
|
89
|
+
* The API tests the most common formats one by one (first.last, flast, first, firstlast, first_last, f.last)
|
|
90
|
+
* and returns the first address the mail server accepts. Each format tested counts as one verification.
|
|
91
|
+
* Resolves to a FindResult (check `found`), rejects with a MailTesterError when the search did not run.
|
|
92
|
+
*/
|
|
93
|
+
async find(first, last, domain) {
|
|
94
|
+
if (this.isFree) {
|
|
95
|
+
throw new MailTesterKeyError("The Finder API needs a paid key: https://mailtester.ninja/verify-emails-unlimited/", 403);
|
|
96
|
+
}
|
|
97
|
+
const qs = new URLSearchParams({ first: first.trim(), last: last.trim(), domain: domain.trim(), key: this.key });
|
|
98
|
+
const { res, text, body } = await this.request(`${this.baseUrl}/find?${qs}`);
|
|
99
|
+
const reason = typeof body?.reason === "string" && body.reason ? body.reason : void 0;
|
|
100
|
+
const message = reason ?? (typeof body?.message === "string" ? body.message : text.trim());
|
|
101
|
+
if (res.status === 429) {
|
|
102
|
+
const wait = Number(res.headers.get("retry-after"));
|
|
103
|
+
throw new MailTesterRateLimitError(message, Number.isFinite(wait) && wait > 0 ? wait : void 0);
|
|
104
|
+
}
|
|
105
|
+
if (res.status === 403 && body) throw new MailTesterKeyError(message, 403);
|
|
106
|
+
if (!res.ok) throw new MailTesterError(message || `HTTP ${res.status}`, res.status);
|
|
107
|
+
if (!body) throw new MailTesterError("Unexpected response from the API", res.status);
|
|
108
|
+
if (body.code === "--") throw new MailTesterKeyError(message, res.status);
|
|
109
|
+
return body;
|
|
110
|
+
}
|
|
85
111
|
/** True only when the address is clearly valid and deliverable (`code` "ok"). */
|
|
86
112
|
async isDeliverable(email) {
|
|
87
113
|
return (await this.verify(email)).code === "ok";
|
package/package.json
CHANGED