AI Agents (API Context)
For humans: This page contains the complete AI Ark API documentation in a single, self-contained document. Copy everything (👉) and paste it into your AI agent (Claude, ChatGPT, Cursor, Clay AI, n8n, Make, etc.) as context or a system prompt. Your agent will then know every endpoint, every credit cost, every filter's exact JSON shape, and the correct workflow to build outbound lists.
Alternatives for agents that can fetch URLs or use tools:
- 📄 Machine-readable index:
https://docs.ai-ark.com/llms.txt(append.mdto any docs page URL for its markdown version)- 🔌 MCP server (recommended for Claude Desktop, Cursor, Windsurf):
https://api.ai-ark.com/v1/mcp?token={YOUR-API-KEY}
AI Ark API — FULL CONTEXT FOR AI AGENTS
You are working with the AI Ark API, a B2B contact & company database (400M+ person profiles, 70M+ company profiles) used to build outbound lead lists with verified emails and mobile numbers.
1. Basics
- Base URL:
https://api.ai-ark.com/api/developer-portal - Authentication: every request needs the header
X-TOKEN: {API_KEY}. Missing/invalid token →401from the gateway. - Content type: every request needs the header
Content-Type: application/json. - All search/enrichment endpoints are
POSTwith a JSON body. Status/result/history endpoints areGET; webhook re-sends arePATCH. - Rate limits: 5 requests/second per token (default). Customers using >450,000 credits/month can request custom limits via [email protected] or the in-app chat.
- Check balance any time (free):
GET /v1/payments/credits→{ "total": 100 }
2. Credit Pricing (memorize this before calling anything)
| Endpoint | Method & Path | Credits | Charged when |
|---|---|---|---|
| People Preview | POST /v1/people/preview | 1 per page (flat) | Same filters as People Search. 1 credit whether the page has 25 or 100 results. Masked last names, no emails/phones, has_* availability flags. |
| People Search | POST /v1/people | 0.5 per result | Per person returned. Full person + company data, no email, no phone. |
| Company Search (incl. Lookalike) | POST /v1/companies | 0.1 per result | Per company returned (lookalike included). |
| Find Emails by Track ID | POST /v1/people/email-finder | 1 per found valid email (0.5 people data + 0.5 email), 0 if not found | Only people with a found, BounceBan-verified email are charged — people without an email cost nothing. |
| Export People with Email | POST /v1/people/export | 0.5 per person + 0.5 per found valid email | 0.5 for every exported person (found or not) + 0.5 extra per found, BounceBan-verified email. Note the difference to Find Emails by Track ID. |
| Export Single Person with Email (v1) | POST /v1/people/export/single | 1 if email found, 0 if not | Only charged when a valid, BounceBan-verified email is returned. |
| Export Single Person with Email (v2, Clay) | POST /v2/people/export/single | 1 if email found, 0 if not | Same as v1. X-Credit response header shows the charge. |
| Mobile Phone Finder (v1) | POST /v1/people/mobile-phone-finder | 5 per found phone number, 0 if none found | Charged only when a number is delivered — attempts that find nothing are free (v1 returns 404). |
| Mobile Phone Finder (v2, Clay) | POST /v2/people/mobile-phone-finder | 5 per found phone number, 0 if none found | Same as v1. X-Credit response header shows the charge. |
| Reverse People Lookup | POST /v1/people/reverse-lookup | 0.5 per request | Per request. |
| Personality Analysis | POST /v1/people/analysis | 4 per request | Per request. |
| Create/Update List | POST /v1/lists | Free | Never charged. |
| Fetch Credits | GET /v1/payments/credits | Free | Never charged. |
All /statistics, /inquiries, /submissions, /notify endpoints | GET / PATCH | Free | Polling, fetching results, history, and webhook re-sends never cost credits — you already paid at submission. |
Cost math examples:
- Preview,
size: 100, any number of results → 1 credit (the same 100 results on People Search would cost 50). - People Search,
size: 25, 25 results → 12.5 credits. - Find Emails on those 25 people, 18 valid emails found → 18 × 1 = 18 credits (the 7 without an email cost nothing).
- Export People with Email,
size: 1000, 700 emails found → 1,000 × 0.5 + 700 × 0.5 = 850 credits. - Enriching 1 person via Export Single: 1 credit (or 0 if no email found).
- Company Search,
size: 100, 100 matches → 10 credits.
Automatic refunds (async jobs — Export People, Find Emails): submissions are auto-refunded, no support ticket needed. Check GET .../submissions → fullyRefunded: true + refundReason:
refundReason | Meaning | What to do |
|---|---|---|
SEARCH_NO_RESULTS | The search matched nobody; fully refunded. | Broaden the filters. |
NO_EMAILS_FOUND | People matched but no emails could be found; fully refunded. | Nothing — you paid 0. |
STUCK_HARD_REFUND | Results not delivered within the time limit (≤10h); auto-refunded. | Resubmit the job. Track-ID endpoints return 403 (status code 4031013). |
STUCK_SOFT_REFUND | Email-finding service did not respond; auto-refunded. | Resubmit the job. Track-ID endpoints return 403 (status code 4031014). |
3. Email Verification (applies everywhere emails are returned)
✅ Every email returned by any AI Ark endpoint (SMTP and CATCH_ALL) is verified in real time by BounceBan at the moment of the request. All returned emails are safe to send — no re-verification with another tool is needed. Email charges only ever apply to emails that passed this real-time validation.
Email output object (identical shape everywhere emails appear — Export results, Email Finder results, Export Single, webhooks):
{
"address": "[email protected]", // only present when found: true
"found": true, // whether an email was found
"status": "VALID", // "VALID" | "INVALID"
"subStatus": "EMPTY", // "EMPTY" | "MAILBOX_NOT_FOUND" | "FAILED_SYNTAX_CHECK"
"domainType": "SMTP", // "SMTP" | "CATCH_ALL" | "UNKNOWN" (UNKNOWN when not found)
"date": "2026-03-06T14:41:53.000474", // timestamp of the real-time verification
"free": false, // free-mail provider domain
"generic": false, // role address like info@
"mx": {
"found": true,
"record": "mx1.example.com", // string | null
"google": false,
"provider": "g-suite" // "microsoft" | "g-suite" | "mimecast" | "barracuda" | "proofpoint" | "cisco ironport" | "other" | null
}
}Use generic and domainType to segment your sending strategy. In async Export results, the per-person wrapper is "email": { "state": "PROCESSING" | "DONE", "output": [ ...email objects ] }.
4. Which endpoint do I use? (decision guide)
| Goal | Use this |
|---|---|
| Cheaply check who/how many match an ICP before spending per-result credits | People Preview POST /v1/people/preview (1 credit/page, flat) |
| Build a list of people matching an ICP (title, industry, location, size…) — data only, no emails yet | People Search POST /v1/people |
| Get emails for the people a search just returned | Find Emails by Track ID POST /v1/people/email-finder (uses the trackId from People Search) |
| One-shot: search an ICP and get emails, up to 10,000 people, async | Export People with Email POST /v1/people/export |
| Enrich ONE known person (row-by-row, e.g. from a CRM or Clay table) by AI-Ark ID or LinkedIn URL | Export Single Person POST /v1/people/export/single (or /v2/... inside Clay) |
| Get a mobile phone number for a known person | Mobile Phone Finder POST /v1/people/mobile-phone-finder (or /v2/... inside Clay) |
| I have an email address — who is this? | Reverse People Lookup POST /v1/people/reverse-lookup |
| Build target ACCOUNT lists, find companies similar to given domains, or find companies that employ a given role | Company Search POST /v1/companies (lookalikeDomains, account.employee) |
| Personality profile of a person for message personalization | Personality Analysis POST /v1/people/analysis |
| Exclude already-contacted people/companies from future searches | Create/Update List POST /v1/lists, then reference the list id in the lists filter |
| Check remaining balance | GET /v1/payments/credits |
5. Standard Workflows (recipes)
Recipe 0 — Always preview first (1 credit/page)
POST /v1/people/preview with your ICP filters. Check totalElements and sample quality; refine until right. The body is identical to People Search — when satisfied, send the same body to Recipe A or B.
Preview results include:
- each person's real
id(accepted directly by Export Single Person — cherry-pick and enrich without a full search), - first name + masked last name (
Ba***e), title, headline, location, department/functions/seniority, position history with dates, - the full company object (id, name, description, industry, headcount, revenue, HQ + office locations, keywords, NAICS/SIC),
has_*availability flags (has_mobile,has_skills,has_educations, …) — see what data exists before paying to enrich;has_mobile: true→ a Mobile Phone Finder call (5 credits) has something to find.
Preview returns no emails, no phones, no trackId (trackId: null — email finding chains off People Search only).
Recipe A — Two-step list building (max control; pay only for found emails)
POST /v1/peoplewith your ICP filters (page,size≤ 100). Cost: 0.5 × results. Response contains the people,totalElements, and atrackId.POST /v1/people/email-finderwith thattrackIdand awebhookURL. ⚠️ EachtrackIdworks once and expires 6 hours after the search response — submit promptly.- Poll
GET /v1/people/email-finder/{trackId}/statisticsuntilstate: DONE(or wait for the webhook). Results are readable while running: unfinished items showstate: PROCESSING. - Fetch results:
GET /v1/people/email-finder/{trackId}/inquiries?page=0&size=100.
Recipe B — One-step bulk export (up to 10,000 people)
POST /v1/people/exportwith the same filters as People Search,sizeup to 10000 (heresize= total export size, not page size), plus awebhookURL. Response returns atrackIdandstate: PENDING.- Poll
GET /v1/people/export/{trackId}/statisticsor wait for the webhook (auto-retried; re-trigger any time withPATCH /v1/people/export/{trackId}/notify). - Fetch results:
GET /v1/people/export/{trackId}/inquiries?page=0&size=100— full person + company + verified email. Returns409while still in progress — poll statistics untilstate: DONEfirst.
Recipe C — Row-by-row enrichment (CRM / Clay / spreadsheets)
- For each row, call
POST /v1/people/export/single(v1) orPOST /v2/people/export/single(Clay) with{ "id": "..." }or{ "url": "https://linkedin.com/in/..." }. - Add
POST /v1/people/mobile-phone-finder(or v2) if you also need mobile numbers (5 credits per found phone).
Recipe D — Account-based (ABM)
POST /v1/companiesto build the account list (0.1/result). Optionally seed withlookalikeDomains(max 5 domains/LinkedIn URLs) to find similar companies, and/or useaccount.employeeto find companies that even have the role you sell to.- Take the returned company
idvalues and use them in People Search undercontact.company.latest/.currentto find people at exactly those companies. - Continue with Recipe A or B for emails.
Suppression & deep pagination (Lists)
POST /v1/listswith{ "type": "people_id", "values": [ ...ids ] }→ returns a listid.- Reference it in searches:
"lists": { "people_id": { "exclude": ["<list-id>"] } }(people endpoints) /"lists": { "company_id": { "exclude": ["<list-id>"] } }(Company Search). Max 10 lists per request. - Retrieve more than 10,000 records:
totalElementsshows the full match count, but a single search pages through at most 10,000 records (page×size). For a 38,000-match search: pull the first 10,000 → add theirids to a list → re-run the search excluding that list (28,000 remain) → pull the next 10,000 → append → repeat until done. - Lists are free, reusable and updatable: send the same
idwithmode: "APPEND"(default, merges values) ormode: "REPLACE"(rewrites the list with a fresh set). Max 10,000 values per list, 50 lists per day, lists expire after 24 hours — recreate them daily.
6. Endpoint Reference
6.0 People Preview — POST /v1/people/preview
POST /v1/people/previewPurpose: run any People Search cheaply to validate filters and volume before committing credits. The recommended first call of every list-building workflow.
Credits: 1 per page, flat — whether the page holds 25 or 100 results.
Body: identical to People Search — account, contact, lists, plus required page (zero-based) and size (1–100, default 25). totalElements is the full match count; up to 10,000 records retrievable via pagination, like People Search.
Response: person id (real, accepted by Export Single Person), first name + masked last name (Ba***e), title, headline, location, department/seniority, position history, full company object, has_* availability flags, last_updated. No emails, no phones, trackId: null.
Related: People Search (full details + trackId), Export People with Email (same body, bulk emails), Export Single Person (enrich picked ids).
6.1 People Search — POST /v1/people
POST /v1/peoplePurpose: search 400M+ person profiles by contact and/or account (company) filters. Returns full person + company data. Does NOT return emails or phones — chain with Find Emails by Track ID, Export Single Person, or Mobile Phone Finder.
Credits: 0.5 per result. You only pay for results actually returned.
Body: { "account": {...}, "contact": {...}, "lists": {...}, "page": 0, "size": 25 } — page and size are required (page zero-based, size 1–100, default 10). Full filter reference: Section 7.
Response: content[] (people — see the response skeleton in Section 9), Spring-style pagination (totalElements, totalPages, size, number, pageable, first, last, empty, numberOfElements), and trackId (single-use, 6h expiry — feed it to /v1/people/email-finder).
Errors: 404 data not found (no matches — treat as empty, not failure); 501 unsupported filter (e.g. socialMediaFollower on a platform other than LinkedIn → SOCIAL_MEDIA_FOLLOWER_PLATFORM_NOT_SUPPORTED).
curl -X POST 'https://api.ai-ark.com/api/developer-portal/v1/people' \
-H 'X-TOKEN: {API_KEY}' -H 'Content-Type: application/json' \
-d '{
"contact": {
"experience": { "latest": { "title": { "any": { "include": { "mode": "SMART", "content": ["marketing manager"] } } } } },
"seniority": { "any": { "include": ["manager", "director"] } },
"location": { "any": { "include": ["Germany"] } }
},
"account": {
"industries": { "any": { "include": { "mode": "WORD", "content": ["software development"] } } },
"employeeSize": { "type": "RANGE", "range": [ { "start": 50, "end": 200 } ] }
},
"page": 0, "size": 25
}'6.2 Company Search — POST /v1/companies
POST /v1/companiesPurpose: search 70M+ company profiles by account filters — ideal for ABM account lists. Also Lookalike Search via lookalikeDomains.
Credits: 0.1 per result (lookalike included).
Body: { "lookalikeDomains": [...], "account": {...}, "lists": {...}, "page": 0, "size": 25 } — page/size required, size 1–100.
Lookalike Search: up to 5 company domains or LinkedIn company URLs in lookalikeDomains. Combine with account filters to constrain the lookalikes:
{ "lookalikeDomains": ["raisin.com", "https://www.linkedin.com/company/n26"],
"account": { "location": { "any": { "include": ["Germany"] } } }, "page": 0, "size": 25 }Company-Search-only filter — account.employee (Job Role): find companies by the people they employ. Three combinable sub-fields:
employee.title— job titles with match modes (SMART/WORD/STRICT) andany/all+include/exclude,employee.seniority— plain arrays, same 12 values ascontact.seniority,employee.departmentAndFunction— plain arrays, same 592 values ascontact.departmentAndFunction.
"account": { "employee": {
"title": { "any": { "exclude": { "mode": "SMART", "content": ["manager"] } } },
"seniority": { "any": { "include": ["c_suite", "vp"] } },
"departmentAndFunction": { "any": { "include": ["software_development"] } }
} }→ companies that have C-suite/VP software-development people (typical use: "which accounts even have a Head of Data?"). Not available in People Search (use contact.* there).
Response: companies with id (UUID) — use these IDs in People Search contact.company (latest/current/previous) to target employees of exactly these companies. Resolve any domain or LinkedIn company URL to its ID this way (works for universities too — needed for the education.school filter).
Related: People Search (find people at these companies), Lists (company_id exclusions).
6.3 Find Emails by Track ID — POST /v1/people/email-finder
POST /v1/people/email-finderPurpose: trigger email finding for the result set of a previous People Search, using its trackId. Step 2 of the two-step flow.
Credits: 1 per found valid email (0.5 people data + 0.5 real-time BounceBan-verified email) — 0 for people where no valid email is found. Unlike Export People with Email, you only pay for delivered emails here.
⚠️ Critical: each trackId can be used exactly once and expires 6 hours after the People Search response. A used/expired id returns 404 — run the search again for a fresh one. Plan: search → immediately submit email finding.
Body: { "trackId": "<uuid>", "webhook": "https://..." } — both required. The webhook must be an HTTPS URL; delivery is auto-retried, and you can re-send any time via the notify endpoint.
Response: { trackId, statistics: { total, found }, state: "PENDING", webhook: { state, retry }, description }.
Concurrency (per token, per service): max 500 in-flight submissions (400 too many pending requests — drain below the cap, then retry); 10 jobs processed in parallel. Auto-refund of undelivered charged jobs after up to 10h.
Sub-endpoints (all free):
GET /v1/people/email-finder/{trackId}/statistics— pollstate+statistics(total,found) untilDONE.403if the submission was STUCK_*-refunded (resubmit);404if the trackId is unknown/expired;200with zeroed statistics if refunded forSEARCH_NO_RESULTS/NO_EMAILS_FOUND.GET /v1/people/email-finder/{trackId}/inquiries?page=0&size=100— paginated results (pagezero-based,size1–100, default 10). Readable while running — unfinished items showstate: PROCESSING. Each item:refId,state,input(firstname,lastname,domain),output(array of email objects — Section 3).403STUCK_*-refunded /404unknown; refunded-no-result →200with an empty page.GET /v1/people/email-finder/submissions?state=&fullyRefunded=&page=&size=&sort=— your own submission history (auto-scoped to your token). Item fields:trackId,service(EMAIL_FINDER),requestSize,state(PENDING|SETTLED),fullyRefunded,refundReason(null unless fully refunded),attempts,created,submittedDate,doneDate,settledAt. Filters:state(PENDING/SETTLED),fullyRefunded(bool),sorte.g.created,desc(repeatable),page≥0,size1–100 (default 25).PATCH /v1/people/email-finder/{trackId}/notify— re-send the completion webhook (body:{ "webhook": "https://..." }, can be a different URL). See Section 8 for the delivery-result semantics.
Related: People Search (produces the trackId), Export People with Email (skip the two-step flow).
6.4 Export People with Email — POST /v1/people/export
POST /v1/people/exportPurpose: one call = People Search + email finding, asynchronously, up to 10,000 people per export. The workhorse for bulk outbound list building.
Credits: 0.5 per exported person (charged for everyone, found or not) + 0.5 per found valid email. Note: differs from Find Emails by Track ID, which charges only for found emails.
Body: same account / contact / lists filters as People Search + required page, size (1–10000 — here size is the total export size) and webhook (HTTPS URL). size > 10,000 → 400 pagination limit exceeded.
Response: { trackId, statistics, state: "PENDING", webhook: { state, retry }, description }. Job completion is POSTed to your webhook (Section 8).
Concurrency & refunds: same as Find Emails by Track ID (500 in flight per token, 10 parallel, ≤10h auto-refund).
Sub-endpoints (all free):
GET /v1/people/export/{trackId}/statistics— pollstate+statisticsuntilDONE. Same403/404/zeroed-200semantics as Email Finder statistics.GET /v1/people/export/{trackId}/inquiries?page=0&size=100— paginated full person + company + email objects (Section 9 skeleton +emailblock). Returns409 track id in progresswhile the export runs — poll statistics untilstate: DONEfirst. Individual items may still showemail.state: PROCESSINGwith an emptyemail.output.403STUCK_*-refunded /404unknown; refunded-no-result →200empty page.GET /v1/people/export/submissions?state=&fullyRefunded=&page=&size=&sort=— same shape as Email Finder submissions;serviceisPEOPLE_EXPORT.PATCH /v1/people/export/{trackId}/notify— re-send the completion webhook (body:{ "webhook": "https://..." }).
Related: People Search + Find Emails by Track ID (the equivalent two-step flow with a preview step), Export Single Person (row-by-row instead of bulk).
6.5 Export Single Person with Email — POST /v1/people/export/single (v1) & POST /v2/people/export/single (v2)
POST /v1/people/export/single (v1) & POST /v2/people/export/single (v2)Purpose: real-time enrichment of ONE person — full profile + verified email. Made for row-by-row enrichment (CRM, spreadsheets, Clay).
Credits: 1 if a valid email is found (0.5 full-profile enrichment + 0.5 real-time BounceBan-validated email), 0 if no valid email is found.
Body: { "id": "<ai-ark person id>" } or { "url": "<linkedin profile url>" } — at least one required; id is looked up first. Both empty → 400 id and url cannot be both empty. The id can come from People Search or Preview results.
Response (200): the full person object (Section 9) including the email block (state: "DONE", output: email objects) and last_updated.
v1 vs v2: identical behavior, except not-found: v1 returns 404 (e.g. no email found); v2 always returns HTTP 200 with envelope { status, error, data } (data: null when not found / no email) — recommended inside Clay, which expects 200s. v2 adds the X-Credit header (e.g. -1.0) on charged responses. Real errors (400/402/500) and gateway errors (401/429) keep their status on both.
Related: People Search / Preview (source of person ids), Mobile Phone Finder (phones for the same person), Reverse People Lookup (start from an email instead).
6.6 Mobile Phone Finder — POST /v1/people/mobile-phone-finder (v1) & POST /v2/people/mobile-phone-finder (v2)
POST /v1/people/mobile-phone-finder (v1) & POST /v2/people/mobile-phone-finder (v2)Purpose: find a person's mobile phone number — add direct dials to your lists.
Credits: 5 per result, charged only when a phone number is found. Nothing found → v1 returns 404 and no credits are charged.
Body — two search modes: { "linkedin": "<full profile url>" } or { "domain": "acme.com", "name": "Jane Doe" } — domain and name must be provided together (400 both domain and name are required otherwise; also 400 invalid domain provided / 400 invalid linkedin url provided).
Response (200): { "id": "<person id>", "linkedin": "<url>", "data": [ [ "+13152468945", ... ] ] } — data is an array of arrays; the inner array can hold multiple numbers ordered by confidence, E.164 format.
v1 vs v2: identical, except not-found: v1 → 404; v2 → HTTP 200 envelope with data: null (recommended for Clay) + X-Credit header (e.g. -5.0) on charged responses.
Related: Export Single Person (email for the same person), People Search / Preview (has_mobile flag tells you whether a lookup has something to find).
6.7 Reverse People Lookup — POST /v1/people/reverse-lookup
POST /v1/people/reverse-lookupPurpose: identify a person from an email address. If the email exists in AI Ark's database, returns the full profile (like a People Search result — profile, company, education, positions, department — but excluding valid email & mobile). Useful for enriching inbound leads, newsletter signups, form fills, and CRM records where you only have an email.
Credits: 0.5 per request.
Body: { "search": "[email protected]" } — search (the email address) is the only, required field.
Errors: 404 = no profile with this email in the database (treat as "no result").
Related: Export Single Person (get a verified email), Mobile Phone Finder, Personality Analysis (personalize outreach to the identified person).
6.8 Personality Analysis — POST /v1/people/analysis
POST /v1/people/analysisPurpose: analyzes a profile's skills, headline and summary and returns a personality profile with ready-to-apply outreach guidance. Pipe the output into your email-generation prompts to personalize at scale.
Credits: 4 per request.
Body: { "url": "https://www.linkedin.com/in/..." } (required — full LinkedIn profile URL).
Response (200): { model, source: { refId, headline, skills, summary }, score, selling: {...}, hiring: {...}, assessments: {...}, status: "SUCCESS", success: true } where:
selling/hiringeach contain:email(field-by-field advice objects —subject,salutation,greeting,tone,emailLength,bulletPoints,gif,messaging,closingLine,complimentaryClose— each withdefinition/advice/example),communication(types,descriptions,adjectives,whatToSay[],whatToAvoid[], plus a readysubject/bodytemplate onhiring), andkeyTraits(risk,abilityToSayNo,speed,decisionDrivers).assessments:archetypelabel,ocean(openness, conscientiousness, extraversion, agreeableness, emotionalStability — each{score, level}),disc(dominance, influence, steadiness, calculativeness — each{score, level}).
Errors:404 profile not found.
Related: People Search / Reverse Lookup (find the person + LinkedIn URL), Export Single Person (their email).
6.9 Create or Update a List — POST /v1/lists
POST /v1/listsPurpose: reusable exclusion lists of people or companies, referenced from People Search / Preview / Company Search / Export People via the lists filter — for suppressing already-contacted prospects, excluded accounts, and deep pagination past 10,000 results.
Credits: free — lists never cost credits.
Body: { "id": "<optional uuid>", "type": "people_id" | "company_id", "values": [ ...up to 10000 ], "mode": "APPEND" | "REPLACE" } — values required; type required when creating.
Behavior: no id → new list. Existing id → update: APPEND (default) merges values, REPLACE rewrites the list with a completely fresh set — the same list id stays valid and reusable across many searches. Unknown id → a new list is created for you.
Response (200): { id, workspace, type, values, created } (created = epoch milliseconds).
Limits: 50 lists/day, 10,000 values/list, lists expire after 24 hours (recreate daily). Violations → 400 (e.g. terms_lookup_list_values_limit_exceeded 10000, missing type on insert, daily quota exceeded).
Usage in searches: "lists": { "people_id": { "exclude": ["<list-id>", ...] } } on people endpoints, "lists": { "company_id": { "exclude": [...] } } on Company Search — up to 10 lists per request.
6.10 Fetch Your Credits — GET /v1/payments/credits
GET /v1/payments/creditsPurpose: remaining balance. Credits: free. Response: { "total": 100 }.
Agent tip: check before large jobs; e.g. an Export of 1,000 people can cost up to 1,000 credits (0.5 + 0.5 each).
7. Complete Filter Reference (People Search, Preview, Export People; account.* also in Company Search)
account.* also in Company Search)Filters live under account (company attributes) and contact (person attributes).
7.1 Match modes, any/all, limits
Most filters use the shape { "any": { "include": [...], "exclude": [...] }, "all": { "include": [...], "exclude": [...] } }:
any= OR — matches if at least one value matches. More values = broader results.all= AND — matches only if every value matches. More values = narrower results.- Both accept
includeandexclude. A search cannot consist ofexcludefilters only.
Text filters wrap values with a match mode: { "mode": "SMART" | "WORD" | "STRICT", "content": ["..."] }:
SMART(default) — AI-related concepts:creative directoralso findsart director,head of creative. Use to expand reach when wording varies.WORD— exact phrase, extra words allowed: matchesassociate creative director, notart director.STRICT— exact characters only: matchescreative directorand nothing else.
Plain-array filters (no match mode): domain, linkedin, socialMediaLink, phoneNumber, location, socialMedia, type, naics, technology (legacy), seniority, departmentAndFunction, profileBadge, company IDs, education.school IDs, account.language.
Limits: max 300 values per include/exclude array; a search pages through at most 10,000 records (totalElements itself is uncapped — use the Lists pattern in Section 5 to retrieve more).
Range filters: { "type": "RANGE", "range": [ { "start": X, "end": Y }, ... ] } — range is an array of bands, OR'd together; add multiple bands to expand results. Omit end for open-ended "X+", omit start for "less than Y". (foundedYear takes a single range object, and foundedYear/revenue additionally accept type: "ALL" / "NONE" — see below.)
7.2 account filters (company attributes)
account filters (company attributes)| Filter | Values / shape | Notes |
|---|---|---|
domain | plain arrays — { "any": { "include": ["apple.com"] } } | Website domain, case-insensitive; bare domain recommended. Best identifier to resolve companies to IDs via Company Search. |
linkedin | plain arrays | Company LinkedIn URL — full URL (https://www.linkedin.com/company/apple), not the slug. |
url | match modes | Most tolerant identifier: bare domain, www., full URL, or LinkedIn company URL all work. |
name | match modes | Company name. Name matching can hit unrelated same-name companies — for precision, resolve to IDs via Company Search and use contact.company. |
socialMediaLink | plain arrays | A specific company social profile URL (Facebook, X, Instagram, LinkedIn…). |
phoneNumber | plain arrays | E.164 format, e.g. +18885335659. |
industries | match modes | 919 values — CSV: https://ai-ark.com/static/industries.csv?v=1. SMART also matches adjacent industries. A company can have several industries; matches if any fits. |
location | plain arrays | Office locations — matches any office, not just HQ. Countries, states, plain city names (munich), metro areas (greater munich metropolitan area), regions (Europe). Same vocabulary as contact.location. |
geoLocation | { "position": { "lat": 53.5511, "lng": 9.9937 }, "radius": 50, "unit": "km" } | Radius search around a coordinate (unit: km or mi); matches companies with any office inside the radius. |
productAndServices | match modes | What the company offers, from its public positioning. E.g. payroll software, solar installation. |
socialMedia | plain arrays | Has a profile on: FACEBOOK, INSTAGRAM, TWITTER, LINKEDIN. |
type | plain arrays | 8 values: PRIVATELY_HELD, SELF_OWNED, SELF_EMPLOYED, PARTNERSHIP, PUBLIC_COMPANY, NON_PROFIT, EDUCATIONAL, GOVERNMENT_AGENCY. |
foundedYear | { "type": "RANGE", "range": { "start": 2015, "end": 2022 } } | type: RANGE (single {start,end} object), ALL (any company with a known founded year), NONE (founded year missing). |
employeeSize | { "type": "RANGE", "range": [ { "start": 51, "end": 200 }, { "start": 201, "end": 500 } ] } | Headcount; multiple bands OR'd. |
retailSize | { "type": "RANGE", "range": [ { "start": 10, "end": 100 } ] } | Number of physical locations worldwide (e.g. retail chains with 10–100 stores); bands OR'd. |
revenue | { "type": "RANGE", "range": [ { "start": 1000000, "end": 10000000 } ] } | Annual revenue in USD (no separators); bands OR'd. type: ALL = has any revenue value; type: NONE = no revenue data. |
funding | see below | Funding history. |
language | plain arrays + range | Languages the company operates in (48 values, see Section 8) — plain arrays here, unlike contact.language. range filters HOW MANY languages. |
keyword | sources shape, see below | Free-text across company fields. Sources (5): NAME, KEYWORD, SEO, DESCRIPTION, INDUSTRY — all 5 may be used at once. |
metric | see below | Employees by department + headcount growth. |
technologies | match modes | Use this one. 16,000+ values — CSV: https://ai-ark.com/static/technologies.csv?v=1. SMART also finds related technologies. |
technology | plain arrays | Legacy filter, kept for backward compatibility — no match modes. Prefer technologies. |
naics | plain arrays | NAICS codes, e.g. 454110. (SIC appears in responses but is not yet filterable.) |
employee | Company Search only | Job Role filter — see 6.2. Returns companies that employ matching people. |
funding — all sub-fields optional, combinable:
"funding": {
"type": ["SEED", "SERIES_A"],
"totalAmount": { "start": 1000000, "end": 20000000 },
"lastAmount": { "start": 500000, "end": 2000000 },
"duration": { "start": "1620847800000", "end": "1747168200000" }
}type— round types raised (any match), 30 values:PRE_SEED,SEED,SERIES_A…SERIES_J,VENTURE_ROUND,ANGEL,PRIVATE_EQUITY,DEBT_FINANCING,CONVERTIBLE_NOTE,GRANT,CORPORATE_ROUND,EQUITY_CROWDFUNDING,PRODUCT_CROWDFUNDING,SECONDARY_MARKET,POST_IPO_EQUITY,POST_IPO_DEBT,POST_IPO_SECONDARY,NON_EQUITY_ASSISTANCE,INITIAL_COIN_OFFERING,UNDISCLOSED,SERIES_UNKNOWN,FUNDING_ROUND.totalAmount— total raised across all rounds, USD range.lastAmount— size of the most recent round, USD range.duration— date of the most recent round as an epoch-milliseconds string range — target companies that raised recently, when budgets are fresh.
metric — department-level headcount metrics. function takes one of 27 department values: sales, marketing, engineering, finance, human_resources, information_technology, operations, business_development, customer_success_and_support, product_management, accounting, legal, consulting, education, research, purchasing, real_estate, media_and_communication, quality_assurance, arts_and_design, healthcare_services, entrepreneurship, community_and_social_services, administrative, military_and_protective_services, program_and_project_management, support.
"metric": {
"employee": [ { "function": ["engineering"], "start": 10, "end": 50 } ],
"growth": [ { "function": ["marketing"], "start": 10, "end": 15, "timeFrame": "SIX" } ]
}employee— current absolute headcount in the department(s), withinstart–end.growth— percentage headcount change over a look-back window;timeFramein months:ONE,THREE,SIX,TWELVE,TWENTY_FOUR. Department growth is one of the strongest intent signals — a growing sales team buys sales tooling.
keyword (account) — free-text across company fields; content holds the terms, sources says where to search, each with its own mode:
"keyword": { "any": { "include": {
"sources": [ { "mode": "SMART", "source": "DESCRIPTION" }, { "mode": "SMART", "source": "SEO" } ],
"content": ["carbon accounting"]
} } }7.3 contact filters (person attributes)
contact filters (person attributes)| Filter | Values / shape | Notes |
|---|---|---|
fullName | match modes | Find a specific person without their LinkedIn URL; combine with contact.company or account.domain to disambiguate common names. |
linkedin | plain arrays | Person's LinkedIn profile URL (full URL). Check whether profiles exist in AI Ark, or exclude known ones. |
socialMediaLink | plain arrays | A specific personal social profile URL. |
company | company IDs (UUIDs), scoped | { "latest": {...}, "current": {...}, "previous": {...} }, each any/all + include/exclude of IDs from Company Search (also present in every People result as company.id). latest = primary active role's company; current = ALL simultaneously-active roles; previous = past employers. Max 300 IDs per array. The precise way to target a fixed account set. |
experience | see below | Job titles + tenure by scope. |
seniority | plain arrays | 12 values: founder, owner, partner, c_suite, vp, director, head, manager, senior, mid-level, entry, intern. Based on the current role: a former Director who is now a VP matches vp. |
departmentAndFunction | plain arrays | 592 values — CSV: https://ai-ark.com/static/departments-and-functions.csv?v=2. One filter matches all three response levels (departments, sub_departments, functions). Taxonomy: 36 parent departments, each expanding into detailed functions, plus other_<department> catch-alls (e.g. other_accounting) and the standalone unknown. A parent department matches everyone in it; a function narrows to that role. E.g. software_development, demand_generation, data_science. |
location | plain arrays | Where the person lives. Countries, states, plain city names (seattle, hamburg), metro areas (greater seattle area, new york city metropolitan area), regions (Europe). For the employer's location use account.location / account.geoLocation. |
language | match modes + range | Languages the person speaks: { "mode": "SMART", "content": ["english"] } (unlike account.language!). range = number of languages spoken — { "start": 3 } finds polyglots (3+), a strong signal for international sales roles. 48 values (Section 8). |
skill | match modes | Profile skills; with all, require several at once (python AND machine learning). Also searchable via keyword source SKILL. |
certification | match modes | E.g. pmp, aws certified solutions architect, cpa. |
education | see below | School (by company ID), degree, field of study, study period. |
keyword | sources shape | Free-text across profile sections, max 5 sources per request. 15 sources: HEADLINE, SUMMARY, SKILL, CERTIFICATION, COURSE, PROJECTS, PUBLICATION, PATENT, AWARD, ORGANIZATION, VOLUNTEERING, TEST_SCORE, WORK_HISTORY_DESCRIPTION, EDUCATION_DESCRIPTION, LANGUAGE_SKILL. |
socialMedia | plain arrays | Has a profile on: FACEBOOK, INSTAGRAM, TWITTER, LINKEDIN. |
profileBadge | plain arrays | 6 values: VERIFIED, PREMIUM, OPEN_TO_WORK, INFLUENCER, CREATOR, HIRING. HIRING = strong buying signal for recruiting-adjacent products; OPEN_TO_WORK = talent sourcing. |
socialMediaFollower | see below | LinkedIn follower/connection counts. LinkedIn only — other platforms → 501. |
contact.experience — job title + tenure, three scopes:
latest— the person's primary active role only (classic "current job title" search).current— all active roles: a person holding 3 simultaneous positions matches if any fits — catches advisors, board members, multi-role founders.previous— past roles only: someone who was a Director and is now a VP matchesprevious: director.
Each scope: title (match modes, any/all) and duration (not on previous-title-only searches; duration keys are named after the scope):
"experience": {
"latest": {
"title": { "any": { "include": { "mode": "SMART", "content": ["creative director"] } } },
"duration": {
"latestCompany": { "min": { "year": 1, "month": 0 }, "max": { "year": 4, "month": 0 } },
"latestJob": { "min": { "year": 0, "month": 0 }, "max": { "year": 0, "month": 6 } },
"total": { "min": { "year": 3, "month": 0 }, "max": { "year": 8, "month": 0 } }
}
}
}latestCompany/currentCompany/previousCompany— time at that employer.latestJob/currentJob/previousJob— time in that specific role.latestJob.max: {year: 0, month: 6}= new in role, a strong outreach trigger.total— total career experience.
contact.education:
"education": {
"school": { "any": { "include": ["49c9a269-b00e-28ac-eeb6-f74d3fd32a00"] } },
"degree": { "any": { "include": { "mode": "SMART", "content": ["mba"] } } },
"fieldOfStudy": { "any": { "include": { "mode": "SMART", "content": ["computer science"] } } },
"date": { "start": 2011, "end": 2015, "present": false }
}school— AI Ark company IDs: universities are companies; resolveharvard.eduor the school's LinkedIn URL via Company Search to get the ID.date— the study period (years):start/endas stated on the profile,present: true= still studying (current students); use for alumni-cohort targeting.
contact.socialMediaFollower — LinkedIn only (facebook/twitter/instagram → 501 SOCIAL_MEDIA_FOLLOWER_PLATFORM_NOT_SUPPORTED). followers and connections independently optional; each type: "RANGE" with an OR'd range array; omit end for open-ended ("30K+"), omit start for "less than". Counts are absolute integers (1.5K = 1500). Empty object = no-op.
"socialMediaFollower": { "linkedin": {
"followers": { "type": "RANGE", "range": [ { "start": 5000, "end": 10000 }, { "start": 30000 } ] },
"connections": { "type": "RANGE", "range": [ { "start": 1500, "end": 4200 } ] }
} }In responses the counts appear under statistics.network.followers_count / .connections_count (either may be absent when unknown — absence ≠ zero).
7.4 lists filter
lists filter{ "people_id": { "exclude": ["<list-id>", ...] } } on people endpoints / { "company_id": { "exclude": [...] } } on Company Search. Up to 10 lists per request, each holding up to 10,000 items. Uses: suppress contacted prospects; paginate beyond the 10,000-result cap (Section 5).
8. Accepted Enum Values (quick reference)
- Seniority (12):
founder,owner,partner,c_suite,vp,director,head,manager,senior,mid-level,entry,intern - Profile badge (6):
VERIFIED,PREMIUM,OPEN_TO_WORK,INFLUENCER,CREATOR,HIRING - Company type (8):
PRIVATELY_HELD,SELF_OWNED,SELF_EMPLOYED,PARTNERSHIP,PUBLIC_COMPANY,NON_PROFIT,EDUCATIONAL,GOVERNMENT_AGENCY - Match modes:
SMART,WORD,STRICT - Social media:
FACEBOOK,INSTAGRAM,TWITTER,LINKEDIN - Contact keyword sources (15, max 5/request):
HEADLINE,SUMMARY,SKILL,CERTIFICATION,COURSE,PROJECTS,PUBLICATION,PATENT,AWARD,ORGANIZATION,VOLUNTEERING,TEST_SCORE,WORK_HISTORY_DESCRIPTION,EDUCATION_DESCRIPTION,LANGUAGE_SKILL - Account keyword sources (5, all usable at once):
NAME,KEYWORD,SEO,DESCRIPTION,INDUSTRY - Headcount-growth timeframes (months):
ONE,THREE,SIX,TWELVE,TWENTY_FOUR - Metric department functions (27): listed in Section 7.2 under
metric - Funding round types (30): listed in Section 7.2 under
funding - Languages (48): english, spanish, french, portuguese, german, dutch, italian, chinese, turkish, polish, russian, swedish, arabic, indonesian, danish, czech, norwegian, japanese, korean, romanian, ukrainian, thai, hindi, malay, tagalog, vietnamese, finnish, persian, greek, hungarian, bengali, marathi, telugu, panjabi, serbian, slovak, croatian, lithuanian, latvian, albanian, icelandic, armenian, bosnian, tamil, javanese, malayalam, kannada, burmese. Note:
contact.languagevalues are wrapped with a match mode;account.languagetakes plain arrays. - Industries: 919 values, e.g.
software development,hospitals and health care— full list: https://ai-ark.com/static/industries.csv?v=1 - Technologies (
account.technologies): 16,000+ values, e.g.salesforce,hubspot— full list: https://ai-ark.com/static/technologies.csv?v=1 - Departments & functions (
contact.departmentAndFunction): 592 values, e.g.software_development,demand_generation— full list: https://ai-ark.com/static/departments-and-functions.csv?v=2 - Locations: free-form and forgiving — countries (
Germany), states (California), plain city names (seattle,hamburg), metro areas (greater seattle area), regions (Europe) all work; exact values are not required. Same vocabulary forcontact.locationandaccount.location.
9. Response Object Skeletons (for writing parsers)
Person object (People Search / Export results / Export Single / Reverse Lookup; Preview returns a reduced + masked variant plus has_* flags):
id — AI Ark person UUID (use in Export Single, Lists)
identifier — LinkedIn slug
profile { first_name, last_name, full_name, headline, title, picture.source, background.source, birth_date, summary }
link { linkedin, twitter, github, facebook }
location { country, state, city, position, default, short }
languages { primary_locale {country, language}, supported_locales[], profile_languages }
industry — person's industry label
educations[] { school {id, name, logo, url}, degree_name, field_of_study, grade, date {start, end} }
certifications[] { name, authority, url, license_number, display_source, company, date }
organizations[] { name, position, date }
volunteer_experiences[] { role, company, cause, description, date }
position_groups[] { company {id, name, logo, url, employees {start, end}}, date {start, end},
profile_positions[] { company, description, title, employment_type, location, date } }
skills[] — strings
member_badges { creator, hiring, open_to_work, premium, verified, influencer }
company { id, summary { name, legal_name, description, overview, seo, founded_year, type, industry,
staff { total, range {start, end} }, logo.source },
link { website, domain, domain_ltd, linkedin, facebook, twitter, crunchbase },
financial { revenue.annual {start, end, amount}, funding {...rounds}, ipo {...},
exit {...}, acquisition {...}, investment {...}, diversity_investment {...},
aberdeen.it_spend },
location { headquarter {continent, country, state, city, street, postal_code, raw_address, position {lat,lng}},
locations[] },
technologies[] { name, category }, industries[], keywords[], hashtags[], languages[],
sub_organizations[], sic[], naics[], last_updated }
department { departments[], sub_departments[], functions[], seniority }
statistics { network { followers_count, connections_count } } — may be absent
email { state: "PROCESSING"|"DONE", output: [ ...email objects, see Section 3 ] } — export endpoints only
last_updated — date of last data refresh
Search/results pagination envelope (Spring style, on all paginated responses): content[], totalElements, totalPages, size, number, numberOfElements, pageable { pageNumber, pageSize, offset, paged, unpaged, sort }, sort, first, last, empty — plus trackId on People Search and on trackId-scoped results.
10. Webhooks (async jobs: Find Emails by Track ID & Export People)
How they work:
- You call the endpoint with a
webhookfield (HTTPS callback URL). - The API responds immediately with a
trackIdandstate: "PENDING". - On completion, a
POSTwithContent-Type: application/jsonis sent to your URL with the full results.
Webhook payload (both jobs):
- Top level:
trackId,state: "DONE",description,statistics { total, found },data[]. - Find Emails
data[]items:{ refId, state: "DONE", input { firstname, lastname, domain }, output: [ ...email objects ] }. - Export People
data[]items: the full person object (Section 9) including theemail { state, output }block. - If the job was fully refunded with no deliverable result (
SEARCH_NO_RESULTS/NO_EMAILS_FOUND), the webhook carries the full-refund payload.
Delivery & retries: delivery is retried automatically. A webhook may occasionally be delivered more than once — deduplicate by trackId. Respond quickly with 200 OK and process asynchronously on your side.
Re-send — PATCH .../{trackId}/notify (free; body { "webhook": "https://..." }, may be a different URL). Returns a structured WebhookDeliveryResult — inspect the fields, not the HTTP status:
delivered: true— your endpoint accepted (2xx).webhook.statusechoes your status,webhook.bodyyour response body.delivered: false, fault: "WEBHOOK"— your endpoint failed (non-2xx or unreachable);messageexplains; fix your endpoint, call notify again. Still HTTP200.delivered: false, fault: "SERVICE"— failure on AI Ark's side (webhook: null); retry later / contact support. Still HTTP200.- HTTP
403— the submission was auto-refunded for non-delivery (STUCK_HARD_REFUND4031013/STUCK_SOFT_REFUND4031014) and is not retrievable — resubmit the job.
11. Job States, Errors & Rate Limits
Async job states: PENDING → processing → DONE. Submission history uses PENDING → SETTLED. Per-item email.state may be PROCESSING while running.
HTTP errors (all endpoints):
| Code | Meaning | Agent action |
|---|---|---|
| 400 | Bad request: invalid filters/fields, id and url cannot be both empty, both domain and name are required, invalid domain/linkedin url provided, pagination limit exceeded (Export size > 10,000), too many pending requests (500 in-flight cap), list limits (terms_lookup_list_values_limit_exceeded 10000, daily quota) | Fix the payload; for the pending cap, wait for completions and retry. |
| 401 | Missing/invalid X-TOKEN (gateway) | Check the API key. |
| 402 | Not enough balance | Stop; check GET /v1/payments/credits; ask the user to top up. |
| 403 | trackId was auto-refunded (STUCK_HARD_REFUND status 4031013 / STUCK_SOFT_REFUND status 4031014) and is not retrievable | Resubmit the job; check /submissions for refundReason. |
| 404 | No data / not found (v1 sync endpoints: no match, no email, no phone, no profile); for trackId endpoints: "not found, expired, or already used" | Treat as "no result", not a failure. Use v2 endpoints in Clay. |
| 409 | Export results not ready — trackId still in progress (Export People Results only; Email Finder results are readable while running) | Poll .../statistics until state: DONE, then fetch results. |
| 429 | Rate limit exceeded (gateway) | Back off and retry (5 req/s per token). |
| 500 | Unexpected server error | Retry with backoff; contact support if persistent. |
| 501 | Filter not supported — e.g. contact.socialMediaFollower on a non-LinkedIn platform (SOCIAL_MEDIA_FOLLOWER_PLATFORM_NOT_SUPPORTED) | Remove/fix the unsupported filter. |
Rate limits: 5 requests/second per token (default); >450,000 credits/month → custom limits via [email protected]. Async submissions: max 500 in flight per token per service, 10 processed in parallel.
12. Rules of Thumb for Agents
- Preview first. Validate any new filter set with
POST /v1/people/preview(1 credit/page) before spending per-result credits; usetotalElementsto size the job andhas_*flags to decide what's worth enriching. - Always estimate cost before big calls (
results × per-result price) and checkGET /v1/payments/creditsfirst for jobs over ~100 credits. - Never re-verify AI Ark emails — they are BounceBan-verified in real time and safe to send. Segment sending by
domainType(SMTPvsCATCH_ALL) andgenericinstead. - Use the
trackIdimmediately — single use, 6-hour expiry. Search → submit email finding in the same workflow. - Prefer Export People with Email for bulk (one call, ≤10,000 people); prefer Export Single v2 and Mobile Finder v2 inside Clay (they return HTTP 200 envelopes and an
X-Creditheader). - Deduplicate with Lists (free): maintain a
people_idexclusion list of everyone already contacted;APPENDto grow,REPLACEto rewrite; rebuild daily (24h expiry). Lists are also the pattern for paginating past 10,000 results. - Company targeting: Company Search → take company
ids → People Searchcontact.company— more precise than name matching. Same trick resolves universities foreducation.school. - Mine the intent signals:
metric.growth(department headcount growth),funding.duration(recent raises),experience.duration.latestJobmax 6 months (new in role),profileBadge: HIRING. - Handle refunds automatically: on
403from a trackId endpoint, resubmit; check/submissions(fullyRefunded,refundReason) before assuming credits were lost — no support ticket needed. - Errors are often "no result": a
404from sync endpoints means the person/email/phone wasn't found (and cost nothing where finding is charged) — continue the workflow, don't retry blindly.
Updated 5 days ago