← all tools
Data & sourcing

AI Ark

Search for companies, then for the people inside them.

Cost per call0.1–1.4 cr
Your own keyoptional
CategoryData & sourcing

AI Ark is a broad B2B database that works from the company downward. Find the firms that match your brief, then the people in the roles you care about, then their emails and mobiles. It runs in reverse too: hand it a number and it tells you whose it is.

AI Ark is one of the first tools hyreflow reaches for when you ask for a list from nothing.

things you can ask for

  • Take this job brief url and build a first longlist using AI Ark and Prospeo
  • Find recruitment agencies in Manchester with 10 to 50 staff, then check who is hiring on TheirStack
  • Whose number is this?
  • Get mobiles for this shortlist and file the companies in Recruit CRM

works well with AI Ark

  • Prospeo

    run both and merge, the coverage rarely overlaps completely

  • TheirStack

    add a hiring signal to a company list

  • Enrichley

    validate before anything sends

  • Recruit CRM

    file the companies and their contacts in one write

Skills that use AI Ark

A skill is a whole pipeline your agent already knows. These ones call AI Ark as part of the run.

alert4 steps

Reverse poach → original client alert

Placed candidate updates LinkedIn. Track via AI Ark, log Fathom call notes, alert the original client.

coming soon

What your agent reads before it touches AI Ark

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the AI Ark docs →
Show the raw playbookwritten for the agent

HOW in lib/aiark.py. Docs cached at reference/docs/aiark/raw/; full filter schema in reference/docs/aiark/raw/people_search.openapi.md.

Auth & config

  • Base URL: https://api.ai-ark.com/api/developer-portal/v1 · Auth: header X-TOKEN (env AIARK_API_KEY; never hardcode).
  • Rate limits: 5 req/s · 300/min · 18,000/hr.
  • All paths CONFIRMED from docs.ai-ark.com (ReadMe .md + llms.txt, Tier-2 plain HTTP). Watch the shapes: company search = POST /v1/companies (plural), email finder = POST /v1/people/email-finder, async status/results use {trackId} path segments and the results path is /inquiries, credits = GET /v1/payments/credits. Full endpoint index at reference/docs/aiark/raw/endpoints.md; people-search schema + enums at reference/docs/aiark/raw/people_search.openapi.md.

Filter structure (critical — from the official OpenAPI spec)

