Clean an existing user list
New signups are checked one by one with POST /v1/check. Users you already have can be checked in bulk: up to 50 addresses per request with POST /v1/check/batch, or a whole CSV export with one command. Bulk checks are part of Premium.
What it finds
| Column in the result | What to do with it |
|---|---|
disposable: true | The account was opened with a throwaway address. Remove its free credits, or ask the user to change to a permanent address. |
deliverable: false | The address can never receive mail. Your emails to it bounce: ask the user to fix it on next login, or deactivate it. |
duplicate_of | Another account uses the same inbox with a different spelling (john+2@gmail.com, j.o.h.n@gmail.com). Review and merge. |
email_normalized | The canonical inbox. Store it in a unique column so the duplicates can't come back. |
relay, alias, found, registration | Trust signals, the same as for single checks: see Check an email. |
Option 1: one command
You need Node.js 20 or later and a Premium API key. Export your users to CSV with an email column, then run:
SIGNITIZE_API_KEY=sk_live_... npx signitize clean users.csv
Checking users.csv -> users.cleaned.csv
Checked 1,000 of 18,250 rows.
...
Rows in file: 18,250
Checked now: 18,212
Disposable: 1,904
Undeliverable: 311
Privacy relay: 97
Duplicates: 1,566 (see duplicate_of)
Errors: 38 (see error)
Output: users.cleaned.csv
Quota left: 81,750 this month
users.cleaned.csvcontains your columns plusemail_normalized,disposable,deliverable,relay,provider,alias,found,registration,duplicate_ofanderror.duplicate_ofis the row number (as in a spreadsheet, with the header on row 1) of the first account with the same inbox. It is empty for the first one.--column <name>picks another column,--out <file>another output file.- Resumable: if it stops (quota used up, outage, Ctrl+C), run the same command again. Rows already in the output file are skipped and never counted twice.
- It waits automatically when the API asks it to slow down, and retries addresses whose check could not complete.
- Everything runs on your machine. Signitize stores no addresses.
Option 2: your own code
import { Signitize, isBatchError } from 'signitize'
const signitize = new Signitize() // reads SIGNITIZE_API_KEY
const rows = await signitize.checkBatch(['John+1@gmail.com', 'abc@yopmail.com', 'nope'])
for (const row of rows) {
if (isBatchError(row)) console.log(row.input, row.error.type) // 'ValidationError' or 'InternalServerError'
else console.log(row.input, row.email, row.disposable, row.deliverable)
}
Or with curl:
curl https://api.signitize.io/v1/check/batch \
-H "Authorization: Bearer $SIGNITIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"emails":["John+1@gmail.com","abc@yopmail.com","nope"]}'
The response has one row per address, in the order you sent them. A row is either the normal check result plus input, or an error for that address only:
{
"results": [
{ "input": "John+1@gmail.com", "email": "john@gmail.com", "disposable": false, "deliverable": true, "relay": false, "provider": "gmail", "registration": null, "alias": ["plus"], "found": 0 },
{ "input": "abc@yopmail.com", "email": "abc@yopmail.com", "disposable": true, "deliverable": true, "relay": false, "provider": null, "registration": "2004-05-26", "alias": [], "found": 0 },
{ "input": "nope", "error": { "type": "ValidationError", "message": "Invalid email \"nope\": expected local@domain" } }
]
}
Row error.type | Meaning | Counted | What to do |
|---|---|---|---|
ValidationError | Not an email address | Yes | Fix or remove the record |
InternalServerError | The check could not run for this address | No | Send it again later |
When the whole request fails, the status code says why:
| Status | error.type | Meaning |
|---|---|---|
403 | AuthorizationError | Your plan has no bulk checks. They need Premium |
422 | ValidationError | The body is wrong: emails must be 1 to 50 strings of at most 254 characters |
429 | RateLimitError | Too fast for your plan. Wait Retry-After seconds and send the batch again |
429 | QuotaExceededError | The batch doesn't fit what is left of your quota. Nothing was counted |
Quota
- Each address counts as one check, the same as a single check. A batch of 50 uses 50.
- If the batch doesn't fit what is left, it is refused as a whole and nothing is counted. Send a smaller batch or wait for the reset.
- Addresses the check could not complete (
InternalServerErrorrows) are not counted. - Premium has 100,000 checks a month, so a list of 300,000 users takes three months of Premium. The command resumes where it stopped each month.
Large lists: Enterprise
For more than 100,000 users, or more than 100,000 checks a month, Enterprise adds bulk credits that never expire to Premium:
- Bulk requests use the credits first, so your monthly quota stays free for live signups.
- Single
/v1/checkcalls never use bulk credits. GET /v1/usageshows the credits left inbulk_credits.
Enterprise is invoiced. Write to support@signitize.io with the size of your list.
Next steps
- Store
email_normalizedin a unique column and check new signups withPOST /v1/check, so the list stays clean. - Never block a signup: keep new signups working when a check fails.