> ## 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.

# Resumen de filtros

> Cómo funcionan los filtros de búsqueda de Pubrio — el motor de filtros unificado, la semántica AND/OR y cuándo usar cada uno.

Los endpoints de búsqueda de Pubrio (`/companies/search`, `/people/search`, `/companies/advertisements/search`) comparten un único motor de filtros. Compones el cuerpo de la solicitud una vez y las mismas reglas se aplican en todos los endpoints — incluida la forma en que se combinan los filtros multivalor, cómo se comparan las ubicaciones y cómo anulas el operador por defecto con `filter_conditions`.

## ¿Por qué un motor de filtros unificado?

<CardGroup cols={2}>
  <Card title="Un esquema, tres endpoints" icon="arrows-rotate">
    Los filtros a nivel de empresa como `technologies`, `verticals` y `founded_dates` funcionan de forma idéntica en `/companies/search`, `/people/search` y dentro de `company_filters` de Monitor — los aprendes una sola vez.
  </Card>

  <Card title="AND/OR por filtro" icon="code-merge">
    El valor por defecto es OR (coincide con cualquiera). Promueve filtros individuales a AND (coincide con todos) agregando una entrada a `filter_conditions` — sin tocar el resto del cuerpo.
  </Card>

  <Card title="Operadores nativos de Postgres" icon="database">
    Los filtros de array se compilan a operadores nativos de Postgres — `&&` (solapamiento) para OR, `@>` (contiene) para AND. Compatibles con índices, sin post-filtrado del lado de la aplicación.
  </Card>

  <Card title="Los mismos filtros en Monitores" icon="bell">
    El bloque `company_filters` en [Monitores](/es/developer-guides/introduction) acepta la misma forma, por lo que un payload de búsqueda que funciona también funciona como payload de monitor.
  </Card>
</CardGroup>

***

## Anatomía de una solicitud de búsqueda

Cada solicitud de búsqueda se construye a partir de tres capas en el mismo cuerpo JSON:

| Capa                    | Dónde vive                                                                                    | Ejemplos                                                                       |
| ----------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Filtros de personas     | claves de nivel superior                                                                      | `people_titles`, `management_levels`, `departments`, `people_locations`        |
| Filtros de empresa      | anidados bajo `company_filters: {...}` (recomendado) — también se acepta en el nivel superior | `technologies`, `verticals`, `founded_dates`, `employees`, `company_locations` |
| Anulaciones de operador | array `filter_conditions` (dentro de `company_filters` cuando se anulan claves de empresa)    | `[{ "key": "technologies", "operator": "and" }]`                               |

Una solicitud mínima a `/people/search` que usa las tres capas:

