Notideus
Tutorials

Sending emails with Python

The same walkthrough in Python with httpx.

There is no official Notideus SDK for Python — the API is plain JSON over HTTPS. This walkthrough uses httpx (pip install httpx); the same shape works with requests.

The send function

raise_for_status catches HTTP errors; the wrapper lifts the API's machine-readable code out of the error envelope so callers can branch on it.

import os

import httpx

API_KEY = os.environ["NT_KEY"]  # "nt_live_…"

class ApiError(Exception):
    """HTTP status plus the API's machine-readable error code."""

    def __init__(self, status: int, code: str, message: str):
        self.status = status
        self.code = code
        super().__init__(f"{status} {code}: {message}")

def send(payload: dict) -> dict:
    with httpx.Client(
        base_url="https://api.notideus.io/v1",
        headers={"Authorization": f"Bearer {API_KEY}"},
    ) as client:
        r = client.post("/emails", json=payload)
    try:
        r.raise_for_status()
    except httpx.HTTPStatusError as exc:
        error = exc.response.json().get("error", {})
        raise ApiError(
            exc.response.status_code, error.get("code"), error.get("message")
        ) from exc
    return r.json()

email = send({
    "from": "Notideus <noreply@demo.dev>",
    "to": ["jane@example.com"],
    "subject": "Welcome to Notideus",
    "html": "<p>Hi Jane,</p><p>Your account is ready.</p>",
})
print(email["id"], email["status"])

Expected output

send returns the stored email row (201 Created). Response abbreviated to id, status, from, to and subject:

{
  "id": "01a0a11c-188e-7c9e-9b69-753fcb4c8d0d",
  "status": "queued",
  "from_address": "Notideus <noreply@demo.dev>",
  "to_emails": ["jane@example.com"],
  "subject": "Welcome to Notideus"
}

id uniquely identifies this email; status is queued — it is picked up for delivery from there.

Error handling

Catch ApiError and branch on exc.code:

  • unauthorized (401) — missing, invalid or revoked key; check NT_KEY.
  • insufficient_scope (403) — the key lacks emails:send; create a key with the right scope.
  • quota_exceeded (402) — monthly allowance reached; back off and retry later. See Quotas and limits.
  • from_domain_not_verified (422) — the From domain matches no verified team domain; verify it in the dashboard first.
Retry safely: send every request with an idempotency_key derived from your own data (for example order-12345-welcome). A timed-out request can be re-sent with the same key without double-sending or double-charging your quota. See Idempotency for key choice and scope rules.
Copyright © 2026