REST API
JSON over HTTPS. One bearer key. The same routes the desktop app uses, so nothing here is second class.
https://api.nvoke.run/v1Authorization: Bearer nvk_sk_...Authentication
Every route except the public ones wants Authorization: Bearer with an API key. Make keys in the desktop app under Account, or with POST /v1/api-keys once you hold one. An agent without an account gets its first key from agent sign-up.
OAuth access tokens from nvoke’s sign-in work too. That is how MCP clients connect, see MCP.
Errors
Failures answer with a 4xx or 5xx status and { "error": "code" }. Validation failures add details. Branch on the code, not on the message.
- claim_required
- An agent created this account more than 7 days ago and nobody claimed it. The human claims it from the emailed link.
- invalid_request
- The body or query failed validation.
detailslists the problems. - not_found
- No such search, key or webhook on this account.
- quota_exceeded
- This month’s verified emails are used up. A human upgrades at
billingUrlfromGET /v1/me. - not_cancelable
- The search already finished.
- api_key_limit
- The account holds 20 live keys. Revoke one first.
- account_exists
- An account with this email exists. Ask the human for an API key.
- email_not_accepted
- Disposable inbox, an opted-out address, or a domain that receives no mail.
- rate_limited
- Too many sign-ups. Try again later.
- too_many_webhooks
- The account holds 10 webhooks. Delete one first.
- webhook_url_not_allowed
- Webhook URLs must be HTTPS on a public address that resolves.
Account
Who you are, your plan and this month’s usage
/v1/meAPI keyReturns the account behind the token: profile, plan, verified emails used this month and the allowance. ?sync=1 re-reads the plan from billing first. billingUrl is where a human upgrades.
curl "https://api.nvoke.run/v1/me" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| user | object | |
| user.id | string | |
| user.email | string | null | |
| user.name | string | null | |
| user.imageUrl | string | null | |
| via | "device" | "clerk" | "api_key" | "oauth" | |
| plan | "free" | "emails_1000" | "emails_2500" | "emails_7500" | "emails_20000" | "emails_50000" | "emails_100000" | |
| usage | object | |
| usage.period | string | |
| usage.emailsVerified | integer | |
| usage.emailsLimit | integer | |
| maxSearchLimit | integer | |
| account | object | |
| account.createdBy | "human" | "agent" | |
| account.agentName | string | null | |
| account.claimedAt | datetime | null | |
| billingUrl | string |
Errors:unauthorized
The plan ladder
/v1/plansPublicEvery plan with its monthly allowance of verified emails and its price in USD. One product, every feature on every plan.
curl "https://api.nvoke.run/v1/plans"Show the 200 responseHide the response
200 response
| plans | object[] |
Searches
Start a search
/v1/searchesAPI keyQueues a search and returns it at once with status queued. Our servers find the companies, read their websites and check every address. Poll GET /v1/searches/{id} until the status is done, failed or canceled. limit caps the companies looked at (default 100, at most 1,000).
Body
kind: "local"
| kind | "local" | |
| params | object | |
| params.niche | string | up to 100 characters |
| params.location | string | up to 200 characters |
| limit? | integer | 1 to 1000 |
kind: "web"
| kind | "web" | |
| params | object | |
| params.keyword | string | up to 200 characters |
| params.country? | string | |
| limit? | integer | 1 to 1000 |
kind: "launches"
| kind | "launches" | |
| params | object | |
| params.keyword? | string | up to 100 characters |
| params.days? | integer | 1 to 30. default 7 |
| params.sources? | ("product_hunt" | "show_hn")[] | |
| limit? | integer | 1 to 1000 |
curl -X POST "https://api.nvoke.run/v1/searches" \
-H "Authorization: Bearer $NVOKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"kind":"local","params":{"niche":"dentists","location":"Aarhus"},"limit":50}'Show the 201 responseHide the response
201 response
kind: "local"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "local" | |
| params | object | |
| params.niche | string | up to 100 characters |
| params.location | string | up to 200 characters |
kind: "web"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "web" | |
| params | object | |
| params.keyword | string | up to 200 characters |
| params.country? | any |
kind: "launches"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "launches" | |
| params | object | |
| params.keyword? | string | up to 100 characters |
| params.days | integer | 1 to 30. default 7 |
| params.sources? | ("product_hunt" | "show_hn")[] |
kind: "drop"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "drop" | |
| params | object | |
| params.dropId | uuid | |
| params.dropName | string | |
| params.keyword? | string | |
| params.sources | ("show_hn" | "launch_hn" | "product_hunt" | "yc")[] |
Errors:unauthorizedinvalid_requestquota_exceededsource_unavailable
List your searches
/v1/searchesAPI keyAll your searches, newest first, with their counters.
curl "https://api.nvoke.run/v1/searches" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| searches | object[] |
Errors:unauthorized
Get a search
/v1/searches/:idAPI keyOne search with its status and counters: companies found and crawled, emails found and emails verified valid. error says why a search failed, for example quota_exceeded.
curl "https://api.nvoke.run/v1/searches/$SEARCH_ID" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
kind: "local"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "local" | |
| params | object | |
| params.niche | string | up to 100 characters |
| params.location | string | up to 200 characters |
kind: "web"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "web" | |
| params | object | |
| params.keyword | string | up to 200 characters |
| params.country? | any |
kind: "launches"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "launches" | |
| params | object | |
| params.keyword? | string | up to 100 characters |
| params.days | integer | 1 to 30. default 7 |
| params.sources? | ("product_hunt" | "show_hn")[] |
kind: "drop"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "drop" | |
| params | object | |
| params.dropId | uuid | |
| params.dropName | string | |
| params.keyword? | string | |
| params.sources | ("show_hn" | "launch_hn" | "product_hunt" | "yc")[] |
Errors:unauthorizednot_found
Cancel a running search
/v1/searches/:id/cancelAPI keyStops a queued or running search. What it found so far stays.
curl -X POST "https://api.nvoke.run/v1/searches/$SEARCH_ID/cancel" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
kind: "local"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "local" | |
| params | object | |
| params.niche | string | up to 100 characters |
| params.location | string | up to 200 characters |
kind: "web"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "web" | |
| params | object | |
| params.keyword | string | up to 200 characters |
| params.country? | any |
kind: "launches"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "launches" | |
| params | object | |
| params.keyword? | string | up to 100 characters |
| params.days | integer | 1 to 30. default 7 |
| params.sources? | ("product_hunt" | "show_hn")[] |
kind: "drop"
| id | uuid | |
| limit | integer | |
| status | "queued" | "running" | "done" | "failed" | "canceled" | |
| error | string | null | |
| companiesFound | integer | |
| companiesCrawled | integer | |
| emailsFound | integer | |
| emailsValid | integer | |
| createdAt | datetime | |
| startedAt | datetime | null | |
| finishedAt | datetime | null | |
| kind | "drop" | |
| params | object | |
| params.dropId | uuid | |
| params.dropName | string | |
| params.keyword? | string | |
| params.sources | ("show_hn" | "launch_hn" | "product_hunt" | "yc")[] |
Delete a search
/v1/searches/:idAPI keyDeletes the search with its companies and emails. Cannot be undone.
curl -X DELETE "https://api.nvoke.run/v1/searches/$SEARCH_ID" \
-H "Authorization: Bearer $NVOKE_API_KEY"Returns 204 with no body.
Errors:unauthorizednot_found
Leads
List companies with their emails
/v1/searches/:id/companiesAPI keyThe companies a search found, each with its checked email addresses, 50 to a page. Filter by verification (valid is what you usually want), by kind (generic like info@, or personal) and by q (name, domain or address). Pass nextCursor back as cursor for the next page. Addresses appear once their check has run.
Query
| verification? | "valid" | "invalid" | "risky" | "catch_all" | "unknown" | |
| kind? | "generic" | "personal" | |
| q? | string | up to 200 characters |
| cursor? | string | up to 200 characters |
| limit? | integer | 1 to 200. default 50 |
curl "https://api.nvoke.run/v1/searches/$SEARCH_ID/companies?verification=valid&limit=50" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| companies | object[] | |
| nextCursor | string | null | |
| total | integer |
Export as CSV
/v1/searches/:id/export.csvAPI keyOne row per email: company, domain, website, email, kind, verification, source_url, location, phone. Takes the same filters as the company list.
Query
| verification? | "valid" | "invalid" | "risky" | "catch_all" | "unknown" | |
| kind? | "generic" | "personal" | |
| q? | string | up to 200 characters |
curl "https://api.nvoke.run/v1/searches/$SEARCH_ID/export.csv?verification=valid" \
-H "Authorization: Bearer $NVOKE_API_KEY"Returns 200 with text/csv.
Webhooks
List webhooks
/v1/webhooksAPI keyYour webhooks with the outcome of their last deliveries. A webhook we turned off after a day of failures has enabled: false and disabledReason failing.
curl "https://api.nvoke.run/v1/webhooks" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| webhooks | object[] |
Errors:unauthorized
Create a webhook
/v1/webhooksAPI keyWe POST lead.ready for every company with a valid address once its checks settle, and search.finished when a search ends. scope drops or a dropId limits it to searches drops made. Requests are signed the Standard Webhooks way (webhook-id, webhook-timestamp, webhook-signature); the response holds the signing secret, and it is never shown again. HTTPS on a public address only. At most 10 webhooks.
Body
| url | string | Where to POST events. HTTPS on a public address.. up to 2000 characters |
| events? | ("lead.ready" | "search.finished")[] | The events to send. |
| scope? | "all" | "drops" | `all`: every search. `drops`: only searches a drop made. |
| dropId? | uuid | Only searches this drop made. Implies scope `drops`. |
curl -X POST "https://api.nvoke.run/v1/webhooks" \
-H "Authorization: Bearer $NVOKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/nvoke","events":["lead.ready"],"scope":"drops"}'Show the 201 responseHide the response
201 response
| id | uuid | |
| url | string | |
| events | ("lead.ready" | "search.finished")[] | |
| scope | "all" | "drops" | |
| dropId | uuid | null | |
| enabled | boolean | |
| disabledReason | "failing" | null | |
| lastSuccessAt | datetime | null | |
| lastFailureAt | datetime | null | |
| lastError | string | null | |
| createdAt | datetime | |
| secret | string |
Errors:unauthorizedinvalid_requesttoo_many_webhookswebhook_url_not_allowed
Change a webhook
/v1/webhooks/:idAPI keyChange the URL, events or scope, or pause and resume it with enabled. dropId: null widens it to the whole scope again.
Body
| url? | string | Where to POST events. HTTPS on a public address.. up to 2000 characters |
| events? | ("lead.ready" | "search.finished")[] | The events to send. |
| scope? | "all" | "drops" | `all`: every search. `drops`: only searches a drop made. |
| dropId? | uuid | null | |
| enabled? | boolean |
curl -X PATCH "https://api.nvoke.run/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $NVOKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'Show the 200 responseHide the response
200 response
| id | uuid | |
| url | string | |
| events | ("lead.ready" | "search.finished")[] | |
| scope | "all" | "drops" | |
| dropId | uuid | null | |
| enabled | boolean | |
| disabledReason | "failing" | null | |
| lastSuccessAt | datetime | null | |
| lastFailureAt | datetime | null | |
| lastError | string | null | |
| createdAt | datetime |
Errors:unauthorizednot_foundinvalid_requestwebhook_url_not_allowed
Delete a webhook
/v1/webhooks/:idAPI keyStops it at once and drops its pending deliveries.
curl -X DELETE "https://api.nvoke.run/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $NVOKE_API_KEY"Returns 204 with no body.
Errors:unauthorizednot_found
Send a test event
/v1/webhooks/:id/testAPI keySends a signed ping event right now and tells you how your server answered.
curl -X POST "https://api.nvoke.run/v1/webhooks/$WEBHOOK_ID/test" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| ok | boolean | |
| status | integer | null | |
| error | string | null | |
| durationMs | integer |
Recent deliveries
/v1/webhooks/:id/deliveriesAPI keyThe last 50 deliveries with their status, attempts and the last answer from your server. Kept 30 days.
curl "https://api.nvoke.run/v1/webhooks/$WEBHOOK_ID/deliveries" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| deliveries | object[] |
Errors:unauthorizednot_found
API keys
List API keys
/v1/api-keysAPI keyYour live API keys. Only the prefix of each key is shown.
curl "https://api.nvoke.run/v1/api-keys" \
-H "Authorization: Bearer $NVOKE_API_KEY"Show the 200 responseHide the response
200 response
| apiKeys | object[] |
Errors:unauthorized
Create an API key
/v1/api-keysAPI keyMakes a new key. The response holds the full key, and it is never shown again. At most 20 live keys.
Body
| name | string | up to 60 characters |
curl -X POST "https://api.nvoke.run/v1/api-keys" \
-H "Authorization: Bearer $NVOKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"CRM sync"}'Show the 201 responseHide the response
201 response
| id | uuid | |
| name | string | |
| prefix | string | |
| origin | "user" | "agent_signup" | |
| createdAt | datetime | |
| lastUsedAt | datetime | null | |
| key | string |
Revoke an API key
/v1/api-keys/:idAPI keyThe key stops working at once. A key may revoke itself.
curl -X DELETE "https://api.nvoke.run/v1/api-keys/$KEY_ID" \
-H "Authorization: Bearer $NVOKE_API_KEY"Returns 204 with no body.
Errors:unauthorizednot_found
Agents
Sign up as an agent
/v1/agents/signupPublicCreates an nvoke account for the person you work for and returns an API key at once. No browser, no human needed. The free plan applies: 50 verified emails a month. We send that person one email with a link to claim the account. Unclaimed accounts work for 7 days, then answer 403 claim_required until claimed, and are deleted after 30 days. Rate limited per IP, per email domain and per day.
Body
| up to 320 characters | ||
| agentName? | string | up to 60 characters |
curl -X POST "https://api.nvoke.run/v1/agents/signup" \
-H "Content-Type: application/json" \
-d '{"email":"ada@example.com","agentName":"Claude"}'Show the 201 responseHide the response
201 response
| apiKey | string | |
| account | object | |
| account.email | string | |
| account.status | "unclaimed" | |
| account.claimEmailSent | boolean | |
| account.activeUntil | datetime | |
| apiBase | string | |
| mcpUrl | string | |
| docsUrl | string | |
| nextSteps | string[] |
Errors:invalid_requestaccount_existsemail_not_acceptedrate_limitedsignup_unavailable
Privacy
Opt an address or domain out
/v1/privacy/opt-outPublicStops collection of an address or a whole domain and deletes what we hold. Always answers { ok: true }.
Body
| up to 320 characters |
| domain | string | up to 253 characters |
curl -X POST "https://api.nvoke.run/v1/privacy/opt-out"Show the 200 responseHide the response
200 response
| ok | true |
Errors:invalid_request
Meta
This API as OpenAPI 3.1
/v1/openapi.jsonPublicMachine readable description of every endpoint above.
curl "https://api.nvoke.run/v1/openapi.json"Show the 200 responseHide the response
200 response
| openapi | string |
Health check
/healthPublicAnswers 200 when the API is up.
curl "https://api.nvoke.run/health"Show the 200 responseHide the response
200 response
| status | "ok" | |
| service | string | |
| version | string | |
| uptimeSeconds | number |