API docs

Reference · Find emails

Find emails for people or businesses

POST /api/v1/find-contacts

Finds and checks emails. findEmail: a named person at a business (first name, last name and website). companyInbox: the business email (info@, office@ …). roleContact: the owner, only when the business names the owner on its own website.

Send up to 50,000 rows (or a listId). The job runs in the background: poll GET /api/v1/jobs/{id}, set webhookUrl, or send wait (up to 30 seconds) to get small jobs back finished.

Only emails that Work are delivered; the others come back with a plain reason and cost nothing.

Credits: 1 credit per email that Works (you only pay for emails that work (everything else is free)). Not found, not sure or "takes any name" results are free. Rows your account already got an answer for in the last 30 days are answered again for free (cached30d).

Body (JSON)

NameTypeRequiredWhat it is
listIdintegerRun on a list you already have (from POST /api/v1/lists or a search) instead of rows.
contactIdsarray of integerWith listId: only these rows (default: all rows of the list). up to 50,000
namestringName of the new list (shown in the app). Default: "API ()".
action"findEmail" · "companyInbox" · "roleContact"findEmail: a named person (first + last name + website). companyInbox: the business email (info@, office@ …). roleContact: the owner, only when the business names them on its own website. default "findEmail"
rowsarray of objectsUp to 50,000 rows. up to 50,000
rows[].companystringBusiness name.
rows[].websitestringWebsite (any form: https://acme.com, acme.com/contact, www.acme.com).
rows[].domainstringDomain, if you have it instead of a website.
rows[].firstNamestring
rows[].lastNamestring
rows[].fullNamestringUsed when first and last name are not given separately.
rows[].jobTitlestring
rows[].emailstringThe email to check (verify).
rows[].phonestring
rows[].citystring
rows[].statestring
rows[].countrystringTwo-letter country code, e.g. US, CA, GB.
rows[].addressstringBusiness address. For US businesses it decides which state rules apply.
rows[].categorystring
webhookUrlstringPublic https URL we POST the result to when the job is done (see the webhooks guide).
webhookSecretstringOptional: sign this webhook with your own secret instead of the account signing secret.
waitintegerSeconds to wait for the job to finish before answering (0–30). Handy for Zapier, Make and Clay: small jobs come back done, with results. default 0, 0–30

Example request

Shell
curl -X POST "https://moleman.io/api/v1/find-contacts" \
  -H "Authorization: Bearer $MOLEMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"rows":[{"firstName":"Sarah","lastName":"Miller","website":"https://brightsmiledental.com"}],"wait":20}'

Example answer

JSON
{
  "jobId": 981,
  "listId": 377,
  "action": "findEmail",
  "status": "done",
  "livemode": true,
  "total": 1,
  "done": 1,
  "failed": 0,
  "creditsCharged": 1,
  "creditsHeld": 0,
  "message": null,
  "createdAt": 1762344000000,
  "finishedAt": 1762344014000,
  "results": [
    {
      "id": 4102,
      "company": "Bright Smile Dental",
      "domain": "brightsmiledental.com",
      "firstName": "Sarah",
      "lastName": "Miller",
      "fullName": "Sarah Miller",
      "jobTitle": null,
      "email": "sarah.miller@brightsmiledental.com",
      "result": "Works",
      "resultCode": "verified",
      "resultMeaning": "We checked it. This inbox is real, so you can send to it.",
      "emailStatus": "valid",
      "emailStatusText": "Works",
      "emailScore": 96,
      "resolveStatus": null,
      "resolveStatusText": null,
      "findStatus": "found",
      "findStatusText": "Found and checked",
      "emailType": null,
      "emailTypeText": null,
      "briefText": null,
      "briefLine": null,
      "briefRelevance": null,
      "extra": {}
    }
  ],
  "nextCursor": null,
  "queued": 1,
  "skipped": [],
  "cached30d": 0,
  "maxCreditsHeld": 1,
  "pollUrl": "/api/v1/jobs/981"
}

Errors

StatusCodeWhen
401missing_api_keyThe request has no API key.
401invalid_api_keyThe API key is not one of ours (a typo, a cut-off copy, or a key from another account that was deleted).
401revoked_api_keyThe API key was revoked on the API page. Revoking takes effect at once.
429rate_limitedMore than 120 requests in one minute with this key.
500server_errorSomething broke on our side. Nothing was charged for it.
400invalid_requestA parameter is missing, has the wrong type or is out of range. details lists every problem with its path.
400invalid_jsonThe body is not valid JSON.
409idempotency_in_progressA request with the same Idempotency-Key is still running.
422idempotency_key_reusedThe Idempotency-Key was already used in the last 24 hours with a different request.
400no_eligible_rowsNone of the rows can run this action (for example: no website or domain to find an email for, or no email to check).
402insufficient_creditsThe account has fewer credits than this request could use at most. We hold the maximum while the job runs and give back what it does not use.
404not_foundThe job or list does not exist, belongs to another account, or was deleted.
400invalid_webhook_urlThe webhook URL is not a public https address (private networks and other schemes are refused).
403wrong_key_modeA test key asked for something made with a live key, or the other way round. Test and live data never mix.