> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pubrio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Vue d'ensemble des filtres

> Comment fonctionnent les filtres de recherche Pubrio — le moteur de filtres unifié, la sémantique AND/OR, et quand utiliser lequel.

Les endpoints de recherche de Pubrio (`/companies/search`, `/people/search`, `/companies/advertisements/search`) partagent un moteur de filtres unique. Vous composez un corps de requête une seule fois et les mêmes règles s'appliquent à tous les endpoints — y compris la façon dont les filtres à valeurs multiples se combinent, la façon dont les lieux sont mis en correspondance, et la façon dont vous remplacez l'opérateur par défaut avec `filter_conditions`.

## Pourquoi un moteur de filtres unifié ?

<CardGroup cols={2}>
  <Card title="Un seul schéma, trois endpoints" icon="arrows-rotate">
    Les filtres au niveau entreprise comme `technologies`, `verticals` et `founded_dates` fonctionnent de façon identique sur `/companies/search`, `/people/search`, et à l'intérieur des `company_filters` des Monitors — vous les apprenez une seule fois.
  </Card>

  <Card title="AND/OR par filtre" icon="code-merge">
    La valeur par défaut est OR (correspond à au moins un). Passez des filtres individuels en AND (correspond à tous) en ajoutant une seule entrée à `filter_conditions` — sans toucher au reste du corps de la requête.
  </Card>

  <Card title="Opérateurs natifs Postgres" icon="database">
    Les filtres de tableau se compilent en opérateurs Postgres natifs — `&&` (chevauchement) pour OR, `@>` (contient) pour AND. Compatibles avec les index, sans post-filtrage côté application.
  </Card>

  <Card title="Mêmes filtres dans les Monitors" icon="bell">
    Le bloc `company_filters` des [Monitors](/fr/developer-guides/introduction) accepte la même structure, de sorte qu'un payload de recherche fonctionnel est également un payload de monitor fonctionnel.
  </Card>
</CardGroup>

***

## Anatomie d'une requête de recherche

Chaque requête de recherche est construite à partir de trois couches dans le même corps JSON :

| Couche                    | Où elle se trouve                                                                                        | Exemples                                                                       |
| ------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Filtres personnes         | clés de premier niveau                                                                                   | `people_titles`, `management_levels`, `departments`, `people_locations`        |
| Filtres entreprise        | imbriqués sous `company_filters: {...}` (recommandé) — également accepté au premier niveau               | `technologies`, `verticals`, `founded_dates`, `employees`, `company_locations` |
| Remplacements d'opérateur | tableau `filter_conditions` (à l'intérieur de `company_filters` lors du remplacement de clés entreprise) | `[{ "key": "technologies", "operator": "and" }]`                               |