<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`: mantén agrupadas las claves de nivel de empresa

El objeto contenedor `company_filters: {...}` es la forma recomendada de enviar filtros a nivel de empresa — separa visualmente qué claves filtran a la *persona* de cuáles filtran a la *empresa*, y coincide con la forma que ya usan los [Monitores](/es/developer-guides/introduction), de modo que los payloads se transfieren limpiamente entre configuraciones de búsqueda y de monitor.

Ambos estilos funcionan; el motor aplana la forma envuelta al nivel superior antes de procesar, y **las claves de nivel superior ganan en caso de conflicto**:

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

  ```json Plano (también funciona) theme={null}
  {
    "people_titles": ["VP of Engineering"],
    "technologies": [37, 152],
    "founded_dates": [2015, 2023],
    "company_locations": ["US"]
  }
  ```
</CodeGroup>

Cuando agregues una anulación de `filter_conditions` para una clave de nivel de empresa, colócala **dentro** de `company_filters` para que viaje junto con las claves que anula.

### Misma forma en las variantes `/search/similar`

`POST /companies/search/similar` y `POST /people/search/similar` aceptan el **mismo cuerpo de filtros** que sus contrapartes sin "similar" (incluyendo el contenedor `company_filters` y `filter_conditions`). Cada uno agrega un paso de similitud adicional:

|                             | Qué necesitan de más                                                                                                        | Qué obtienes de más                                                                                           |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `/companies/search/similar` | Una empresa de referencia — `domain_search_id`, `domain`, `linkedin_url`, o `domains`                                       | Cada resultado obtiene un `similarity_score` (flotante, 0-1) y las filas se ordenan por similitud descendente |
| `/people/search/similar`    | Una persona/título de referencia — uno de `people_titles`, `people_search_id`, `linkedin_url`, `linkedin_urls`, o `peoples` | Igual — `similarity_score` por fila, ordenado por similitud                                                   |

El sobre de la respuesta es, por lo demás, idéntico al del endpoint estándar `search`. Los filtros acotan el conjunto de candidatos *antes* de que se aplique la clasificación por similitud — por lo que combinar `company_locations: ["US"]` con `/people/search/similar` devuelve a las personas con sede en EE. UU. más cercanas a tus títulos de referencia, que es el patrón "encuentra más personas como X dentro de estas restricciones".

<Note>
  A diferencia de los endpoints estándar `/search`, `/search/similar` **no** devuelve un `pagination.total_entries` exacto — el valor tiene un tope porque la búsqueda por similitud clasifica los resultados por relevancia y solo muestra las mejores coincidencias. Usa la búsqueda por similitud para encontrar las *mejores* coincidencias, no para enumerar cada una de ellas.
</Note>

***

## AND frente a OR — la única decisión que tomas por filtro

Los filtros multivalor (`technologies`, `verticals`, `keywords`, `categories`, …) aceptan un array. El operador decide qué significa "coincidencia":

<Tabs>
  <Tab title="OR (por defecto)">
    **Coincide con cualquier valor.** Devuelve filas cuyo array se solapa con la entrada.

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

    Una empresa se incluye si su stack tecnológico contiene **al menos uno** de `Python`, `PostgreSQL` o `Kubernetes`. Se compila a `column && ARRAY[...]` en Postgres.

    Úsalo cuando: quieres un alcance amplio — "interesado en *cualquiera* de estos", "ubicado en *cualquiera* de estos países".
  </Tab>

  <Tab title="AND">
    **Coincide con todos los valores.** Devuelve filas cuyo array contiene todos los valores de entrada.

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

    Los IDs numéricos de etiqueta provienen de `GET /technologies?search_term=python` (y similares). **No combines `is_enable_similarity_search: true` con AND en la misma clave** — la similitud expande cada término de texto libre en muchos IDs de etiqueta y `@>` luego exige que la fila los contenga todos, lo que casi siempre devuelve cero.

    Una empresa se incluye solo si su stack tecnológico contiene **todos** de `[37, 152, 408]`. Se compila a `column @> ARRAY[...]` en Postgres.

    Úsalo cuando: quieres precisión — "usa *todas* estas tecnologías juntas", "etiquetado con *todos* estos verticales".
  </Tab>
</Tabs>

<Note>
  Los filtros no listados en `filter_conditions` usan el operador por defecto (OR dentro de un array, AND entre claves de filtro distintas). Solo declaras las anulaciones — nunca los valores por defecto.
</Note>

***

## Qué puedes anular

Cada endpoint acepta anulaciones para un conjunto de claves distinto. Las claves provienen del enum de OpenAPI en cada esquema `*_filter_conditions`:

<CardGroup cols={3}>
  <Card title="Endpoint de empresas" icon="building" href="/es/api-reference/endpoint/companies/search">
    Claves de `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 de personas" icon="user" href="/es/api-reference/endpoint/people/search">
    Claves de `people_filter_conditions` (delegan al motor de empresa): `keywords`, `verticals`, `vertical_categories`, `vertical_sub_categories`, `technologies`, `categories`, `places`, `exclude_places`, además de `social_media`.
  </Card>

  <Card title="Endpoint de anuncios" icon="bullhorn" href="/es/api-reference/endpoint/companies/advertisements_search">
    Claves de `ads_filter_conditions`: `target_locations`, `exclude_target_locations`. Conjunto más pequeño porque los anuncios solo se filtran por país de impresión.
  </Card>
</CardGroup>

