---
title: "nextmail.tools"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/python/reference/tools"
area: "Python"
category: "Reference"
---

# nextmail.tools

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

The free tools on the Nextmail website: checks of the public mail records of any domain, which are its DMARC policy, its BIMI logo and a deliverability report across MX, SPF, DKIM, DMARC and BIMI, a reader for the sign-in code in a message, and the price of every plan for a usage. They need no scope.

### `tools.check_dmarc()`

Check a domain's DMARC policy

```python
def check_dmarc(
    domain: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DmarcReportResource
```

Reads the domain's DMARC record from public DNS, or the record of the organisational domain it inherits from, and says how strict it is and what is wrong with it, the DMARC checker on the Nextmail website. `stage` is `missing`, `invalid`, `monitor` for `p=none`, `quarantine` or `reject`, and `issues` lists the problems written to be shown to a person.

**Parameters**

- `domain` (`str`, required): The domain to check, such as `acme.com`. An email address or a URL is read as its domain, and any public domain works, not only the ones in this workspace.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DmarcReportResource` with `domain`, `record`, `inheritedFrom`, every tag in `tags`, `stage` and `issues`.

**Example**

```python
from nextmail import nextmail

report = nextmail.tools.check_dmarc('acme.com')

print(report['stage'], report['tags'].get('p'), report['issues'])
```

**Notes**

- Needs no scope: any key or access token for the workspace may run it.
- A domain that does not read as one is 422 `invalid_parameter` on `domain`, and DNS that did not answer is 503 `unreachable`, which is worth retrying.

Also available in: API [`GET /tools/dmarc`](https://nextmail.sh/docs/api/reference/tools#get-tools-dmarc); TypeScript [`nextmail.tools.checkDmarc()`](https://nextmail.sh/docs/sdk/reference/tools#checkDmarc); Ruby [`tools.check_dmarc`](https://nextmail.sh/docs/ruby/reference/tools#checkDmarc); PHP [`tools->checkDmarc`](https://nextmail.sh/docs/php/reference/tools#checkDmarc); Go [`Tools.CheckDmarc`](https://nextmail.sh/docs/go/reference/tools#checkDmarc); Java [`tools().checkDmarc`](https://nextmail.sh/docs/java/reference/tools#checkDmarc); C# [`Tools.CheckDmarcAsync`](https://nextmail.sh/docs/csharp/reference/tools#checkDmarc); CLI [`nextmail tools check-dmarc`](https://nextmail.sh/docs/cli/reference/tools#tools-check-dmarc).

### `tools.check_bimi()`

Check a domain's BIMI logo

```python
def check_bimi(
    domain: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> BimiReportResource
```

Reads the domain's BIMI record, fetches the logo it names and checks the DMARC policy beside it, since inboxes draw a logo only at `p=quarantine` or `p=reject`, the BIMI checker on the Nextmail website. `status` is `present`, `partial` when the record is there but something stops inboxes showing the logo, `absent` or `invalid`, and `issues` says why.

**Parameters**

- `domain` (`str`, required): The domain to check, such as `acme.com`. An email address or a URL is read as its domain, and any public domain works, not only the ones in this workspace.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`BimiReportResource` with `record`, `logoUrl`, `authorityUrl`, `logoSvg`, `logoReachable`, `dmarcPolicy`, `dmarcEnforced`, `status` and `issues`.

**Example**

```python
from nextmail import nextmail

report = nextmail.tools.check_bimi('acme.com')

if report['status'] != 'present':
    for issue in report['issues']:
        print(issue)
```

**Notes**

- Needs no scope: any key or access token for the workspace may run it.
- A domain that does not read as one is 422 `invalid_parameter` on `domain`, and DNS that did not answer is 503 `unreachable`, which is worth retrying.

Also available in: API [`GET /tools/bimi`](https://nextmail.sh/docs/api/reference/tools#get-tools-bimi); TypeScript [`nextmail.tools.checkBimi()`](https://nextmail.sh/docs/sdk/reference/tools#checkBimi); Ruby [`tools.check_bimi`](https://nextmail.sh/docs/ruby/reference/tools#checkBimi); PHP [`tools->checkBimi`](https://nextmail.sh/docs/php/reference/tools#checkBimi); Go [`Tools.CheckBimi`](https://nextmail.sh/docs/go/reference/tools#checkBimi); Java [`tools().checkBimi`](https://nextmail.sh/docs/java/reference/tools#checkBimi); C# [`Tools.CheckBimiAsync`](https://nextmail.sh/docs/csharp/reference/tools#checkBimi); CLI [`nextmail tools check-bimi`](https://nextmail.sh/docs/cli/reference/tools#tools-check-bimi).

### `tools.check_deliverability()`

Check a domain's deliverability

```python
def check_deliverability(
    domain: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeliverabilityReportResource
```

Checks the records a receiving mailbox reads before it trusts mail from the domain, the deliverability checker on the Nextmail website: MX, SPF, DKIM at the selectors its mail provider uses, DMARC and BIMI. Each check says `pass`, `warn`, `fail` or `absent` with a verdict and notes, and `score` weighs them into 0 to 100, graded `A` to `F`.

**Parameters**

- `domain` (`str`, required): The domain to check, such as `acme.com`. An email address or a URL is read as its domain, and any public domain works, not only the ones in this workspace.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeliverabilityReportResource` with `score`, `grade`, `provider` and `checks`, each with `id`, `label`, `status`, `verdict`, `record` and `notes`.

**Example**

```python
from nextmail import nextmail

report = nextmail.tools.check_deliverability('acme.com')

print(report['grade'], report['score'])

for check in report['checks']:
    print(check['id'], check['status'], check['verdict'])
```

**Notes**

- Needs no scope: any key or access token for the workspace may run it.
- A domain that does not read as one is 422 `invalid_parameter` on `domain`, and DNS that did not answer is 503 `unreachable`, which is worth retrying.

Also available in: API [`GET /tools/deliverability`](https://nextmail.sh/docs/api/reference/tools#get-tools-deliverability); TypeScript [`nextmail.tools.checkDeliverability()`](https://nextmail.sh/docs/sdk/reference/tools#checkDeliverability); Ruby [`tools.check_deliverability`](https://nextmail.sh/docs/ruby/reference/tools#checkDeliverability); PHP [`tools->checkDeliverability`](https://nextmail.sh/docs/php/reference/tools#checkDeliverability); Go [`Tools.CheckDeliverability`](https://nextmail.sh/docs/go/reference/tools#checkDeliverability); Java [`tools().checkDeliverability`](https://nextmail.sh/docs/java/reference/tools#checkDeliverability); C# [`Tools.CheckDeliverabilityAsync`](https://nextmail.sh/docs/csharp/reference/tools#checkDeliverability); CLI [`nextmail tools check-deliverability`](https://nextmail.sh/docs/cli/reference/tools#tools-check-deliverability).

### `tools.read_verification_code()`

Read the sign-in code in a message

```python
def read_verification_code(
    body: VerificationCodeRead,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> VerificationCodeResource
```

Finds the sign-in or verification code in a message you pass in, with the reader that puts a code at the top of a message in Nextmail. Send the whole MIME source as `raw`, or the `subject` with `html` or `text`.

It answers only when it is sure: the message has to hold exactly one code worded as one, such as a verification code, a one-time code or a security code, in any of the common languages. Two different codes, or a number that could be an order, a booking, a meeting, a phone number or a promotion, answer with `code` set to `None`.

It reads only what you send, so it cannot tell whether the sender is who it claims to be. The `verificationCode` on a message in your mailbox also requires the sender to pass authentication.

**Parameters**

- `body['raw']` (`str`): The whole message as MIME source, as it was received. Wins over the other keys when it is set.
- `body['subject']` (`str`): Subject line, at most 998 characters.
- `body['html']` (`str`): HTML body. Links are left out before the code is looked for.
- `body['text']` (`str`): Plain text body, used when there is no `html`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`VerificationCodeResource` with `object` set to `verification_code` and `code`: the code as you would type it, digits only or capital letters and digits without spaces or dashes, or `None` when the reader is not sure.

**Example**

```python
from nextmail import nextmail

result = nextmail.tools.read_verification_code(
    {
        'subject': 'Your verification code',
        'html': '<p>Your code is <b>396-696</b>. It expires in 10 minutes.</p>',
    }
)

if result['code']:
    print(result['code'])
```

**Notes**

- Needs no scope: any key or access token for the workspace may run it.
- Nothing is stored and no model is called, so it is safe to run on every message.
- A body with none of `raw`, `subject`, `html` or `text` is 422 `invalid_parameter`.

Also available in: API [`POST /tools/verification-code`](https://nextmail.sh/docs/api/reference/tools#post-tools-verification-code); TypeScript [`nextmail.tools.readVerificationCode()`](https://nextmail.sh/docs/sdk/reference/tools#readVerificationCode); Ruby [`tools.read_verification_code`](https://nextmail.sh/docs/ruby/reference/tools#readVerificationCode); PHP [`tools->readVerificationCode`](https://nextmail.sh/docs/php/reference/tools#readVerificationCode); Go [`Tools.ReadVerificationCode`](https://nextmail.sh/docs/go/reference/tools#readVerificationCode); Java [`tools().readVerificationCode`](https://nextmail.sh/docs/java/reference/tools#readVerificationCode); C# [`Tools.ReadVerificationCodeAsync`](https://nextmail.sh/docs/csharp/reference/tools#readVerificationCode); CLI [`nextmail tools read-verification-code`](https://nextmail.sh/docs/cli/reference/tools#tools-read-verification-code).

### `tools.estimate_cost()`

Estimate what Nextmail costs for a usage

```python
def estimate_cost(
    *,
    sends: int | None = None,
    domains: int | None = None,
    people: int | None = None,
    ai_actions: int | None = None,
    interval: BillingInterval | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CostEstimateResource
```

Prices every plan for the usage you describe, as the cost calculator on the Nextmail website does: the plan itself, the extra sends and AI actions it would bill, the monthly total, and whether the plan fits at all. A plan does not fit when the usage needs more domains than it includes, more than one person without team access, or more sends than it allows without billing extra. `cheapest` names the cheapest plan that fits.

Amounts are in US cents per month. With `interval` set to `'year'`, the plan price is the monthly equivalent of paying yearly.

**Parameters**

- `sends` (`int`): Emails sent in a month. Defaults to 0.
- `domains` (`int`): Domains you send from. Defaults to 1.
- `people` (`int`): People who use the workspace. Defaults to 1.
- `ai_actions` (`int`): AI actions in a day, such as drafts, rewrites and summaries. Defaults to 0.
- `interval` (`BillingInterval`): How you pay: `'month'` or `'year'`. Defaults to `'month'`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CostEstimateResource` with the `interval`, the `need` that was priced with its defaults filled in, every plan in `plans` with `fits`, `blockers`, `baseCents`, `extraSends`, `sendsCents`, `extraAiActions`, `aiCents`, `extrasCents` and `totalCents`, and `cheapest`, the plan id of the cheapest one that fits or `None`.

**Example**

```python
from nextmail import nextmail

estimate = nextmail.tools.estimate_cost(sends=25000, domains=2, people=4, ai_actions=30)

for plan in estimate['plans']:
    if plan['plan'] == estimate['cheapest']:
        print(plan['name'], plan['totalCents'])
```

**Notes**

- Needs no scope: any key or access token for the workspace may run it.
- It prices the plans as they are sold today and reads nothing in the workspace.

Also available in: API [`GET /tools/cost`](https://nextmail.sh/docs/api/reference/tools#get-tools-cost); TypeScript [`nextmail.tools.estimateCost()`](https://nextmail.sh/docs/sdk/reference/tools#estimateCost); Ruby [`tools.estimate_cost`](https://nextmail.sh/docs/ruby/reference/tools#estimateCost); PHP [`tools->estimateCost`](https://nextmail.sh/docs/php/reference/tools#estimateCost); Go [`Tools.EstimateCost`](https://nextmail.sh/docs/go/reference/tools#estimateCost); Java [`tools().estimateCost`](https://nextmail.sh/docs/java/reference/tools#estimateCost); C# [`Tools.EstimateCostAsync`](https://nextmail.sh/docs/csharp/reference/tools#estimateCost); CLI [`nextmail tools estimate-cost`](https://nextmail.sh/docs/cli/reference/tools#tools-estimate-cost).

### `tools.check_address()`

Check an email address

```python
def check_address(
    email: str,
    *,
    deep: bool | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> AddressCheckResource
```

Says whether an address is worth sending to, the email checker on the Nextmail website: `result` is `'valid'`, `'risky'`, `'invalid'` or `'unknown'`, and `reasons` says why. It checks the syntax, whether the domain takes mail at all, whether it is a throwaway domain or a shared address such as `info@`, and offers a correction in `suggestion` for a likely typo such as `gmial.com`. Run it on a sign-up form before the first message, or over a list before a broadcast.

With the `emails:read` scope, `workspace` also says what this workspace knows: whether it suppressed the address and when it last delivered to it or saw it bounce. It never shows another workspace's history, and without the scope it is `None`.

`deep` also asks whether the mailbox itself exists, in `checks['mailbox']`. A deep check is billed per address through pay as you go and needs the `emails:send` scope. Without it the check is free and `checks['mailbox']` is `None`.

**Parameters**

- `email` (`str`, required): The address to check, such as `'ada@acme.com'`.
- `deep` (`bool`): Also ask whether the mailbox exists. Billed per address through pay as you go. Defaults to `False`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`AddressCheckResource` with `email`, `normalized`, `result`, `reasons`, `suggestion`, `checks`, `workspace` and `checkedAt`.

**Example**

```python
from nextmail import nextmail

check = nextmail.tools.check_address('ada@gmial.com')

if check['result'] != 'valid':
    print(check['reasons'], check['suggestion'])
```

**Notes**

- Needs no scope without `deep`: any key or access token for the workspace may run it.
- An address whose syntax is wrong is not an error: it returns with `result` `'invalid'` and the reason `'invalid_syntax'`.
- With `deep`, a workspace where pay as you go is off, paused or not part of the plan is 409 `deep_check_unavailable`, a check that would pass its monthly limit is 409 `deep_check_limit_reached`, and a mailbox check that did not answer is 503 `deep_check_unavailable`. Nothing is billed for any of them.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client's `max_retries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /tools/address`](https://nextmail.sh/docs/api/reference/tools#get-tools-address); TypeScript [`nextmail.tools.checkAddress()`](https://nextmail.sh/docs/sdk/reference/tools#checkAddress); Ruby [`tools.check_address`](https://nextmail.sh/docs/ruby/reference/tools#checkAddress); PHP [`tools->checkAddress`](https://nextmail.sh/docs/php/reference/tools#checkAddress); Go [`Tools.CheckAddress`](https://nextmail.sh/docs/go/reference/tools#checkAddress); Java [`tools().checkAddress`](https://nextmail.sh/docs/java/reference/tools#checkAddress); C# [`Tools.CheckAddressAsync`](https://nextmail.sh/docs/csharp/reference/tools#checkAddress); CLI [`nextmail tools check-address`](https://nextmail.sh/docs/cli/reference/tools#tools-check-address).

### `tools.check_addresses()`

Check several email addresses

```python
def check_addresses(
    body: AddressesCheck,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> AddressCheckListResource
```

Checks up to 100 addresses in one call, each as `check_address` does, and returns them in the order they were sent. Use it to clean a list before importing it or sending a broadcast to it.

`deep` applies to every address and is billed for each one that reaches the mailbox check, through pay as you go, and needs the `emails:send` scope.

**Parameters**

- `body['emails']` (`list[str]`, required): The addresses to check, a list of 1 to 100 strings.
- `body['deep']` (`bool`): Also ask whether each mailbox exists. Billed per address through pay as you go. Defaults to `False`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`AddressCheckListResource`, a dict with one `AddressCheckResource` per address in `data`, in the order they were sent.

**Example**

```python
from nextmail import nextmail

checks = nextmail.tools.check_addresses({'emails': ['ada@acme.com', 'info@example.org']})

safe = [check['email'] for check in checks['data'] if check['result'] == 'valid']

print(safe)
```

**Notes**

- Needs no scope without `deep`: any key or access token for the workspace may run it.
- More than 100 addresses, or none, is 422 `invalid_parameter` on `emails`.
- It changes nothing, so it is safe to run again, but the SDK does not retry a POST.

Also available in: API [`POST /tools/addresses`](https://nextmail.sh/docs/api/reference/tools#post-tools-addresses); TypeScript [`nextmail.tools.checkAddresses()`](https://nextmail.sh/docs/sdk/reference/tools#checkAddresses); Ruby [`tools.check_addresses`](https://nextmail.sh/docs/ruby/reference/tools#checkAddresses); PHP [`tools->checkAddresses`](https://nextmail.sh/docs/php/reference/tools#checkAddresses); Go [`Tools.CheckAddresses`](https://nextmail.sh/docs/go/reference/tools#checkAddresses); Java [`tools().checkAddresses`](https://nextmail.sh/docs/java/reference/tools#checkAddresses); C# [`Tools.CheckAddressesAsync`](https://nextmail.sh/docs/csharp/reference/tools#checkAddresses); CLI [`nextmail tools check-addresses`](https://nextmail.sh/docs/cli/reference/tools#tools-check-addresses).
