# Piiple AI Candidate Sourcing & Recruiting Agent API (`piriai/piiple-candidate-pack-api`) Actor

Find candidates across LinkedIn, recruitment platforms, and the open web with Piiple's AI sourcing agent. Search by role, location, skills, and company. Get structured profiles with screening scores and evaluation reasons. No separate Piiple account or API key required.

- **URL**: https://apify.com/piriai/piiple-candidate-pack-api.md
- **Developed by:** [Piri AI](https://apify.com/piriai) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $56.00 / 1,000 evaluated candidates

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## AI sourcing and recruiting agent for LinkedIn and the open web

Piiple Candidate Pack API is an **AI candidate sourcing and recruiting agent API**. Its AI recruiting agent searches **LinkedIn, recruitment platforms, and the open web** to find professional profiles and evaluate them against your hiring criteria.

Define the job titles, locations, target companies, skills, and requirements that matter to your search. Receive structured candidate profiles with profile links, evaluation scores, and reasons for qualification or elimination. Recruiters, talent acquisition teams, and developers can use these results to review potential hires and build candidate shortlists.

You do not need a separate Piiple account or API key. Each run saves all completed candidate evaluations to the default Apify Dataset and a job summary to the key-value store's `OUTPUT` record.

### Candidate sourcing and screening features

- **Find candidates by hiring criteria:** search by role, location, skills, and target employers.
- **Evaluate candidate fit:** assess profiles against your must-have and nice-to-have requirements.
- **Review the reasoning:** see qualification or elimination reasons, evaluation scores, and missing evidence.
- **Access all completed evaluations:** review both qualified and eliminated profiles in the Dataset.
- **Find more candidates:** continue a completed search using its Piiple job ID and saved filters. This works only for jobs created by this Actor under the same Apify account.
- **Track search outcomes:** check evaluated candidate counts, qualified candidate counts, issues, and completion details in `OUTPUT`.

### Recruiting use cases

#### LinkedIn candidate search by role and skills

Find professional profiles for a specific hiring need, such as backend engineers in Istanbul with Node.js and PostgreSQL experience. Review how each evaluated profile matches your requirements.

#### Talent sourcing from target companies

Focus your candidate search on target employers, required previous companies, industry, or company size. Combine employer filters with role and location criteria to define the talent pool you want to search.

#### Open web recruiting and candidate evaluation

Discover professionals across recruitment platforms and the open web, then use the completed evaluations to build a shortlist. Review the available profile evidence and the reasons each candidate qualifies or is eliminated.

### How to find candidates with Piiple

1. Define your ideal candidate profile, or **ICP**, using roles, locations, skills, and hiring requirements.
2. Set `scan_limit` to the maximum number of candidates you want evaluated, from 1 to 1000, and ensure the Apify run spending limit covers the requested candidate evaluations.
3. Run the Actor and review the default Dataset. Filter `evaluation.status` by `qualified` to see matching candidates.
4. Check `OUTPUT` for search counts and outcome details. To continue a completed search, use its `jobId` in a Find more run.

### Input

For a new search, provide `icp` and `scan_limit`. The example below is illustrative and contains no real candidate data.

```json
{
  "icp": {
    "roles": ["Backend Engineer", "Software Engineer"],
    "role_scope": "current_only",
    "locations": [
      { "country_code": "TR", "city": "Istanbul" }
    ],
    "target_companies": [],
    "company_scope": "current_only",
    "skills": ["Node.js", "PostgreSQL"],
    "must_haves": [
      { "id": "production-backend", "text": "Professional experience building production backend systems" }
    ],
    "nice_to_haves": []
  },
  "scan_limit": 10
}
```

`scan_limit` is the maximum number of candidates to evaluate, not the number of qualified candidates guaranteed to be returned. The Actor input accepts values from 1 to 1000; some evaluation attempts may fail, so the completed and billable total can be lower. The technical field remains named `scan_limit`. Search results depend on the criteria and available profile evidence; a successful run may return fewer candidates or no matches.

Use these optional filters to refine your candidate search:

| ICP field | Meaning |
| --- | --- |
| `past_job_titles` | Required prior-role alternatives, in addition to the main role requirement. |
| `required_past_companies` | Required prior-employer alternatives; entries accept `name` and/or `linkedin_url`. |
| `current_company_tenure` | Current-company tenure bounds, e.g. `{ "min_years": 2, "max_years": 5 }`. |
| `minimum_role_duration_months` | Minimum average tenure across merged employment stints, in months. |
| `company_industries` | Employer industry constraints. |
| `company_headcount_ids` | Current-employer size buckets: A self-employed, B 1–10, C 11–50, D 51–200, E 201–500, F 501–1,000, G 1,001–5,000, H 5,001–10,000, I 10,001+. |
| `recently_changed_jobs` | Set to `true` to search for people who changed their current role in the last 90 days. |
| `exclusions` | Describe profiles or characteristics to exclude from your search. |

All these fields are optional. Use clear company names and requirements; if a company or requirement cannot be resolved, you may need to revise your input. Find more preserves your saved filters.

#### Find more candidates

After a completed run, copy its `jobId` from `OUTPUT` and start another run with that ID and a new candidate evaluation limit. The saved ICP is reused; you do not need to send it again.

```json
{
  "parent_job_id": "00000000-0000-4000-8000-000000000000",
  "scan_limit": 10
}
```

Each Find more run is a separate search. Review the price shown by Apify before starting it.

### Results

Profile photos are archived to Piiple storage when possible. If a photo cannot be archived, its original source URL is retained; photo storage failures do not fail the candidate search. Existing saved results are not backfilled.

New evaluations also include `profile_image_url`, `experience[]` (all available jobs, including simultaneous current positions), `education[]`, and `total_experience_months`. Employment records include title, company, LinkedIn company URL, partial start/end dates, current status and duration in months. Education records include school, LinkedIn school URL, degree, field of study and partial dates. Dates use `{year, month, day}`; unknown parts remain null. Current roles have a null end date. Job durations are inclusive calendar months and remain null without sufficient date evidence or for reversed date ranges. `duration_text` preserves the original provider duration label separately, even when numeric duration cannot be calculated. Total experience uses the shared non-overlapping career calculation; partial source dates follow that calculation's conventions. Missing histories are empty arrays and unavailable values are null. Previously saved results may omit these added fields. Existing fields and evaluation/billing semantics are unchanged.

The default Dataset contains one item per completed billable candidate evaluation, including eliminated profiles. `evaluation.status` is `qualified`, `rejected`, or `insufficient_evidence`; the last two are eliminated outcomes. `evaluation.summary` explains the outcome, `evaluation.criteria` contains assessed requirements, and `evaluation.missing_evidence` identifies required criteria without sufficient evidence. Filter by `qualified` to see matching candidates only. Typical profile fields include `full_name`, `linkedin_url`, `headline`, `location`, `current_position`, `total_experience_years`, and `provenance`.

For example, 10 completed billable evaluations can produce 3 qualified and 7 eliminated profiles: all 10 are accessible in the Dataset. Technical failures and candidates skipped before evaluation are not billed as completed evaluations.

The `OUTPUT` key-value record contains status, candidate evaluation limits, counters, outcome, issues, and a support reference. `returnedCount` counts qualified candidates; `exportedProfileCount` counts all exported evaluations. `rejectedCount` and `insufficientEvidenceCount` report eliminated outcomes. Cancelled and failed runs may contain completed evaluations as partial output; failed runs retain their error status. Evaluated candidates are delivered and billed progressively while the Actor runs. After abrupt termination, evaluated candidates already written remain in the Dataset. Resurrect the same run to recover any remaining results.

Example `OUTPUT` record (illustrative):

```json
{
  "status": "completed",
  "jobId": "00000000-0000-4000-8000-000000000000",
  "requestedScanLimit": 10,
  "effectiveScanLimit": 10,
  "partial": false,
  "returnedCount": 3,
  "exportedProfileCount": 10,
  "scannedCount": 10,
  "qualifiedCount": 3,
  "rejectedCount": 6,
  "insufficientEvidenceCount": 1,
  "chainTotals": {
    "scanned_count": 10,
    "enriched_count": 10,
    "rejected_count": 6,
    "qualified_count": 3,
    "discovered_count": 25,
    "enrichment_failed_count": 0,
    "evaluation_failed_count": 0,
    "evaluation_completed_count": 10,
    "insufficient_evidence_count": 1
  },
  "outcome": {
    "coverage": "limited",
    "stop_reason": "scan_limit_reached",
    "match_outcome": "matches_found",
    "warning_codes": [],
    "scan_completion": "complete"
  },
  "issues": [],
  "supportReference": null,
  "completedAt": "2026-10-16T07:00:00.000Z",
  "billing": {
    "provider": "Apify",
    "eventName": "candidate-scan",
    "status": "settled",
    "billableScanCount": 10,
    "chargedEventCount": 10
  }
}
```

Example Dataset item (illustrative; nested values depend on available evidence):

```json
{
  "id": "example-candidate-id",
  "full_name": "Example Candidate",
  "linkedin_url": "https://example.invalid/profile",
  "headline": "Backend Engineer",
  "location": { "city": "Istanbul", "country_code": "TR" },
  "current_position": { "title": "Backend Engineer", "company_name": "Example Company" },
  "total_experience_years": 5,
  "evaluation": {
    "status": "qualified",
    "score": 90,
    "summary": "The profile matches the backend role and provides evidence of production backend development.",
    "criteria": [
      {
        "id": "icp.target_role",
        "kind": "required",
        "source": "model",
        "status": "passed",
        "evidence_summary": "The current Backend Engineer title matches the requested role."
      }
    ],
    "missing_evidence": []
  },
  "provenance": {},
  "piiple_job_id": "00000000-0000-4000-8000-000000000000"
}
```

### Price per evaluated candidate

The price per evaluated candidate depends on your Apify subscription plan:

| Apify plan | Price per evaluated candidate | Price per 1,000 evaluated candidates |
| --- | --- | --- |
| Free | $0.060 | $60.00 |
| Starter (Bronze) | $0.060 | $60.00 |
| Scale (Silver) | $0.058 | $58.00 |
| Business (Gold) | $0.056 | $56.00 |

You pay for each completed candidate evaluation, including candidates who do not qualify (`rejected` or `insufficient_evidence`). Unfinished or failed evaluations are not charged. Apify platform usage is included in the customer price. Unused evaluation budget is not charged; a completed search with no qualified matches may still have billable evaluated candidates. For example, 10 completed evaluations cost $0.60 on Free/Starter, $0.58 on Scale, or $0.56 on Business, even if only 3 profiles qualify. Review the applicable price shown by Apify before starting a run.

The run spending limit must cover the entire requested `scan_limit` at your plan's price per evaluated candidate. Otherwise, the Actor fails before starting a Piiple job; it never silently lowers your requested candidate evaluation limit. Increase the run spending limit or explicitly lower `scan_limit` to retry. For example, `scan_limit: 1000` requires a $60 event budget on Free/Starter, $58 on Scale, or $56 on Business. This check uses the Apify run spending limit, not a direct query of your account credit balance; Apify controls account-level credit availability.

Completed candidate evaluations are written to the Dataset before their evaluation charge is recorded, including eliminated profiles. Charges are recorded progressively as evaluations are delivered. If the search later fails or is cancelled, completed billable work is still charged; unfinished or failed evaluations are not. A job with zero completed candidate evaluations has no evaluation charge.

On resurrection, the Actor checks previously confirmed charges against delivered evaluated candidates to avoid charging the same work twice. If a previous charge outcome cannot be verified, it stops further charging and writes a `BILLING_RECONCILIATION_REQUIRED` record for recovery. Without resurrection, an abrupt stop may leave the last completed evaluations undelivered and uncharged.

### Frequently asked questions

#### Does Piiple find candidates on LinkedIn and the open web

Yes. Piiple's AI sourcing agent searches LinkedIn, recruitment platforms, and the open web for professional profiles relevant to your hiring criteria. Source coverage and returned profile details depend on available evidence.

#### Do I need a separate Piiple account or API key

No. You can run this Actor through Apify without a separate Piiple account or Piiple API key.

#### Does the Dataset include candidates who do not qualify

Yes. The Dataset includes all completed billable candidate evaluations, including rejected profiles and profiles with insufficient evidence. Filter `evaluation.status` by `qualified` to create your shortlist.

#### Does Piiple find email addresses or send recruiting messages

This Actor sources and evaluates candidates. It does not find or verify email addresses or phone numbers, send messages, or perform outreach.

#### Why can a search return fewer qualified candidates than the evaluation limit

`scan_limit` limits the number of candidates submitted for evaluation. It does not guarantee a number of qualified candidates. Results depend on your requirements and the available profile evidence; a successful run may return fewer candidates or no qualified matches.

#### Failed runs

Handled errors write `status: "failed"` and an `error` object to `OUTPUT`, with `code`, `message`, `stage` and `action`. API validation errors also include field `details` and `httpStatus`. Previously delivered profiles remain in the Dataset; existing counters, issues and billing information are preserved. `backendStatus` describes the last known Piiple job status, separately from the failed Actor operation.

`jobStarted` is `false` when no job was started, `true` when a job is known, and `null` when the creation request outcome could not be confirmed. For an unknown creation outcome or delivery/billing interruption, keep the same run and use **Resurrect** as instructed in `error.action`; starting a new run can create a separate sourcing job. Billing with `status: "reconciliation_required"` needs recovery, and `chargedEventCount` is the last confirmed checkpoint, not a fresh charge verification.

If Apify terminates the process abruptly, or its storage is unavailable, the Actor may be unable to save a new error record. Check the run Log and keep the run storage for recovery.

Insufficient run budget produces a failed run and a machine-readable `OUTPUT` record in the default key-value store:

```json
{
  "status": "failed",
  "jobStarted": false,
  "requestedScanLimit": 1000,
  "effectiveScanLimit": 0,
  "error": {
    "code": "INSUFFICIENT_RUN_BUDGET",
    "message": "The Apify run spending limit does not cover the requested 1000 candidate evaluations. Increase the run spending limit or explicitly lower scan_limit and try again. No Piiple job was started.",
    "requestedScanLimit": 1000,
    "affordableScanCount": 500
  }
}
```

The output schema's **Piiple job summary** link points to this record. API clients can retrieve `OUTPUT` from the run's `defaultKeyValueStoreId` after the run fails. `affordableScanCount` reflects the run spending limit; it is not the account credit balance (and is `null` if the SDK cannot provide a finite count). No sourcing job or evaluation charge is created for this failure.

### Privacy and support

Use candidate data only for lawful recruiting purposes and follow applicable privacy and platform rules. The example input above is synthetic. For help, include the Apify run ID and the `supportReference` from `OUTPUT`; do not include API keys or other secrets in support requests.

# Actor input Schema

## `icp` (type: `object`):

Required for a new pack. Supports past_job_titles, required_past_companies, current_company_tenure (min_years/max_years), minimum_role_duration_months, company_industries, company_headcount_ids (A-I), recently_changed_jobs and exclusions, in addition to roles, locations and existing criteria. For find more, enter parent_job_id instead; the saved ICP is reused.

## `scan_limit` (type: `integer`):

Required. Maximum 1000 candidates to evaluate per run. Completed evaluations are billable, including candidates who do not qualify. Failed or unfinished evaluations are not charged. This is not a guarantee of qualified matches; the technical field is scan_limit.

## `parent_job_id` (type: `string`):

When set, creates a continuation from this completed Piiple job and reuses its saved ICP. Completed candidate evaluations in the new run are billed separately.

## Actor input object example

```json
{
  "icp": {
    "roles": [
      "Backend Engineer"
    ],
    "locations": [
      {
        "country_code": "TR",
        "city": "Istanbul"
      }
    ],
    "skills": [
      "Node.js"
    ],
    "must_haves": [
      {
        "id": "production",
        "text": "Production backend development experience"
      }
    ]
  },
  "scan_limit": 1
}
```

# Actor output Schema

## `candidates` (type: `string`):

No description

## `jobSummary` (type: `string`):

No description

# 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 = {
    "icp": {
        "roles": [
            "Backend Engineer"
        ],
        "locations": [
            {
                "country_code": "TR",
                "city": "Istanbul"
            }
        ],
        "skills": [
            "Node.js"
        ],
        "must_haves": [
            {
                "id": "production",
                "text": "Production backend development experience"
            }
        ]
    },
    "scan_limit": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("piriai/piiple-candidate-pack-api").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 = {
    "icp": {
        "roles": ["Backend Engineer"],
        "locations": [{
                "country_code": "TR",
                "city": "Istanbul",
            }],
        "skills": ["Node.js"],
        "must_haves": [{
                "id": "production",
                "text": "Production backend development experience",
            }],
    },
    "scan_limit": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("piriai/piiple-candidate-pack-api").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 '{
  "icp": {
    "roles": [
      "Backend Engineer"
    ],
    "locations": [
      {
        "country_code": "TR",
        "city": "Istanbul"
      }
    ],
    "skills": [
      "Node.js"
    ],
    "must_haves": [
      {
        "id": "production",
        "text": "Production backend development experience"
      }
    ]
  },
  "scan_limit": 1
}' |
apify call piriai/piiple-candidate-pack-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,piriai/piiple-candidate-pack-api"
        }
    }
}
```

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/FWD3j6ridgHLT5c2y/builds/NJ0P5e1Vrh3kqBfhH/openapi.json