<Tip>
  Al usar `/people/search`, el `filter_conditions[].key` para las ubicaciones a nivel de empresa usa el nombre **simple** del motor de empresa — `places`, `exclude_places` — no el nombre con prefijo de la API de personas (`company_places`). Consulta [Personas + Filtros de empresa](/es/developer-guides/filters/people-with-company-filters#key-remap-reference).
</Tip>

***

## Consejos de rendimiento

<AccordionGroup>
  <Accordion title="Filtra primero por columnas indexadas" icon="bolt">
    Las ubicaciones, los rangos de empleados y `founded_dates` están indexados y reducen el conjunto de candidatos más rápido que los filtros de texto libre o de vertical. Combínalos con uno o dos filtros precisos antes de recurrir a la búsqueda por similitud.
  </Accordion>

  <Accordion title="No abuses de AND en arrays grandes" icon="triangle-exclamation">
    `column @> ARRAY[a, b, c, …]` exige que cada valor esté presente. La cardinalidad crece rápido — un AND de 10 tecnologías en una categoría con un promedio de 3 etiquetas tecnológicas devuelve casi cero filas y fuerza un escaneo completo. Prefiere entre 2 y 4 valores por filtro AND; cambia a OR para consultas exploratorias.
  </Accordion>

  <Accordion title="Usa is_enable_similarity_search para entradas de texto libre" icon="wand-magic-sparkles">
    Si no puedes proporcionar IDs de slug (verticales, tecnologías, categorías) y solo tienes cadenas de texto libre, configura `is_enable_similarity_search: true` y `similarity_score: 0.7`. El motor resuelve las coincidencias antes de aplicar el filtro — mucho más económico que escanear texto.
  </Accordion>

  <Accordion title="Prefiere rangos sobre listas de enum para tamaño e ingresos" icon="arrows-left-right">
    `employees: [[201, 500], [501, 1000]]` (un array de rangos) y `revenues: [1000000, 5000000]` (un único rango mínimo/máximo) son más rápidos y más idiomáticos que listas largas de IDs.
  </Accordion>
</AccordionGroup>

***

## Filtros de actividad publicitaria

`/companies/search` puede filtrar por la huella publicitaria de una empresa — cuántos anuncios ejecuta, si alguno está actualmente activo, qué plataformas y formatos usa, y cómo se clasifica frente a otros anunciantes en un país determinado. `POST /companies/advertisements/search` también acepta todos estos filtros (`advertisement_active_ads`, `advertisement_running_ads`, `advertisement_total_ads`, `advertisement_platform_count`, `advertisement_format_count`, `advertisement_impressions_estimate`, `advertisement_formats`, `advertisement_country_activity`) — un anuncio se incluye si la empresa propietaria cumple el límite.

| Filtro                                                                             | Forma                                                                                                                                                  | Alcance          |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- |
| `advertisement_active_ads`, `advertisement_running_ads`, `advertisement_total_ads` | rango `[min, max]`                                                                                                                                     | Todos los países |
| `advertisement_platform_count`, `advertisement_format_count`                       | rango `[min, max]`                                                                                                                                     | Todos los países |
| `advertisement_impressions_estimate`                                               | rango `[min, max]`                                                                                                                                     | Todos los países |
| `advertisement_formats`                                                            | array — `image`, `video`, `text`, `carousel`, `dynamic_product`, `document`, `message`, `event`, `article`, `spotlight`, `follow`, `job`, `engagement` | Todos los países |
| `advertisement_platforms`                                                          | array — `linkedin`, `facebook`, `google`, `tiktok`, `apple` (`meta` se acepta como alias de `facebook`)                                                | Todos los países |
| `advertisement_publisher_platforms`, `advertisement_exclude_publisher_platforms`   | array — `facebook`, `instagram`, `messenger`, `threads`, `audience_network`                                                                            | Todos los países |
| `advertisement_status`                                                             | array — `currently_running` (tiene anuncios registrados), `active_last_30_min` (los registros de anuncios cambiaron en los últimos 30 minutos)         | Todos los países |
| `advertisement_country_activity`                                                   | objeto, ver a continuación                                                                                                                             | Un país          |

Usa `null` para un límite abierto — `[1, null]` significa "al menos 1", `[null, 500]` significa "500 o menos".

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

### Clasificación dentro de un país

`advertisement_country_activity` acota el rango, el percentil y la puntuación de volumen a un país a la vez — el rango publicitario de una empresa en EE. UU. no dice nada sobre su rango en Alemania, por lo que `country` es obligatorio:

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

<Warning>
  **`rank` cuenta hacia abajo desde el mayor anunciante, como en una carrera.** `1` es el mayor anunciante en ese país, y el número *sube* a medida que el volumen publicitario *baja*. Para encontrar a los **mayores** anunciantes, filtra `rank` con un límite superior **bajo** — `[null, 500]` es el top 500. Filtrar `[500, null]` encuentra a todos los que están **fuera** del top 499, que es lo opuesto de "gran anunciante". Si quieres una versión libre de escala que funcione igual sin importar cuántos anunciantes haya en ese país, usa `percentile` en su lugar — funciona en la dirección contraria, por lo que un número **más alto** significa un anunciante **más grande**: `[90, null]` es el 10% superior.
</Warning>

Combina `advertisement_country_activity` con los filtros de todos los países anteriores en la misma solicitud — por ejemplo, "clasificado en el top 500 en EE. UU., y tiene al menos 100 anuncios activos a nivel de empresa":

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

***

## Próximos pasos

<CardGroup cols={2}>
  <Card title="filter_conditions" icon="code-merge" href="/es/developer-guides/filters/filter-conditions">
    Página de referencia — cada clave admitida, cada valor por defecto, y recetas AND/OR copiables.
  </Card>

  <Card title="Personas + Filtros de empresa" icon="users-rectangle" href="/es/developer-guides/filters/people-with-company-filters">
    Usa cualquier filtro de empresa dentro de `/people/search`. La función destacada del motor unificado.
  </Card>

  <Card title="Referencia de Company Search" icon="building" href="/es/api-reference/endpoint/companies/search">
    Esquema completo de solicitud/respuesta para `/companies/search`.
  </Card>

  <Card title="Referencia de People Search" icon="user" href="/es/api-reference/endpoint/people/search">
    Esquema completo de solicitud/respuesta para `/people/search`.
  </Card>
</CardGroup>

<Note>
  ¿Buscas el recorrido de filtrado del lado del dashboard? Consulta [Filtrar y exportar contactos](/es/knowledge-base/concepts/search-filters) en la Base de conocimiento.
</Note>
