Mozart EmailMMozart Email

API.

Two features, one API. Single search for live one-off lookups, Bulk search for asynchronous batches up to 5,000 rows. JSON in, JSON out — except for bulk uploads (multipart) and downloads (CSV).

Generate keys in Settings → API. Each key is shown once — store it like a password. You can disable or rotate keys at any time.

Base URL
https://mozartemail.com

Authentication

Every request must carry an Authorization header:

Authorization: Bearer mz_live_<your_key>

Keys are scoped to a single account and counted against that account's credit balance. There is no separate API quota — what you see in your dashboard is what you can spend. Keys can be disabled or deleted at any time; revoked keys return 401 unauthorized.

POST

/api/find-email

Find a verified email from first name, last name, and a company domain or name. The call is synchronous — responses arrive in 1–3 seconds for ~95% of requests; the SMTP-confirmation step can extend that to 10–15 seconds for harder targets. One credit is charged only on a verified result; not-found requests are free.

Request body

FieldTypeRequiredNotes
firstNamestringyesUp to 80 chars.
lastNamestringyesUp to 80 chars.
domainOrCompanystringyesEither a domain (acme.com) or a company name (Acme Inc). Domains yield the most precise results.

Example — curl

curl -X POST https://mozartemail.com/api/find-email \
  -H "Authorization: Bearer mz_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Clara",
    "lastName": "Weber",
    "domainOrCompany": "deutschebank.com"
  }'

Response — found

{
  "ok": true,
  "status": "found",
  "email": "clara.weber@deutschebank.com",
  "domain": "deutschebank.com",
  "confidence": 95,
  "provider": "Microsoft 365",
  "rawStatus": "FOUND",
  "elapsed": 1.4
}

Response — not found

{
  "ok": false,
  "status": "not_found",
  "email": null,
  "domain": "deutschebank.com",
  "confidence": 0,
  "provider": null,
  "rawStatus": "NOT_FOUND",
  "elapsed": 2.1
}

Example — Python

import requests

r = requests.post(
    "https://mozartemail.com/api/find-email",
    headers={"Authorization": "Bearer mz_live_xxx"},
    json={
        "firstName": "Clara",
        "lastName": "Weber",
        "domainOrCompany": "deutschebank.com",
    },
    timeout=30,
)
data = r.json()
if data["ok"]:
    print(data["email"], "·", data["confidence"])

Example — Node

const r = await fetch("https://mozartemail.com/api/find-email", {
  method: "POST",
  headers: {
    "Authorization": "Bearer mz_live_xxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    firstName: "Clara",
    lastName: "Weber",
    domainOrCompany: "deutschebank.com",
  }),
});
const data = await r.json();
if (data.ok) console.log(data.email, "·", data.confidence);

Bulk search

Upload a CSV or .xlsx, poll for status, download a results CSV. Jobs run on our worker and survive network drops on your side — you can disconnect after uploading and come back later for the results.

Lifecycle

  1. Upload a CSV or .xlsx with first name, last name, and domain-or-company columns. We parse it, hold one credit per row, and return a jobId.
  2. Status moves queuedprocessingdone (or error / cancelled). Poll every 5–10 seconds.
  3. Download the enriched CSV when status is done.

Pricing

One credit per row, only charged when an email is found. We pre-hold the full row count when you upload, then refund unused credits when the job finishes. If your balance can't cover the batch, the upload is rejected with insufficient_credits — nothing is deducted.

Limits

Max rows per job5,000
Max file size3 MB
Concurrent jobs per account3
Result retentionIndefinite (delete with DELETE /api/bulk/{id})

File format

UTF-8 CSV or .xlsx (first worksheet) with a header row. Column names are case-insensitive — we accept common variants:

Required columnAccepted headers (case-insensitive)
First namefirst_name, firstname, first name, prenom
Last namelast_name, lastname, last name, nom
Targetdomain, company, company_name, website, societe

Blank rows are skipped silently. Extra columns are ignored.

POST

/api/bulk/upload

Upload a CSV or .xlsx file as multipart/form-data with a single field named file. The endpoint validates the file, holds the credits, and returns a job ID immediately. Processing happens asynchronously.

Example — curl

curl -X POST https://mozartemail.com/api/bulk/upload \
  -H "Authorization: Bearer mz_live_xxx" \
  -F "file=@./prospects.csv"

# .xlsx works the same way
curl -X POST https://mozartemail.com/api/bulk/upload \
  -H "Authorization: Bearer mz_live_xxx" \
  -F "file=@./prospects.xlsx"

Response — accepted

{
  "ok": true,
  "jobId": "b18e2c80-3f7d-4a1d-9d1e-c4ad3e2c5b71",
  "total": 1248
}

Common rejections

HTTPreasonMeaning
400no_fileMultipart had no file field.
400file_too_largeLarger than 3 MB.
400empty_csvFewer than 2 rows (no data).
400invalid_fileCouldn't parse the file (corrupt .xlsx, malformed CSV, etc.).
400missing_columnsHeader didn't expose first-name, last-name, and a target column.
400no_valid_rowsAll rows blank after filtering.
400too_many_rowsOver the 5,000-row limit. Response includes got and limit.
402insufficient_creditsBalance can't cover the full batch. Response includes balance and required.
429too_many_active_jobsAlready 3 jobs in queued or processing. Wait for one to finish.
GET

