Blacklist monitoring
Keep an eye on the addresses and domains you send from. Enroll a target once and we re-check it on a schedule; call the check endpoint when you want an answer right now — after requesting a delisting, say.
GET /v1/blacklist/targetsPOST /v1/blacklist/targetsGET /v1/blacklist/targets/{id}DELETE /v1/blacklist/targets/{id}POST /v1/blacklist/targets/{id}/checkGET /v1/blacklist/targets/{id}/checksEvery endpoint takes a secret key in the Authorization header — see
Authentication.
The flow
Section titled “The flow”- Enroll a domain or IP with
POST /v1/blacklist/targets. The first check runs on the next scheduled pass. - Poll
GET /v1/blacklist/targetsfor anything whosestatusislisted. - Re-check on demand with
POST /v1/blacklist/targets/{id}/checkonce you’ve asked a list to remove you, instead of waiting for the next pass.
Step 2 has an alternative worth taking: set a webhook on the target and we POST each listing and delisting to you as it happens, signed, instead of you polling for it. See Blacklist webhooks.
Add a target
Section titled “Add a target”POST /v1/blacklist/targets| Field | Type | Required | Description |
|---|---|---|---|
value |
string | Yes | A domain (example.com) or an IP address, v4 or v6. |
label |
string | No | Your own name for it, e.g. Primary sending IP. Max 120 characters. |
monitoring_enabled |
boolean | No | Defaults to true. Set false to enroll without scheduling checks. |
Whether value is a domain or an address is decided for you, and the value is
normalized before it’s stored: Example.COM. and example.com are one target.
curl -X POST https://api.tryverifyemail.com/v1/blacklist/targets \ -H "Authorization: Bearer sk_your_api_key" \ -H "Content-Type: application/json" \ -d '{"value": "203.0.113.24", "label": "Primary sending IP"}'const res = await fetch("https://api.tryverifyemail.com/v1/blacklist/targets", { method: "POST", headers: { Authorization: `Bearer ${process.env.VERIFYEMAIL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ value: "203.0.113.24", label: "Primary sending IP" }),});
const { created, target } = await res.json();import os, requests
res = requests.post( "https://api.tryverifyemail.com/v1/blacklist/targets", headers={"Authorization": f"Bearer {os.environ['VERIFYEMAIL_API_KEY']}"}, json={"value": "203.0.113.24", "label": "Primary sending IP"},)
payload = res.json()Response
Section titled “Response”201 Created on first enrollment.
{ "created": true, "target": { "id": "665f1c2a9b4e7d0012ab34cd", "kind": "ipv4", "value": "203.0.113.24", "label": "Primary sending IP", "status": "pending", "monitoring_enabled": true, "listed_on": [], "last_checked_at": null, "next_check_at": "2026-08-21T10:04:11.000Z", "webhook_configured": false, "created_at": "2026-08-21T10:04:11.000Z", "updated_at": "2026-08-21T10:04:11.000Z" }}A value that is neither an IP address nor a plausible domain answers 400.
The target object
Section titled “The target object”| Field | Description |
|---|---|
id |
Use this in every other call on this page. |
kind |
domain, ipv4 or ipv6, decided when the target was added. |
value |
The normalized target, in the form the lists are queried with. |
status |
See the table below. |
monitoring_enabled |
Whether scheduled checks run. Pausing keeps the history. |
listed_on |
Stable blocklist ids currently listing the target, e.g. spamhaus_zen. |
last_checked_at |
When we last completed a check, or null if never. |
next_check_at |
When the next scheduled check is due. |
webhook_configured |
Whether transition webhooks are wired up for this target. |
status |
Meaning |
|---|---|
pending |
Added, not yet checked. |
clean |
The last check found no listing. |
listed |
On at least one blocklist. See listed_on. |
unknown |
The last check established nothing — every applicable list refused or failed. |
Checks are scheduled by state, not on a fixed clock: a listed target is
re-checked roughly hourly because somebody is usually waiting on a delisting,
and everything else roughly every six hours.
List your targets
Section titled “List your targets”GET /v1/blacklist/targets{ "targets": [ { "id": "665f1c2a9b4e7d0012ab34cd", "kind": "ipv4", "value": "203.0.113.24", "status": "listed", "listed_on": ["spamhaus_zen"] } ]}Retrieve one target
Section titled “Retrieve one target”GET /v1/blacklist/targets/{id}Returns a single target object in the shape shown under
add a target. Unknown or not yours → 404.
Check now
Section titled “Check now”POST /v1/blacklist/targets/{id}/checkRuns the check immediately rather than waiting for next_check_at, and returns
the target as the check left it alongside the check itself — so confirming a
delisting is one request, not two.
curl -X POST https://api.tryverifyemail.com/v1/blacklist/targets/{id}/check \ -H "Authorization: Bearer sk_your_api_key"{ "target": { "id": "665f1c2a9b4e7d0012ab34cd", "status": "clean", "listed_on": [], "last_checked_at": "2026-08-21T11:30:02.000Z", "next_check_at": "2026-08-21T17:30:02.000Z" }, "check": { "id": "665f1c2a9b4e7d0012abcdef", "target_id": "665f1c2a9b4e7d0012ab34cd", "checked_at": "2026-08-21T11:30:02.000Z", "listed": false, "listed_on": [], "failed_on": ["spamhaus_zen"] }}failed_on lists the providers that supplied no evidence in either direction on
this check. They are reported rather than hidden: a list that keeps appearing
there is an operational problem, not a clean result.
Check history
Section titled “Check history”GET /v1/blacklist/targets/{id}/checks| Parameter | Type | Description |
|---|---|---|
limit |
integer | Checks per page, 1–200. |
cursor |
string | The next_cursor from the previous page. |
{ "checks": [ { "id": "665f1c2a9b4e7d0012abcdef", "target_id": "665f1c2a9b4e7d0012ab34cd", "checked_at": "2026-08-21T11:30:02.000Z", "listed": false, "listed_on": [], "failed_on": [] } ], "next_cursor": null}Newest first. next_cursor is null on the last page; treat it as opaque and
pass it back verbatim. History is retained for 30 days.
Remove a target
Section titled “Remove a target”DELETE /v1/blacklist/targets/{id}{ "deleted": true }Removes the target and its check history. Deleting something you don’t have
enrolled answers {"deleted": false} rather than a 404 — the end state you
asked for already holds.