> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.client-p.pylote.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner search

> 2-stage flow — anonymized search, then full profile retrieval

## Overview

On top of the [full synchronization](/en/integration-guide) (`GET /freelances`),
partners can query the Pylote pool **on demand**, in 2 stages:

1. **Stage 1 — Anonymized search** (`POST /partners/search`): your query is ranked
   by our matching engine. You receive **partial, anonymized** profiles: enough to
   assess relevance, nothing to identify or contact.
2. **Stage 2 — Reveal** (`POST /partners/reveal`): given the `id` of a result, you
   receive the full profile (JSON Resume format, identical to `GET /freelances`).
   This is when the freelancer is notified of the view (a `profile_view` event is
   emitted automatically on Pylote's side).

This separation lets you implement your own credit logic: consumption only
happens at Stage 2.

## Stage 1 — Search

```javascript theme={null}
const response = await axios.post(
  'https://client-p.pylote.io/partners/search',
  {
    keywords: 'react "design system" -wordpress',
    skills: ['React', 'TypeScript'],                      // AND: all required (name from the Skills referential)
    workAreas: ['Île-de-France'],                          // OR (name from the Regions referential)
    seniority: ['senior_6-10_ans', 'master_10_ans'],       // OR (slug from the Seniority referential)
    available: true,
    page: 1,
    limit: 50,
  },
  { headers: { 'x-api-key': API_KEY } },
);
// response.data.freelances : anonymized profiles ranked by relevance
// response.data.infos      : { total, page, limit, totalPages }
```

### `keywords` semantics

| Syntax            | Effect                              |
| ----------------- | ----------------------------------- |
| `react node`      | Implicit AND: both terms must match |
| `"design system"` | Exact phrase                        |
| `-wordpress`      | Exclusion                           |
| OR                | Not supported at this stage         |

### Filters and referential

Filters are **exact matches** (no typo tolerance): an out-of-referential value
does not raise an error, it simply matches no profile. Fetch the accepted
values from the public `POST /referential` endpoint:

```javascript theme={null}
const { data } = await axios.post('https://client-p.pylote.io/referential', {
  action: 'list:all',
  field: 'Seniority',   // or 'Skills', 'Regions'
});
// -> [{ name: 'Senior 6-10 ans', slug: 'senior_6-10_ans', ... }, ...]
```

Mind which field to send for each filter:

| Filter      | Referential table | Field to send | Example           |
| ----------- | ----------------- | ------------- | ----------------- |
| `skills`    | `Skills`          | `name`        | `React`           |
| `workAreas` | `Regions`         | `name`        | `Île-de-France`   |
| `seniority` | `Seniority`       | **`slug`**    | `senior_6-10_ans` |

Geography is filtered by mobility areas (regions, countries), not by zip code

* radius: map your target areas to our regions.

### What a result contains

Each Stage 1 profile contains: `id` (the Stage 2 key), `headline`, `summary`,
location (city/region/country/zip), skills, languages, seniority, professions,
availability, work preferences, work history (titles, durations and
descriptions, **without** an employer field), and `matchRank`.

All free text (profile and mission `summary`) is **redacted**: emails, phone
numbers, URLs and the freelancer's own name are removed. These are texts
written by the freelancer: a company name may occasionally appear in the prose.

`matchRank` is the profile's **absolute rank** in the relevance ranking
(1 = best match). It is a real rank, not a percentage score.

What Stage 1 never contains: name, email (even proxied), phone, profile links
(resume, LinkedIn), structured employer field.

## Stage 2 — Reveal a profile

```javascript theme={null}
const response = await axios.post(
  'https://client-p.pylote.io/partners/reveal',
  {
    freelanceId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', // id from Stage 1
    recruiterEmail: 'marie.gilles@lutessa.com',           // must be whitelisted
  },
  { headers: { 'x-api-key': API_KEY } },
);
// response.data : full profile in JSON Resume format
```

Good to know:

* `recruiterEmail` must be in your [whitelist](/en/tracking-obligations).
  Otherwise: 404. This is what feeds the "{company} via {partner}" line the
  freelancer sees in their stats.
* The `profile_view` event is emitted automatically: do not call
  `POST /partners/events` for the view itself. Keep it for granular actions
  (click\_cv, click\_linkedin, add\_favorite...).
* Contact goes through the **Pylote proxy email** and the **tracked LinkedIn
  link** of the revealed profile. Phone numbers are not exposed through the
  partner channel.
* An `opt_out` recruiter cannot reveal a profile (403): a reveal exposes
  contact details, which requires tracking.

## Rate limiting

120 requests/minute per API key, across all routes. Beyond that: `429`.
One search = one request, whatever the `limit`.