People search body = {account, contact, lists, page, size}; page (0-based) and size (≤100) are required. account/contact filters take an any and/or all wrapper, each with include/exclude:

  • Text filters{ "mode": "SMART"|"WORD"|"STRICT", "content": [...] } (e.g. account name, url, productAndServices, industries, technologies; contact fullName, skill, certification, title (under experience), language). The mode enum is SMART/WORD/STRICT — not "FUZZY".
  • String / enum filters → bare arrays { "include": [...] } (e.g. account domain, linkedin, location, naics, technology, type; contact seniority, departmentAndFunction, socialMedia, profileBadge).
  • Field names: the contact location field is contact.location (not contactLocation); titles/durations live under contact.experience.{latest,current,previous}.
  • Scope a people search to a company with account.domain / account.linkedin / account.name. contact.company.{latest,current,previous} takes company UUIDs and returns contaminated rows even for a correct UUID — use the account.* fields, with root domains.
  • A totalElements of ~414,000,000 means the filters were ignored (that's the whole database), not that you matched a lot. An unrecognized filter key is dropped silently behind a 200, so peek the count with size: 1 when trying a new filter shape: adding a filter must reduce it.
  • Enum lists (SeniorityLevel, DepartmentAndFunction, Industry, CompanyType, ProfileBadge, Language, TimeFrame) are in the cached spec.

Response shape: top-level { content: [...people...], pageable, totalElements, totalPages, number, size, trackId, first, last, empty } — read people at response["content"]; the find-emails trackId is top-level. Errors: 404 data-not-found; 501 = socialMediaFollower on a non-LinkedIn platform (only linkedin supported).

Async flows

  • Export people: export_people → trackId → poll export_statistics (DONE) → export_results (paginated).
  • Find emails: run people_search (returns trackId) → find_emails(trackId,+webhook) → poll email_finder_statisticsemail_finder_results. trackId is single-use, expires ~6h.

Guardrails

  • mobile_phone_finder is expensive (0.4 credits) — use only on high-confidence matches. fetch_credit() = safe read-only pilot. Pilot small; gate bulk export/find-emails.

Handoff

Sourcing + enrichment: company→people search → email/phone (export/find-emails or mobile finder) → ATS/sequencer. Discover companies first, then people.

Field notes (production experience)

Field-shape note: these are vendor-native operational notes. The client returns the raw vendor JSON and uses the method names in this file — read field shapes accordingly (no normalized-wrapper / result.data. prefix).

Base URL: server is https://api.ai-ark.com/api/developer-portal; the people-search path is /v1/people, so the full URL is .../developer-portal/v1/people. BASE_URL folds /v1 in and people_search posts to people.

The filter shapes, mode enum (SMART/WORD/STRICT), field names, and response shape are documented from the spec in the Filter structure section above.

Credit costs: company search 0.1/result; people search 0.1/result; reverse lookup 1.4/req; mobile phone finder 0.4/req (use only after a high-confidence match); export/email-finder 0.1/email; people analysis 0.3/req; polling, statistics, results = free.

Async (export_people / find_emails): launch -> trackId -> poll *_statistics until state:"DONE" -> fetch *_results (paginated). find_emails trackId is single-use, expires 6h after the people_search that generated it.

Pagination: zero-based page + size (search/export use JSON body; results browsing uses query params). Errors: 409 = you fetched results while the async job is still running (poll statistics first); 404 = profile not found; 429 = rate limit (resets every 60s; limits 5/s, 300/min, 18k/hr). Constraints: export max 10,000 results; webhooks auto-retry up to 30x (respond 200 immediately, HTTPS only).

Callable surface — lib/aiark.py

Import: from lib.aiark import AiArk → instantiate AiArk() (reads key from env). Base: https://api.ai-ark.com/api/developer-portal/v1. Generic passthrough: request(method, path, *, params, json).

  • company_search(payload: dict) -> dict — POST /v1/companies — company search (CONFIRMED). Body: filters + page (0-based) + size (<=100).
  • email_finder_results(track_id: str, *, page: int | None = None, size: int | None = None) -> dict — GET /v1/people/email-finder/{trackId}/inquiries — paginated results
  • email_finder_statistics(track_id: str) -> dict — GET /v1/people/email-finder/{trackId}/statistics — job status (free, CONFIRMED).
  • export_people(payload: dict) -> dict — POST /v1/people/export — async export (filters + page + size 1..10000 + webhook).
  • export_results(track_id: str, *, page: int | None = None, size: int | None = None) -> dict — GET /v1/people/export/{trackId}/inquiries — paginated export results
  • export_single(payload: dict) -> dict — POST /v1/people/export/single — export one person with email by id OR url (CONFIRMED).
  • export_statistics(track_id: str) -> dict — GET /v1/people/export/{trackId}/statistics — export job status (free, CONFIRMED).
  • fetch_credit() -> dict — GET /v1/payments/credits — remaining credits (read-only, safe pilot; CONFIRMED).
  • find_emails(payload: dict) -> dict — POST /v1/people/email-finder — async email finder from a prior people_search trackId
  • mobile_phone_finder(payload: dict) -> dict — POST /v1/people/mobile-phone-finder — mobile by linkedin OR domain+name (+type).
  • people_analysis(payload: dict) -> dict — POST /v1/people/analysis — personality analysis from a LinkedIn profile url (CONFIRMED).
  • people_search(payload: dict) -> dict — POST /v1/people — people search (CONFIRMED path). Strict nested filter structure:
  • reverse_lookup(payload: dict) -> dict — POST /v1/people/reverse-lookup — look up a person by email/phone via search (CONFIRMED).
  • save_list(payload: dict) -> dict — POST /v1/lists — create/update a list (up to 10k items) for use in the people_search