# Exa.ai People Search (`jobscrawler/exa-ai-people-search`) Actor

Search public professional profiles through Exa’s official keyless MCP service. Source-attributed index results with profile text and original links; no paid API key or synthetic fallback. Indexed search results are not direct LinkedIn recrawls.

- **URL**: https://apify.com/jobscrawler/exa-ai-people-search.md
- **Developed by:** [Jobs Scraper](https://apify.com/jobscrawler) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 63.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Exa.ai People Search

### Overview

Standalone Exa people-search API wrapper with secret-key handling and rich public-profile output. This folder is a complete, standalone Apify Actor. Its layout intentionally follows the workspace’s `jobright-scraper`: source code lives under `src/`, platform definitions live under `.actor/`, the container is defined by `Dockerfile`, and the dataset can be checked with `dataset-validator.js`. It does not import code from another scraper folder, so it can be pushed, built, scheduled, or versioned independently.

The live extraction path is Exa’s official Search API with `category: people`. Live runs require `exaApiKey` (or `EXA_API_KEY`). Credentials are sent only in the relevant request header. The Actor never prints them, stores them in the dataset, or writes them to diagnostics.

### Extraction design

The Actor prefers structured public data. It normalizes source records into a stable job/profile contract and also retains every upstream field inside `rawData`. Null and undefined source values are removed recursively. Normalized fields use empty strings, empty arrays, or empty objects when an optional value is not published, so dataset records contain no JSON `null` values. This is deliberate: an empty value means “not supplied by the source,” not an invented fact.

For job records, the output can include identifiers, title, company, all locations, salary range and unit, employment type, workplace mode, dates, description text and HTML, responsibilities, requirements, benefits, skills, taxonomy, education, language, visa information, application links, recruiter data, organization enrichment, funding, classifications, and structured geocoding. Source-specific fields that do not have a normalized column remain available in `rawData`.

### Inputs

The full machine-readable input contract is in `.actor/input_schema.json`. Every Actor defaults to a maximum of ten results and enforces a minimum request of ten. Depending on this source, useful fields include search terms, location, time window, start URLs, concurrency, richer-description toggles, and secret credentials. Advanced filters are passed only as documented primitive query parameters; the Actor does not execute arbitrary code from input.

### Local development and verification

Install dependencies and run a ten-record smoke test:

```bash
npm install
apify run --purge --input '{"sampleMode":true,"maxItems":10}'
npm run validate:data
```

A source-live example is:

```bash
apify run --purge --input '{"exaApiKey":"<secret>","query":"Senior software engineers in Berlin","maxItems":10}'
npm run validate:data
```

The optional `sampleMode` uses real public GitHub profile records as a credential-free structural smoke test. The records are labelled `public_structural_smoke`; they are not represented as Exa results.

After each run, inspect `storage/datasets/default` for records and `storage/key_value_stores/default/RUN_SUMMARY.json` for machine-readable status. `SOURCE_METADATA` records the provider, source counts, and mode. A failure creates a sanitized `ERROR_DIAGNOSTIC`; it never creates fake fallback jobs or silently converts an access error into `no_results`.

### Production behavior

Push this folder as its own Actor with `apify push`. Configure secrets in Apify’s encrypted input or environment settings, not in source files. The output schema exposes direct links to the dataset, run summary, and diagnostic store. Deduplication uses a stable source ID or canonical URL. HTTP requests use bounded timeouts and limited retry with backoff for temporary network or rate-limit failures.

The Actor reads public job or profile information only. Availability and field completeness depend on what the upstream source publishes at run time. Empty optional fields are preserved honestly; values are never guessed. Respect the source’s terms, applicable privacy rules, API plan limits, and retention requirements when scheduling or redistributing output.

### Source identity

- Standalone actor: `exa-ai-people-search`
- Original catalog reference ID: `uf4Y3fzLfSWsnHsn5`
- Provider mode: `exa`
- Source/platform: `Exa.ai`
- Record type: `person`
- Default output request: `10`

## Keyless Exa mode (2026-10-21)

The default `dataSource: "mcp"` performs a fresh people search through Exa's official public MCP service at https://mcp.exa.ai/mcp, documented at https://exa.ai/mcp. It needs no API key and returns up to 20 public LinkedIn professional profiles. The service's free usage and availability limits apply; rate limits fail explicitly without evasion or sample fallback.

Results are live queries of Exa's search index, not direct LinkedIn recrawls or independently verified employment claims. `dataMode` is `live_exa_index`, `extractionStatus` is `indexed_profile`, and source timestamps and endpoint are retained. Missing fields remain empty. Query and location influence retrieval; they are not guaranteed exact filters. No private contact details are requested.

For keyed `/search` features choose `dataSource: "api"` and supply your own Exa key. API-only search type/highlight controls do not apply to MCP mode. `includeText` and `maxCharacters` control the returned text in both modes. Sample mode is rejected. Older API instructions below apply to API mode only.

# Changelog

This Actor's version history is a separate document: https://apify.com/jobscrawler/exa-ai-people-search/changelog.md

# Actor input Schema

## `dataSource` (type: `string`):

MCP uses the official no-key service with free-service limits. API requires your own Exa key. Indexed profiles are not direct LinkedIn recrawls.

## `exaApiKey` (type: `string`):

Secret Exa key used only in the x-api-key header. It is never logged or stored.

## `query` (type: `string`):

Natural-language description of the professionals to find.

## `userLocation` (type: `string`):

Two-letter country code used to bias results.

## `maxItems` (type: `integer`):

Maximum profiles to return. Keyless MCP mode caps at 20 and may return fewer results.

## `searchType` (type: `string`):

Keyed API mode only. MCP uses the service's default search.

## `includeText` (type: `boolean`):

Ask Exa to return available public profile text.

## `maxCharacters` (type: `integer`):

Maximum profile text length per result.

## `highlightsQuery` (type: `string`):

Optional topic used to select relevant public-profile highlights.

## `numSentences` (type: `integer`):

Maximum sentences in each requested highlight.

## `highlightsPerUrl` (type: `integer`):

Maximum highlights requested for each profile.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout in seconds.

## Actor input object example

```json
{
  "dataSource": "mcp",
  "query": "Senior software engineers in the United States",
  "userLocation": "US",
  "maxItems": 10,
  "searchType": "auto",
  "includeText": true,
  "maxCharacters": 2000,
  "highlightsQuery": "",
  "numSentences": 3,
  "highlightsPerUrl": 3,
  "requestTimeoutSecs": 45
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing normalized non-null records plus complete cleaned rawData.

## `runSummary` (type: `string`):

Machine-readable status, data provenance mode, counts, timing, and null validation.

## `files` (type: `string`):

Key-value store containing source metadata and any sanitized error diagnostic.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "Senior software engineers in the United States"
};

// Run the Actor and wait for it to finish
const run = await client.actor("jobscrawler/exa-ai-people-search").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "query": "Senior software engineers in the United States" }

# Run the Actor and wait for it to finish
run = client.actor("jobscrawler/exa-ai-people-search").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "Senior software engineers in the United States"
}' |
apify call jobscrawler/exa-ai-people-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobscrawler/exa-ai-people-search"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/zffK5vfR1ZLhoHZpf/builds/dzIRpya6GSBEz7Tyf/openapi.json