Une requête `/people/search` minimale utilisant les trois couches :

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.pubrio.com/people/search \
    -H "Content-Type: application/json" \
    -H "pubrio-api-key: YOUR_API_KEY" \
    -d '{
      "people_titles": ["VP of Engineering", "CTO"],
      "company_filters": {
        "technologies": ["Kubernetes", "Docker"],
        "is_enable_similarity_search": true,
        "company_locations": ["US"]
      },
      "per_page": 25,
      "page": 1
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.pubrio.com/people/search",
      headers={
          "Content-Type": "application/json",
          "pubrio-api-key": "YOUR_API_KEY",
      },
      json={
          "people_titles": ["VP of Engineering", "CTO"],
          "company_filters": {
              "technologies": ["Kubernetes", "Docker"],
              "is_enable_similarity_search": True,
              "company_locations": ["US"],
          },
          "per_page": 25,
          "page": 1,
      },
  )
  print(response.json())
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.pubrio.com/people/search", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "pubrio-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      people_titles: ["VP of Engineering", "CTO"],
      company_filters: {
        technologies: ["Kubernetes", "Docker"],
        is_enable_similarity_search: true,
        company_locations: ["US"],
      },
      per_page: 25,
      page: 1,
    }),
  });
  console.log(await response.json());
  ```
</CodeGroup>

***

## `company_filters` : regrouper les clés au niveau entreprise

L'objet enveloppant `company_filters: {...}` est la façon recommandée d'envoyer les filtres au niveau entreprise — il sépare visuellement les clés qui filtrent la *personne* de celles qui filtrent l'*entreprise*, et correspond à la structure déjà utilisée par les [Monitors](/fr/developer-guides/introduction), de sorte que les payloads se transfèrent proprement entre les configurations de recherche et de monitor.

Les deux styles fonctionnent ; le moteur aplatit la forme enveloppée au premier niveau avant traitement, et **les clés de premier niveau l'emportent en cas de conflit** :

<CodeGroup>
  ```json Wrapped (recommended) theme={null}
  {
    "people_titles": ["VP of Engineering"],
    "company_filters": {
      "technologies": [37, 152],
      "founded_dates": [2015, 2023],
      "company_locations": ["US"]
    }
  }
  ```

  ```json Flat (also works) theme={null}
  {
    "people_titles": ["VP of Engineering"],
    "technologies": [37, 152],
    "founded_dates": [2015, 2023],
    "company_locations": ["US"]
  }
  ```
</CodeGroup>

Lorsque vous ajoutez un remplacement `filter_conditions` pour une clé au niveau entreprise, placez-le **à l'intérieur** de `company_filters` afin qu'il voyage avec les clés qu'il remplace.

### Même structure sur les variantes `/search/similar`

`POST /companies/search/similar` et `POST /people/search/similar` acceptent le **même corps de filtres** que leurs équivalents non « similar » (y compris l'enveloppe `company_filters` et `filter_conditions`). Chacun ajoute une étape de similarité par-dessus :

|                             | Ce qu'ils nécessitent en plus                                                                                                   | Ce que vous obtenez en plus                                                                                      |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `/companies/search/similar` | Une entreprise de référence — `domain_search_id`, `domain`, `linkedin_url`, ou `domains`                                        | Chaque résultat obtient un `similarity_score` (float, 0-1) et les lignes sont triées par similarité décroissante |
| `/people/search/similar`    | Une personne/un titre de référence — l'un de `people_titles`, `people_search_id`, `linkedin_url`, `linkedin_urls`, ou `peoples` | Idem — `similarity_score` par ligne, trié par similarité                                                         |

L'enveloppe de réponse est par ailleurs identique à celle de l'endpoint `search` standard. Les filtres réduisent le vivier de candidats *avant* l'application du classement par similarité — combiner `company_locations: ["US"]` avec `/people/search/similar` renvoie donc les personnes basées aux États-Unis les plus proches de vos titres de référence, ce qui correspond au schéma « trouver plus de personnes comme X dans ces contraintes ».

<Note>
  Contrairement aux endpoints `/search` standard, `/search/similar` ne renvoie **pas** de `pagination.total_entries` exact — la valeur est plafonnée car la recherche par similarité classe les résultats par pertinence et ne fait remonter que les meilleures correspondances. Utilisez la recherche par similarité pour trouver les *meilleures* correspondances, pas pour énumérer chacune d'entre elles.
</Note>

***

## AND ou OR — la seule décision à prendre par filtre

Les filtres à valeurs multiples (`technologies`, `verticals`, `keywords`, `categories`, …) acceptent un tableau. L'opérateur détermine ce que signifie « correspondance » :

<Tabs>
  <Tab title="OR (par défaut)">
    **Correspond à au moins une valeur.** Renvoie les lignes dont le tableau chevauche l'entrée.

    ```json theme={null}
    {
      "technologies": ["Python", "PostgreSQL", "Kubernetes"],
      "is_enable_similarity_search": true
    }
    ```

    Une entreprise est incluse si sa stack technologique contient **au moins une** des technologies `Python`, `PostgreSQL` ou `Kubernetes`. Se compile en `column && ARRAY[...]` Postgres.

    À utiliser quand : vous voulez une large couverture — « intéressé par *n'importe lequel* de ces éléments », « situé dans *n'importe lequel* de ces pays ».
  </Tab>

  <Tab title="AND">
    **Correspond à toutes les valeurs.** Renvoie les lignes dont le tableau contient chaque valeur d'entrée.

    ```json theme={null}
    {
      "technologies": [37, 152, 408],
      "filter_conditions": [
        { "key": "technologies", "operator": "and" }
      ]
    }
    ```

    Les identifiants numériques de tags proviennent de `GET /technologies?search_term=python` (et similaires). **Ne combinez pas `is_enable_similarity_search: true` avec AND sur la même clé** — la similarité étend chaque terme en texte libre vers de nombreux identifiants de tags, et `@>` exige alors que la ligne les contienne tous, ce qui renvoie presque toujours zéro résultat.

    Une entreprise est incluse uniquement si sa stack technologique contient **la totalité** de `[37, 152, 408]`. Se compile en `column @> ARRAY[...]` Postgres.

    À utiliser quand : vous voulez de la précision — « utilise *l'ensemble* de ces technologies ensemble », « taguée avec *toutes* ces verticales ».
  </Tab>
</Tabs>

<Note>
  Les filtres non listés dans `filter_conditions` utilisent l'opérateur par défaut (OR au sein d'un tableau, AND entre clés de filtre distinctes). Vous ne déclarez que les remplacements — jamais les valeurs par défaut.
</Note>

***

## Ce que vous pouvez remplacer

Chaque endpoint accepte des remplacements pour un ensemble de clés différent. Les clés proviennent de l'énumération OpenAPI de chaque schéma `*_filter_conditions` :

<CardGroup cols={3}>
  <Card title="Endpoint entreprise" icon="building" href="/fr/api-reference/endpoint/companies/search">
    Clés `company_filter_conditions` : `keywords`, `verticals`, `vertical_categories`, `vertical_sub_categories`, `technologies`, `categories`, `advertisement_target_locations`, `advertisement_exclude_target_locations`, `advertisement_search_terms`, `places`, `exclude_places`, `job_exclude_locations`.
  </Card>

  <Card title="Endpoint personnes" icon="user" href="/fr/api-reference/endpoint/people/search">
    Clés `people_filter_conditions` (délèguent au moteur entreprise) : `keywords`, `verticals`, `vertical_categories`, `vertical_sub_categories`, `technologies`, `categories`, `places`, `exclude_places`, plus `social_media`.
  </Card>

  <Card title="Endpoint annonces" icon="bullhorn" href="/fr/api-reference/endpoint/companies/advertisements_search">
    Clés `ads_filter_conditions` : `target_locations`, `exclude_target_locations`. Ensemble plus restreint car les annonces ne filtrent que par pays d'impression.
  </Card>
</CardGroup>

<Tip>
  Lors de l'utilisation de `/people/search`, la clé `filter_conditions[].key` pour les lieux au niveau entreprise utilise le nom **brut** issu du moteur entreprise — `places`, `exclude_places` — et non le nom préfixé côté API personnes (`company_places`). Voir [Filtres personnes + entreprise](/fr/developer-guides/filters/people-with-company-filters#key-remap-reference).
</Tip>

***

## Conseils de performance

<AccordionGroup>
  <Accordion title="Filtrez tôt sur les colonnes indexées" icon="bolt">
    Les lieux, les tranches d'effectifs et `founded_dates` sont indexés et réduisent l'ensemble de candidats plus rapidement que les filtres en texte libre ou par verticale. Combinez-les avec un ou deux filtres précis avant de recourir à la recherche par similarité.
  </Accordion>

  <Accordion title="N'abusez pas de AND sur de grands tableaux" icon="triangle-exclamation">
    `column @> ARRAY[a, b, c, …]` exige que chaque valeur soit présente. La cardinalité augmente rapidement — un AND sur 10 technologies dans une catégorie ayant en moyenne 3 tags technologiques renvoie un nombre de lignes proche de zéro et force un scan complet. Privilégiez 2 à 4 valeurs par filtre AND ; passez à OR pour les requêtes exploratoires.
  </Accordion>

  <Accordion title="Utilisez is_enable_similarity_search pour une saisie en texte libre" icon="wand-magic-sparkles">
    Si vous ne pouvez pas fournir d'identifiants slug (verticales, technologies, catégories) et disposez uniquement de chaînes en texte libre, définissez `is_enable_similarity_search: true` et `similarity_score: 0.7`. Le moteur résout les correspondances avant d'appliquer le filtre — bien moins coûteux qu'un scan de texte.
  </Accordion>

  <Accordion title="Préférez les plages aux listes d'énumérations pour la taille et le chiffre d'affaires" icon="arrows-left-right">
    `employees: [[201, 500], [501, 1000]]` (un tableau de tranches) et `revenues: [1000000, 5000000]` (une seule plage min/max) sont plus rapides et plus idiomatiques que de longues listes d'identifiants.
  </Accordion>
</AccordionGroup>

***

## Filtres d'activité publicitaire

`/companies/search` peut filtrer sur l'empreinte publicitaire d'une entreprise — combien d'annonces elle diffuse, si certaines sont actuellement actives, quelles plateformes et formats elle utilise, et comment elle se classe par rapport aux autres annonceurs dans un pays donné. `POST /companies/advertisements/search` accepte également chacun de ces filtres (`advertisement_active_ads`, `advertisement_running_ads`, `advertisement_total_ads`, `advertisement_platform_count`, `advertisement_format_count`, `advertisement_impressions_estimate`, `advertisement_formats`, `advertisement_country_activity`) — une annonce est incluse si l'entreprise qui la possède satisfait la borne.

| Filtre                                                                             | Structure                                                                                                                                                 | Portée    |
| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| `advertisement_active_ads`, `advertisement_running_ads`, `advertisement_total_ads` | plage `[min, max]`                                                                                                                                        | Tous pays |
| `advertisement_platform_count`, `advertisement_format_count`                       | plage `[min, max]`                                                                                                                                        | Tous pays |
| `advertisement_impressions_estimate`                                               | plage `[min, max]`                                                                                                                                        | Tous pays |
| `advertisement_formats`                                                            | tableau — `image`, `video`, `text`, `carousel`, `dynamic_product`, `document`, `message`, `event`, `article`, `spotlight`, `follow`, `job`, `engagement`  | Tous pays |
| `advertisement_platforms`                                                          | tableau — `linkedin`, `facebook`, `google`, `tiktok`, `apple` (`meta` accepté comme alias de `facebook`)                                                  | Tous pays |
| `advertisement_publisher_platforms`, `advertisement_exclude_publisher_platforms`   | tableau — `facebook`, `instagram`, `messenger`, `threads`, `audience_network`                                                                             | Tous pays |
| `advertisement_status`                                                             | tableau — `currently_running` (a des annonces enregistrées), `active_last_30_min` (enregistrements d'annonces modifiés au cours des 30 dernières minutes) | Tous pays |
| `advertisement_country_activity`                                                   | objet, voir ci-dessous                                                                                                                                    | Un pays   |

Utilisez `null` pour une borne ouverte — `[1, null]` signifie « au moins 1 », `[null, 500]` signifie « 500 ou moins ».

```json theme={null}
{
  "advertisement_active_ads": [1, null],
  "advertisement_formats": ["video"]
}
```

### Classement au sein d'un pays

`advertisement_country_activity` limite le rang, le percentile et le score de volume à un seul pays à la fois — le rang publicitaire d'une entreprise aux États-Unis ne dit rien de son rang en Allemagne, donc `country` est obligatoire :

```json theme={null}
{
  "advertisement_country_activity": {
    "country": "US",
    "rank": [null, 500]
  }
}
```

<Warning>
  **`rank` décompte à partir du plus gros annonceur, comme dans une course.** `1` est le plus gros annonceur de ce pays, et le chiffre *augmente* à mesure que le volume publicitaire *diminue*. Pour trouver les **plus gros** annonceurs, filtrez `rank` avec une borne supérieure **basse** — `[null, 500]` correspond au top 500. Filtrer `[500, null]` trouve tous ceux qui sont **hors** du top 499, ce qui est l'inverse de « gros annonceur ». Si vous voulez une version indépendante de l'échelle qui fonctionne de la même façon quel que soit le nombre d'annonceurs dans ce pays, utilisez plutôt `percentile` — il fonctionne dans l'autre sens, donc un chiffre **plus élevé** signifie un annonceur **plus important** : `[90, null]` correspond au top 10 %.
</Warning>

Combinez `advertisement_country_activity` avec les filtres tous-pays ci-dessus dans la même requête — par exemple, « classée dans le top 500 aux États-Unis, et ayant au moins 100 annonces actives à l'échelle de l'entreprise » :

```json theme={null}
{
  "advertisement_country_activity": { "country": "US", "rank": [null, 500] },
  "advertisement_active_ads": [100, null]
}
```

***

## Étapes suivantes

<CardGroup cols={2}>
  <Card title="filter_conditions" icon="code-merge" href="/fr/developer-guides/filters/filter-conditions">
    Page de référence — chaque clé prise en charge, chaque valeur par défaut, et des exemples AND/OR prêts à copier.
  </Card>

  <Card title="Filtres personnes + entreprise" icon="users-rectangle" href="/fr/developer-guides/filters/people-with-company-filters">
    Utilisez n'importe quel filtre entreprise à l'intérieur de `/people/search`. La fonctionnalité phare du moteur unifié.
  </Card>

  <Card title="Référence Recherche entreprise" icon="building" href="/fr/api-reference/endpoint/companies/search">
    Schéma complet de requête/réponse pour `/companies/search`.
  </Card>

  <Card title="Référence Recherche personnes" icon="user" href="/fr/api-reference/endpoint/people/search">
    Schéma complet de requête/réponse pour `/people/search`.
  </Card>
</CardGroup>

<Note>
  Vous cherchez le guide de filtrage côté tableau de bord ? Voir [Filtrer et exporter des contacts](/fr/knowledge-base/concepts/search-filters) dans la base de connaissances.
</Note>
