Explee Public API

Version: 1.0.0

> Tip: This documentation is designed for humans. To integrate with AI agents, provide them with the OpenAPI schema at https://api.explee.com/public/api/openapi.json instead.   Search millions of companies worldwide using natural language queries and structured filters! Rate limit: 10000 requests per hour, 150 concurrent requests per organization. Timeout: 90s.

Authentication

ApiKeyAuth: apiKey - Get your API key at [API Keys](https://explee.com/api-keys)

Endpoints

POST /public/api/v1/search/companies

Companies

Search for companies using structured definitions. ## How It Works Provide a structured definition of your target companies in the `definition` field — our AI will find matching companies. All other filters are optional. See the request schema below for the full list of available filters. **Definition examples:** - `"YouTube video summarization tool"` - `"electronic signature platform"` - `"AI B2B SaaS"` - `"real estate company"` ## AI Enrichment Use the `criteria` parameter to score companies against custom criteria. Each company will be evaluated and scored (0-5) with reasoning for each criterion. ## Pricing - **First 100 results are free** (including AI enrichment) - **0.5 credits** per company after the first 100 - **+0.1 credits** per company per criterion after the first 100 Example: 150 companies with 2 criteria = 50 billable × (0.5 + 0.1 × 2) = 35 credits (first 100 are free) Empty results = 0 credits. ## Deduplication Pass dedup list ids in `exclude_lists` to skip companies you already own — they are not returned and not charged. Free results zone does not apply. See the Deduplication endpoints.

Request Body

Request body required

Responses

POST /public/api/v1/search/companies-by-domains

Companies by Domains

Find companies by their domains. **How it works:** 1. Provide a list of company domains (up to 1000) 2. The system looks up each domain and returns **at most one** company profile per domain (the best match by quality score) **Pricing:** - **0.5 credit(s)** per company found - **0 credits** for domains not found **Limits:** - Maximum 1000 domains per request - Maximum 10000 companies per response **Deduplication:** - Pass dedup list ids in `exclude_lists` — matched companies are not returned and not charged

Request Body

Request body required

Responses

POST /public/api/v1/search/people

People

Search for people (employees) at companies matching your criteria. ## How It Works Provide `job_titles` in `people_filters` to find people by their role. Use `company_filters.definition` to narrow down which companies to search. Use `company_linkedin_ids` to restrict results to a specific list of company LinkedIn IDs. **Job title examples:** - `["Head of Sales", "VP Sales"]` - `["CTO", "VP Engineering"]` - `["Founder", "CEO"]` ## AI Enrichment Use the `criteria` parameter in `people_filters` to score people against custom criteria. Each person will be evaluated and scored (0-5) with reasoning for each criterion. ## Pricing - **First 100 results are free** (including AI enrichment) - **1.0 credit** per person after the first 100 - **+0.1 credits** per person per criterion after the first 100 Example: 150 people with 2 criteria = 50 billable × (1.0 + 0.1 × 2) = 60 credits (first 100 are free) Empty results = 0 credits. ## Deduplication Pass dedup list ids in `exclude_lists` to skip people you already own — they are not returned and not charged. Free results zone does not apply. See the Deduplication endpoints.

Request Body

Request body required

Responses

POST /public/api/v1/search/people-by-domains

People by Domains

Find people at companies by their domains. **How it works:** 1. Provide a list of company domains (up to 1000) and job titles to search for 2. The system finds companies by domains 3. Returns up to `people_per_company` people per found company with matching job titles **Job title matching:** - Uses semantic search: "Head of Sales" will match "VP Sales", "Sales Director", etc. - Multiple job titles are combined with OR logic **Parameters:** - `people_per_company`: Number of people to return per company (1-1000, default: 1) **Limits:** - Maximum 1000 domains per request - Maximum 20 job titles per request - Maximum 1000 people per company - Maximum 10000 people per response (total) **Billing:** - 1 credit per person returned **Deduplication:** - Pass dedup list ids in `exclude_lists` — matched people are not returned and not charged

Request Body

Request body required

Responses

POST /public/api/v1/search/nl-to-filters

NL to Filters

Convert a free-form natural language query into structured filters for companies and people search. ## How It Works Send any query in plain English (or another language), for example: - `"SaaS companies using Stripe"` - `"Founders of AI startups in Germany"` - `"B2B fintech companies in US and Canada"` The endpoint returns parsed filters in the same shape used by search endpoints: - `companies_filters` - `people_filters` - `focus` (`companies` or `people`) This endpoint does not perform a search and does not charge credits.

Request Body

Request body required

Responses

POST /public/api/v1/enrich/email

Find email by name

Find email address for a single person. ## How It Works Provide the person's first name, last name, and company domain. ## Presets | Preset | Cost | Success Rate | Description | |--------|------|--------------|-------------| | **basic** | 1.5 credits | ~50% | Single provider, pattern-based lookup. Fast and cheap. | | **premium** | 5.0 credits | ~78% | 6 providers with cross-validation. Higher accuracy. | ## Pricing You are only charged when an email is found. Email not found = 0 credits.

Request Body

Request body required

Responses

POST /public/api/v1/enrich/phone

Find phone by LinkedIn URL

Find the work phone number for a single person. ## How It Works Provide the person's LinkedIn profile URL (`linkedin_url`) and we'll find their work phone. You can also pass a known `email` if you have one. ## Presets | Preset | Cost | Success Rate | Description | |--------|------|--------------|-------------| | **basic_new** | 15.0 credits | ~45% | Faster, lower-cost lookup. | | **premium** | 30.0 credits | ~60% | Broader, multi-source lookup. Higher hit rate. | ## Pricing You are only charged when a phone is found. Phone not found = 0 credits.

Request Body

Request body required

Responses

POST /public/api/v1/enrich/email/batch

Find email by name (batch request)

Find email addresses for multiple contacts in one request (async). **How it works:** 1. Submit up to 100 contacts with first_name, last_name, company_domain 2. Receive a `task_id` immediately 3. Poll GET endpoint with `task_id` to check status and retrieve results **Limits:** - Maximum 100 contacts per batch **Pricing:** - **basic**: 1.5 credits per found email - **premium**: 5 credits per found email - Only charged for emails found (not found = 0 credits)

Request Body

Request body required

Responses

GET /public/api/v1/enrich/email/batch/{task_id}

Find email by name (batch results)

Check status and retrieve results for a batch request. **Statuses (in meta.status):** - `pending` — processing in progress, poll again in a few seconds - `completed` — results ready, `contacts` array contains enriched data - `failed` — error occurred, see `meta.error` for details **Response structure:** - `contacts` — null while pending, array when completed - `meta.status` — current processing status - `meta.error` — error description (null if no error) - `meta.credits_charged` — credits used (only charged for found emails) **Typical workflow:** 1. POST to submit batch, get `task_id` 2. GET with `task_id`, check `meta.status` 3. If `pending`, wait 2-5 seconds and poll again 4. When `completed`, read `contacts` array

Parameters

Responses

POST /public/api/v1/find-and-enrich

Find people and enrich their emails (async)

Search for people matching your filters and enrich their emails in a single async job. **How it works:** 1. Submit search filters + `max_contacts` (how many contacts with emails you want, max 500 per call) 2. Receive a `task_id` immediately 3. Poll the GET endpoint with `task_id` for `progress` (with `eta_seconds`) and results 4. For more than `max_contacts`, call again with the previous response's `next_cursor` **Pricing:** - **Search is free** — you are never charged for people searched. - **basic**: 1.5 credits per found email · **premium**: 5 credits per found email. - You are charged ONLY for emails actually found. Worst-case credits are held up front (402 if insufficient). **Deduplication:** pass `exclude_lists` to skip people you already own — they are not enriched and not charged.

Request Body

Request body required

Responses

GET /public/api/v1/find-and-enrich/{task_id}

Find-and-enrich job status and results

Check progress and retrieve results for a find-and-enrich job. **Statuses (in meta.status):** - `pending` — running; read `meta.progress` (incl. `eta_seconds`) and poll again - `completed` — results ready; `contacts` holds the enriched contacts, `meta.next_cursor`/`meta.has_more` for paging - `failed` — error occurred (or timed out — credits released), see `meta.error` Only contacts with a found email are returned. `meta.credits_charged` reflects found emails only.

Parameters

Responses

GET /public/api/v1/tasks

List async tasks

List all async tasks for your organization, including find-and-enrich jobs, batch email enrichments, and campaign lead imports. **Use cases:** - Track async jobs across every API key owned by the same organization - Find task IDs you may have lost - Monitor pending vs completed tasks **Response:** - `tasks` — array of task objects with status and result URL - `total` — total count for pagination **Filtering:** - Use `status` parameter to filter by task status (pending, completed, failed)

Parameters

Responses

GET /public/api/v1/agents

List Explee agents

Returns all pre-built Explee agents. Each agent has a fixed system prompt, input/output schema, and search strategy. Use the agent `id` with the "Run Explee agent" endpoint.

Responses

POST /public/api/v1/agents/{agent_id}/runs

Run Explee agent

Run a pre-built Explee agent by its ID. Explee agents come with a tuned system prompt, search strategy, and output schema — you only provide the input data. Use `GET /public/api/v1/agents` to browse available agents and their expected input schemas. **Cost**: 1 credit per run.

Parameters

Request Body

Request body required

Responses

POST /public/api/v1/agents/runs

Run custom agent

Run a fully custom agent — you provide the system prompt, input/output schemas, and input data. Use this when Explee's pre-built agents don't fit your use case. The agent will execute asynchronously; poll `GET /agents/runs/{job_id}` for results. **Cost**: 1 credit per run.

Request Body

Request body required

Responses

GET /public/api/v1/agents/runs/{run_id}

Get agent run status

Get the current status of an agent run. Returns `input` (original input data), `result` (agent output on success), and `meta` (status, progress, timing).

Parameters

Responses

POST /public/api/v1/web/search

Web Search

Search the web using our internal search infrastructure. Returns up to 10 organic search results (title, snippet, URL) for a given query. If fewer results are available, returns as many as found. ## Pricing - **Flat rate: 0.005 credits per request** ($0.05 per 1,000 requests) ## Rate Limit - **400,000 requests per hour** per API key

Request Body

Request body required

Responses

POST /public/api/v1/billing/topup

Top up credits

> ⚠️ **Restricted access** — the billing API is enabled per organization. If you > get a `403`, request access via [email protected]. Buy credits and charge your organization's saved card immediately. Use this to refill programmatically when your balance runs low (poll `GET /public/api/v1/billing/balance` to decide when). Credits are priced at **1 credit = $0.01**, so `credits` is also the price in cents. ## Request body | Field | Type | Required | Notes | |-------|------|----------|-------| | `credits` | integer | yes | How many credits to buy. **Minimum 500** ($5), **maximum 100000** ($1,000) per call. | | `idempotency_key` | string | no (recommended) | Safe-retry token — see below. | There is also a **rolling-24h limit of 1,000,000 credits ($10,000) per organization** across all top-up calls — a safety cap. Above it the call returns `429` (see below). ### idempotency_key — how to use it (important for automated callers) If a request times out or errors and you're not sure it went through, **retry it with the same `idempotency_key`**: you'll be charged at most once. Use a **new, unique** key (e.g. a UUID) for each *new* top-up. If you omit it, every call is an independent charge — so an accidental double-call charges twice. ## Responses **200 — charged, credits already applied** ```json { "charged": true, "balance_updated": true, "credits_added": 5000, "new_balance": 5200 } ``` **200 — charged, credits still being applied** (re-check the balance endpoint shortly) ```json { "charged": true, "balance_updated": false, "credits_added": 5000, "new_balance": 200, "reason": "Payment captured. Credits are being applied..." } ``` **400 — could not charge a saved card** (no card on file, declined, or extra verification needed). Open `billing_url` (the Explee billing page), add a card, then retry the top-up. ```json { "charged": false, "billing_url": "https://explee.com/billing", "reason": "No saved card on file. Add a card on your billing page, then retry." } ``` **429 — daily top-up limit reached** ($10,000 / 24h per organization). Nothing was charged; retry later. ```json { "charged": false, "reason": "Daily top-up limit reached ($10,000 / 24h). Try again later." } ``` `422` — `credits` outside the 500–100000 range, or `idempotency_key` too long.

Request Body

Request body required

Responses

GET /public/api/v1/billing/balance

Get balance

> ⚠️ **Restricted access** — the billing API is enabled per organization. If you > get a `403`, request access via [email protected]. Return your organization's net credit balance — the same number shown on the Explee dashboard. Poll this to decide when to top up — when `remain` drops below the threshold you need for upcoming requests, call `POST /public/api/v1/billing/topup`. ## Response ```json { "remain": 4200 } ``` `remain` is the net balance in credits (1 credit = $0.01): prepaid funds minus any active postpaid (AutoGTM) usage that hasn't been collected yet. It can be **negative** if the org is carrying postpaid debt.

Responses

GET /public/api/v1/autogtm/projects

List AutoGTM projects

List the AutoGTM projects owned by your organization. Authenticated by the same `X-API-Key` as the rest of this API — the key resolves to your organization, and its active projects are returned newest first. **Response:** - `projects` — active projects with `id`, `domain` (the project's identity in the app), and `daily_budget_usd` (the project-wide daily budget anchor, `null` when not set). - `total` — number of projects returned. Use a project `id` to filter the campaigns list (`GET /public/api/v1/autogtm/campaigns?project_id=…`).

Responses

GET /public/api/v1/autogtm/campaigns

List AutoGTM campaigns

List the AutoGTM campaigns owned by your organization. Authenticated by the same `X-API-Key` as the rest of this API — the key resolves to your organization, and campaigns are returned across all of its projects. Pass `project_id` (see `GET /public/api/v1/autogtm/projects`) to get one project's campaigns only. **Response:** - `campaigns` — non-archived campaigns with `id`, `project_id`, `name`, `target_url`, `daily_limit_usd`, and lifecycle `status`. - `total` — number of campaigns returned. Use a campaign `id` to scope follow-up management calls (inbox replies, budget, start/stop, analytics).

Parameters

Responses

POST /public/api/v1/autogtm/campaigns/import

Create a campaign from your own leads (import)

Create a new campaign from your own lead list (async). Each call creates a **new** campaign in the given project — the campaign is built for your leads only (no agent lead discovery) and starts sending automatically once live, within your project budget. **How it works:** 1. Submit `project_id`, a campaign `name`, and up to 30,000 `leads` 2. Receive a `task_id` immediately 3. Poll `GET /public/api/v1/autogtm/campaigns/import/{task_id}` until `completed` 4. `result.campaign_id` is your new campaign — it appears in the campaigns list **Per lead** — mandatory: `email`, `first_name`, `last_name`, `company_domain`, `job_title`; recommended: `linkedin_url` (enriches the lead from Explee's contact base for a richer email). Leads missing mandatory fields are skipped (reported in the result), never a request-level error. Duplicates of leads already contacted in the project and do-not-contact entries are dropped. **Pricing:** the import itself is free. Emails are verified at send time at Explee's cost; sending is billed as usual.

Request Body

Request body required

Responses

GET /public/api/v1/autogtm/campaigns/import/{task_id}

Campaign import status/result

Check status and retrieve the result of a campaign import. **Statuses:** - `pending` — import in progress; `progress` shows the current stage. Poll every few seconds. - `completed` — done; `result.campaign_id` is the new campaign (or `null` if no leads survived validation/dedup — then no campaign was created). - `failed` — see `error`.

Parameters

Responses

GET /public/api/v1/autogtm/hot-leads

List hot leads

Every **hot lead** across your organization — people who replied to your outreach with real interest — newest first, as enriched contacts ready to map into CRM fields. **Polling:** pass `since` (ISO 8601) with your last poll time to get only leads that went hot after it — the Zapier/Make pattern. `became_hot_at` of the newest item is your next cursor. A lead leaves the feed when you dismiss it in the inbox («Not a hot lead») or when it later unsubscribes. To read the full conversation or answer: `GET /autogtm/campaigns/{campaign_id}/inbox/{person_id}` and `…/reply`.

Parameters

Responses

GET /public/api/v1/autogtm/campaigns/{campaign_id}/inbox

List inbox conversations

List the campaign's conversations (contacted people), most recent first. **Tabs:** `need_reply` — replies awaiting a human answer (start here), `replied` — everyone who replied, `sent` — everyone contacted. Omit for the full universe.

Parameters

Responses

GET /public/api/v1/autogtm/campaigns/{campaign_id}/inbox/{person_id}

Read a conversation thread

The full conversation with one contact: our outbound emails and their replies, in order, plus the reply gate (`can_reply`). To answer, POST to `/reply` with just your message text.

Parameters

Responses

POST /public/api/v1/autogtm/campaigns/{campaign_id}/inbox/{person_id}/reply

Reply in a thread

Reply to this contact — send just the message text. You reply to the **lead**, not to an email address: the server resolves the recipient, the subject (`Re: …`) and the threading from the contact's real conversation, so a reply can only ever go back to the person who wrote to you — never to an address you supply. The message goes through the same compliance gate and content moderation as replies sent from the app. Allowed only for a contact who has **replied** and isn't `unsubscribe` — otherwise `403`. You may send at most three replies per message the lead sent you (`429` if exceeded). The send is recorded under your organization's owner identity (an API key carries no user).

Parameters

Request Body

Request body required

Responses

GET /public/api/v1/autogtm/campaigns/{campaign_id}/inbox/{person_id}/note

Read the lead note

The team-shared note on this lead — the same free-text field your team sees and edits in the AutoGTM inbox (Lead info panel). `note` is `null` when no note has been set.

Parameters

Responses

POST /public/api/v1/autogtm/campaigns/{campaign_id}/inbox/{person_id}/note

Set the lead note

Set (or clear) the lead's team-shared note. The note is one shared text per lead, last-write-wins — a write here replaces what a teammate typed in the app, and vice versa. Pass `{"note": null}` (or an empty string) to clear it.

Parameters

Request Body

Request body required

Responses

GET /public/api/v1/autogtm/projects/{project_id}/budget

Get project daily budget

Read the project's total daily outreach budget (the anchor the dashboard slider sets). Per-campaign allocations are derived from this number automatically — see `daily_limit_usd` on each campaign in `GET /autogtm/campaigns`.

Parameters

Responses

PATCH /public/api/v1/autogtm/projects/{project_id}/budget

Set project daily budget

Set the project's total daily outreach budget. The change is spread across the project's active campaigns proportionally — exactly what the dashboard slider does. **`0` pauses all sending** while keeping campaigns configured (the explicit off switch).

Parameters

Request Body

Request body required

Responses

PATCH /public/api/v1/autogtm/campaigns/{campaign_id}/budget

Set campaign daily budget

Set one campaign's daily budget. The campaign's `daily_limit_usd` is set directly and the project's total daily budget follows as the sum of its running campaigns — the other campaigns are not touched (grow or shrink the total spend, not redistribute a fixed one). **Editable only while Autopilot is OFF** (`GET/PATCH …/projects/{id}/autopilot`): while it's ON the agent owns the split and this returns `409`. A paused / not-yet- running campaign holds no allocation, so setting its budget also returns `409` — resume it first. To set the whole-project budget instead, use `PATCH …/projects/{id}/budget`.

Parameters

Request Body

Request body required

Responses

POST /public/api/v1/autogtm/campaigns/{campaign_id}/stop

Stop campaign

Stop a campaign **from any status**, idempotently: interrupts a running lead search, cancels queued outreach emails, and freezes a pending review's auto-approve countdown. Nothing sends until you start it again.

Parameters

Responses

POST /public/api/v1/autogtm/campaigns/{campaign_id}/start

Start campaign

Re-enable a stopped campaign — the status-aware inverse of stop: - stopped while sending → resumes sending (requires an affordable balance); - stopped in review → un-freezes the review countdown; - stopped during lead search → re-runs the search. Already-running or archived campaigns are a no-op. **Note:** the AutoGTM autopilot also manages campaigns. If it is enabled for the project, it may later re-balance what you start or stop here.

Parameters

Responses

GET /public/api/v1/autogtm/campaigns/{campaign_id}/analytics

Campaign analytics

Campaign performance for a period: emails sent, replies, reply rate, hot leads, spend, cost per lead, budget allocation, the leads pool and the manual lead funnel. The same numbers the AutoGTM dashboard shows — use them to drive your own budget/start/stop decisions.

Parameters

Responses

GET /public/api/v1/autogtm/projects/{project_id}/analytics

Project analytics

Project-level performance for a period: the aggregate totals (emails, replies, reply rate, hot leads, spend) plus the per-campaign breakdown — exactly what the AutoGTM dashboard shows for a project. Both the totals and the per-campaign rows cover the requested `period`.

Parameters

Responses

GET /public/api/v1/autogtm/projects/{project_id}/autopilot

Get autopilot settings

Read the project's autopilot and auto-reply settings. - `autopilot_enabled` — the AutoGTM autopilot agent. While ON it manages campaigns and the per-campaign budget split (per-campaign budget edits return 409); turn it OFF to control budgets yourself. - `auto_reply_enabled` / `auto_reply_delay_minutes` — AI auto-reply behaviour. - `reply_cc_emails` — project defaults added to CC on every manual or automatic reply.

Parameters

Responses

PATCH /public/api/v1/autogtm/projects/{project_id}/autopilot

Set autopilot settings

Update the project's autopilot / auto-reply settings. Send only the fields you want to change — at least one is required. Turning `autopilot_enabled` **off** hands per-campaign budgets back to you (`PATCH …/campaigns/{id}/budget` stops returning 409); turning it **on** makes them agent-managed again.

Parameters

Request Body

Request body required

Responses

POST /public/api/v1/feedback

Send feedback

Send us freeform feedback about the API: request a feature, report a problem, tell us data is missing or wrong — anything, in your own words. The message lands directly with the Explee team. We read everything, though we don't guarantee an individual reply. No credits are charged.

Request Body

Request body required

Responses

GET /public/api/v1/dedup/people

People Lists

Responses

POST /public/api/v1/dedup/people

Create People List

Create an immutable list of people you already own. Pass the person objects returned by people search or find-and-enrich in `people`; they are matched by LinkedIn URL, normalized email, or first name + last name + company domain. Existing integrations can keep using the legacy `linkedin_urls` field. Pass the returned id in `exclude_lists` on people search — matched people are not returned and not charged. Lists can't be modified: to grow your ledger, create a new list per batch and pass all ids; consolidate periodically. URLs are normalized on write; unparseable values are skipped and reported. **Limits:** 100,000 values per list; per search request: 100 lists, 500,000 values combined.

Request Body

Request body required

Responses

GET /public/api/v1/dedup/people/{list_id}

Get People List

Returns the list metadata and all stored values.

Parameters

Responses

DELETE /public/api/v1/dedup/people/{list_id}

Delete People List

Removes the whole list. Excluded people will appear (and be charged) in search results again.

Parameters

Responses

GET /public/api/v1/dedup/companies

Companies Lists

Responses

POST /public/api/v1/dedup/companies

Create Companies List

Create an immutable list of companies you already own (`domain` values from search results). Pass the returned id in `exclude_lists` on companies search — matched companies are not returned and not charged. Lists can't be modified: to grow your ledger, create a new list per batch and pass all ids; consolidate periodically. Domains are normalized on write; unparseable values are skipped and reported. **Limits:** 100,000 values per list; per search request: 100 lists, 500,000 values combined.

Request Body

Request body required

Responses

GET /public/api/v1/dedup/companies/{list_id}

Get Companies List

Returns the list metadata and all stored values.

Parameters

Responses

DELETE /public/api/v1/dedup/companies/{list_id}

Delete Companies List

Removes the whole list. Excluded companies will appear (and be charged) in search results again.

Parameters

Responses