Skip to main content
Pubrio’s search endpoints (/companies/search, /people/search, /companies/advertisements/search) share a single filter engine. You compose a request body once and the same rules apply across endpoints — including the way multi-value filters combine, how locations are matched, and how you override the default operator with filter_conditions.

Why a unified filter engine?

One schema, three endpoints

Company-level filters like technologies, verticals, and founded_dates work identically on /companies/search, /people/search, and inside Monitor company_filters — you learn them once.

Per-filter AND/OR

The default is OR (match any). Promote individual filters to AND (match all) by adding one entry to filter_conditions — without touching the rest of the body.

Postgres-native operators

Array filters compile to native Postgres operators — && (overlap) for OR, @> (contains) for AND. Index-friendly, no application-side post-filtering.

Same filters in Monitors

The company_filters block in Monitors accepts the same shape, so a working search payload is also a working monitor payload.

Anatomy of a search request

Every search request is built from three layers in the same JSON body: A minimal /people/search request that uses all three layers:

company_filters: keep company-level keys grouped

The company_filters: {...} wrapper object is the recommended way to send company-level filters — it visually separates which keys filter the person from which filter the company, and matches the shape Monitors already use, so payloads transfer cleanly between search and monitor configurations. Both styles work; the engine flattens the wrapped form to the top level before processing, and top-level keys win on conflict:
When you add a filter_conditions override for a company-level key, put it inside company_filters so it travels with the keys it overrides.

Same shape on the /search/similar variants

POST /companies/search/similar and POST /people/search/similar accept the same filter body as their non-similar counterparts (including the company_filters wrapper and filter_conditions). Each one adds a similarity step on top: The response envelope is otherwise identical to the standard search endpoint. Filters narrow the candidate pool before similarity ranking is applied — so combining company_locations: ["US"] with /people/search/similar returns the closest US-based people to your reference titles, which is the “find more people like X within these constraints” pattern.
Unlike the standard /search endpoints, /search/similar does not return an exact pagination.total_entries — the value is capped because similar search ranks results by relevance and only surfaces the top matches. Use similar search to find the best matches, not to enumerate every one.

AND vs OR — the one decision you make per filter

Multi-value filters (technologies, verticals, keywords, categories, …) accept an array. The operator decides what “match” means:
Match any value. Returns rows whose array overlaps with the input.
A company is included if its tech stack contains at least one of Python, PostgreSQL, or Kubernetes. Compiles to Postgres column && ARRAY[...].Use when: you want broad reach — “interested in any of these”, “located in any of these countries”.
Filters not listed in filter_conditions use the default operator (OR within an array, AND across distinct filter keys). You only declare the overrides — never the defaults.

What you can override

Each endpoint accepts overrides for a different set of keys. The keys come from the OpenAPI enum on each *_filter_conditions schema:

Company endpoint

company_filter_conditions keys: 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.

People endpoint

people_filter_conditions keys (delegate to the company engine): keywords, verticals, vertical_categories, vertical_sub_categories, technologies, categories, places, exclude_places, plus social_media.

Ads endpoint

ads_filter_conditions keys: target_locations, exclude_target_locations. Smaller set because ads only filter by impression country.
When using /people/search, the filter_conditions[].key for company-level locations uses the bare name from the company engine — places, exclude_places — not the prefixed people-API name (company_places). See People + Company Filters.

Performance tips

Locations, employee buckets, and founded_dates are indexed and reduce the candidate set faster than free-text or vertical filters. Combine them with one or two precise filters before reaching for similarity search.
column @> ARRAY[a, b, c, …] requires every value to be present. Cardinality grows fast — a 10-tech AND on a category with average 3 tech tags returns near-zero rows and forces a full scan. Prefer 2-4 values per AND filter; switch to OR for exploratory queries.
If you can’t supply slug IDs (verticals, technologies, categories) and only have free-text strings, set is_enable_similarity_search: true and similarity_score: 0.7. The engine resolves matches before applying the filter — much cheaper than scanning text.
employees: [[201, 500], [501, 1000]] (an array of buckets) and revenues: [1000000, 5000000] (a single min/max range) are faster and more idiomatic than long ID lists.

Advertising activity filters

/companies/search can filter by a company’s advertising footprint — how many ads they run, whether any are currently active, which platforms and formats they use, and how they rank against other advertisers in a given country. POST /companies/advertisements/search accepts every one of these filters too (advertisement_active_ads, advertisement_running_ads, advertisement_total_ads, advertisement_platform_count, advertisement_format_count, advertisement_impressions_estimate, advertisement_formats, advertisement_country_activity) — an ad is included if its owning company meets the bound. Use null for an open-ended bound — [1, null] means “at least 1”, [null, 500] means “500 or fewer”.

Ranking within a country

advertisement_country_activity scopes rank, percentile, and volume score to one country at a time — a company’s ad rank in the US says nothing about its rank in Germany, so country is required:
rank counts down from the biggest spender, like a race. 1 is the biggest advertiser in that country, and the number goes up as advertising volume goes down. To find the biggest spenders, filter rank with a low upper bound — [null, 500] is the top 500. Filtering [500, null] finds everyone outside the top 499, which is the opposite of “big spender”. If you want a scale-free version that works the same regardless of how many advertisers are in that country, use percentile instead — it runs the other direction, so a higher number means a bigger spender: [90, null] is the top 10%.
Combine advertisement_country_activity with the all-country filters above in the same request — for example, “ranked in the top 500 in the US, and has at least 100 active ads company-wide”:

Ranking the results

Filtering by advertisement_country_activity narrows which companies come back; it does not by itself decide the order. To get the biggest advertisers in that market first, ask for sort_by: "advertisement_country_rank":
Whenever advertisement_country_activity is present, each company also carries its standing in that country, so you can display the rank you filtered on: sort_by: "advertisement_country_rank" is ignored when advertisement_country_activity is absent, since a rank only exists inside a country. Pass is_ascending_order: false to list the smallest advertisers in the market first.

Finding new entrants to a market

advertisement_country_activity also accepts two date windows, so you can ask who started advertising in a market rather than who is already big there: Companies that entered Japan since the start of July:
Either bound may be null for an open-ended window. Dates are interpreted in the request’s timezone. A company whose ads carry no start date has no first-seen value and is treated as unknown — it is never reported as new. Combine with the ranked keys in the same object to narrow further, for example new entrants that are already spending heavily.

Next steps

filter_conditions

Reference page — every supported key, every default, and copyable AND/OR recipes.

People + Company Filters

Use any company filter inside /people/search. The headline new feature of the unified engine.

Company Search reference

Full request/response schema for /companies/search.

People Search reference

Full request/response schema for /people/search.
Looking for the dashboard-side filtering walkthrough? See Filtering & Exporting Contacts in the Knowledge Base.
Last modified on September 4, 2026