/api/bulk/{id}

Returns the job header — status, progress counters, and credit accounting. Poll every 5–10 seconds while queued or processing.

Example — curl

curl https://mozartemail.com/api/bulk/b18e2c80-3f7d-4a1d-9d1e-c4ad3e2c5b71 \
  -H "Authorization: Bearer mz_live_xxx"

Response

{
  "ok": true,
  "job": {
    "id": "b18e2c80-3f7d-4a1d-9d1e-c4ad3e2c5b71",
    "filename": "prospects.csv",
    "status": "processing",
    "total": 1248,
    "processed": 412,
    "found": 358,
    "notFound": 54,
    "creditsHeld": 1248,
    "creditsCharged": 358,
    "errorMessage": null,
    "createdAt": "2026-05-11T17:24:08.221Z",
    "startedAt": "2026-05-11T17:24:09.804Z",
    "finishedAt": null
  }
}

Status values

queuedAccepted, waiting for the worker.
processingWorker is running rows through the verifier.
doneAll rows processed. Results ready to download.
errorJob failed before finishing. errorMessage is non-null. Held credits are refunded automatically.
cancelledJob was cancelled (delete or admin action). Unused credits refunded.
GET

/api/bulk/list

List the most-recent 50 jobs for the authed account, newest first.

Response

{
  "ok": true,
  "jobs": [
    {
      "id": "b18e2c80-...",
      "filename": "prospects.csv",
      "status": "done",
      "total": 1248,
      "processed": 1248,
      "found": 1041,
      "notFound": 207,
      "creditsHeld": 0,
      "creditsCharged": 1041,
      "createdAt": "2026-05-11T17:24:08.221Z",
      "finishedAt": "2026-05-11T17:31:52.118Z"
    }
  ]
}
GET

/api/bulk/{id}/download

Returns the enriched results as a CSV download. Available for any job — partial results stream as soon as rows are processed. Response is text/csv; charset=utf-8.

Output columns

first_nameEcho of the input.
last_nameEcho of the input.
domain_or_companyEcho of the input.
emailFound email, or blank.
statusOne of verified, catch-all, risky, invalid, not_found.
confidence0–100 (see scale below).
providerDetected mail provider, e.g. Microsoft 365, Google Workspace.

Example — curl

curl https://mozartemail.com/api/bulk/b18e2c80-.../download \
  -H "Authorization: Bearer mz_live_xxx" \
  -o results.csv
DELETE

/api/bulk/{id}

Remove a job and its rows. Permanent — there is no undo. Use this on jobs you no longer need; finished results are already in any CSV you downloaded.

curl -X DELETE https://mozartemail.com/api/bulk/b18e2c80-... \
  -H "Authorization: Bearer mz_live_xxx"

Responses: { "ok": true } on success, 404 not_found if the ID doesn't belong to your account.

Statuses & confidence

Both Single and Bulk searches return the same set of result statuses. Use status for branching logic and confidence for risk-based thresholds.

Result statuses

verifiedSMTP-confirmed deliverable. Safe to send.
catch-allDomain accepts all mail — pattern likely correct but can't be SMTP-proven.
riskyPartial signals. Send at lower volume or warm carefully.
invalidSMTP refused; do not send.
not_foundNo plausible pattern. No credit charged.

Confidence scale

ScoreMeaning
99SMTP-confirmed deliverable.
95Pattern + MX confirmed.
85Pattern match, catch-all domain.
75Probable, partial signal.
60Risky — verify manually.
0Not found.

Errors

All non-2xx responses include { "ok": false, "reason": "<code>" }. Common codes across both features:

HTTPreasonWhen
400invalid_jsonBody wasn't valid JSON (find-email only).
400missing_fieldsRequired fields absent.
400invalid_formMultipart body could not be parsed (bulk upload).
401unauthorized / not_authenticatedMissing or revoked API key.
402insufficient_creditsTop up to continue. Bulk includes required; single returns balance: 0.
404not_foundUnknown job ID, or job belongs to another account.
429rate_limitedHit one of the /api/find-email rate gates (see Rate limits below). Retry-After header indicates back-off seconds.
429too_many_active_jobsAlready 3 bulk jobs queued or processing.
500server_exceptionInternal error. Contact support if persistent.
503upstream_rate_limitedUpstream verifier rate-limited us. Back off using the Retry-After header (5s by default).
503icypeas_timeoutUpstream verifier didn't respond in 30s; safe to retry.

Rate limits

Single search40 requests / min per account, 3 requests / sec per account (burst), and 40 requests / min across all accounts (shared upstream quota). Any of the three returns 429 rate_limited with a Retry-After header — back off on the indicated seconds and retry.
Bulk uploads3 concurrent jobs per account. Queue more by waiting for one to finish.
Status / list / downloadUnlimited. Poll every 5–10 seconds for status; longer intervals are kinder to our cache.

Need something else? Back home · Manage keys · concierge@mozartemail.com