Pulse Intelligence

Filters

The curated filter vocabulary, operator semantics, and option lookups

Every resource on the public API exposes a deliberately curated set of filterable fields and response columns — not the full internal field set Pulse's own product UI can filter on. This guide covers the concepts: how to discover the vocabulary, every operator and what it does, how a search payload combines filters, and how to resolve select-type values. The complete field-by-field listing lives in the filter & column reference.

Curated vocabulary

Each resource — companies, mining-assets — has its own allowlist of field keys it exposes publicly. A field only becomes available once someone deliberately adds it to that resource's allowlist; new internal fields are excluded by default, not exposed by default. This keeps the public contract stable and avoids leaking user-scoped internal concepts (like a saved company set, which has no meaning for an organization-wide API key) onto the public surface.

publications and people search have no such allowlist — see Publications and people search below for what they actually accept.

If you reference a filter field or request a column outside a resource's curated set, the request fails with a 400unsupported_filter_field for filters, unsupported_column for columns; see Common error codes for the response shape.

Discovering fields

GET /v1/filters/{resource}/

Call this endpoint to get the current filterable fields and available columns for a resource — the same set that resource's /search endpoint will accept. resource is one of companies or mining-assets.

curl https://api.pulseintelligence.com/v1/filters/companies/ \
  -H "Authorization: Bearer pulse_a1b2c3d4_<secret>"

A truncated response for companies looks like:

{
  "resource": "companies",
  "filters": [
    {
      "field": "market_cap_in_usd",
      "operators": ["gte", "lte"],
      "type": "number",
      "unit": "USD",
      "options": null,
      "options_endpoint": null,
      "label": "Market Cap"
    },
    {
      "field": "industry",
      "operators": ["has_any"],
      "type": "select",
      "unit": null,
      "options": [
        { "id": "mining", "label": "Mining" },
        { "id": "steel", "label": "Steel" }
      ],
      "options_endpoint": null,
      "label": "Industry"
    },
    {
      "field": "commodities",
      "operators": ["has_any", "has_only"],
      "type": "select",
      "unit": null,
      "options": null,
      "options_endpoint": "/commodities/options/",
      "label": "Primary commodities"
    }
  ],
  "columns": [
    { "key": "market_cap_in_usd", "title": "Market Cap", "type": "number", "sortable": true },
    { "key": "commodities", "title": "Primary Commodities", "type": null, "sortable": false }
  ]
}

Every field of a filter or column entry is documented on the List filters reference page. The two that drive the rest of this guide: operators is the set of operators the field accepts (see Operator semantics below), while a select field supplies either closed inline options or an options_endpoint for request-time resolution (see Resolving option values below).

An unsupported resource returns a 400:

{
  "error": {
    "code": "invalid_argument",
    "message": "Unsupported resource 'listings'. Available in the public API: ['companies', 'mining-assets'].",
    "resource": "listings"
  }
}

Operator semantics

The shared filter engine (pulse_table) defines a larger operator set, but the public API's curated fields only ever advertise these four — every operators array you'll see from GET /v1/filters/{resource}/ is a subset of this list:

OperatorField typesValue shapeMeaning
gtenumber, datea single number, or an ISO date stringField ≥ value.
ltenumber, datea single number, or an ISO date stringField ≤ value.
has_anyselectnon-empty list of option idsMatch if the field's value (or, for a multi-value field, any of its values) is in the given list — an OR match.
has_onlyselectnon-empty list of option idsExact-set match: matches only rows with no qualifying value outside the given list — the complement of has_any. Every has_only field in the filter & column reference (exchanges, commodities, all_commodities, asset_phases, asset_stages on companies; commodities, exchanges on mining assets) implements this exact-set semantic.

No public field uses eq, ne, gt, lt, exact, contains, or range — those exist in pulse_table for internal product filters but aren't reachable through any field in either resource's curated allowlist.

A gte/lte pair on the same field is how you express a numeric or date range (there's no separate range operator on the public surface):

{
  "filters": [
    { "field": "market_cap_in_usd", "operator": "gte", "value": 100000000 },
    { "field": "market_cap_in_usd", "operator": "lte", "value": 500000000 }
  ]
}

Private company and asset sets from the signed-in application are not public API filter fields. Use the curated fields listed by the filter reference. Custom query expressions remain available here; these requests do not save or modify a saved search.

Building a search payload

The search endpoints (POST /v1/companies/search/, POST /v1/mining-assets/search/) take a list of filters clauses, a logic mode that combines them, an optional comma-separated columns string shaping each result row, and optional limit/offset/sort paging parameters (covered in Pagination and enumeration — including what an empty filters list does) — each parameter is documented in full on the Search companies and Search mining assets reference pages. The part that deserves explanation is logic="custom": it pairs with an expression string like "1 AND (2 OR 3)" that references filters by their 1-based position in the filters list, letting you group AND and OR arbitrarily. expression is only used, and only required, when logic is "custom" — omitting it there is a 400.

One practical note on columns: omitting it returns the resource's full default row — several hundred keys, including per-commodity metric and projection families beyond what the filter & column reference lists as filterable. Those extra keys are read-only output, not part of the curated filter contract. Requesting an explicit columns set keeps rows lean and your daily row quota usage low.

An invalid logic value is rejected before your filters are ever evaluated:

{
  "error": {
    "code": "invalid_argument",
    "message": "logic must be one of ['all', 'any', 'custom']."
  }
}

Full field allowlist

The complete allowlist — every filterable field with its operators, unit, and options endpoint, plus every response column key — is published as the filter & column reference. It is generated from the same vocabulary GET /v1/filters/{resource}/ serves, so the two never disagree; use the live endpoint when you need the vocabulary programmatically.

POST /v1/publications/search/ and POST /v1/people/search/ have no curated vocabulary layer — there's no /v1/filters/ equivalent for them and no allowlist to reject against. Instead of the filters/logic DSL, each takes its own structured parameters (a free-text query plus entity/date/type filters for publications; query/limit/offset for people), documented in full on their reference pages: Search publications and Search people.

Two behavioral quirks worth knowing beyond the schemas: people search never rejects input — every parameter is clamped or defaulted rather than validated, so its only possible 400 is an unparseable JSON body — and both endpoints can return a 408 (query_timeout) if a broad query runs past the server's statement timeout; narrow the search term (or, for publications, add a company/date filter) and retry.

Resolving option values

GET /v1/options/{path}/

For select-type fields, first read the field's options array from /v1/filters/{resource}/. Closed vocabularies such as company industry and mining-asset phase carry their exact {id, label} pairs inline. When options is null, use the field's options_endpoint to resolve a database-backed or user-scoped vocabulary rather than guessing at spellings or internal ids. Strip the leading/trailing slashes from options_endpoint and place it in the {options_path} segment of this route. For example, /commodities/options/ becomes GET /v1/options/commodities/options/.

curl "https://api.pulseintelligence.com/v1/options/commodities/options/?query=gold&limit=5" \
  -H "Authorization: Bearer pulse_a1b2c3d4_<secret>"
{
  "query": "gold",
  "matches": [
    { "id": 1, "label": "Gold (Au)" }
  ]
}
  • query — the search term to resolve (empty string returns the top matches unfiltered).
  • limit — maximum number of matches to return (default 10).

id and label are the guaranteed keys on every match; some option types return additional informational keys alongside them (commodities, for example, include a shortLabel). Treat extras as display context — the id is the only part a filter value needs.

The id type nuance

matches[].id is not always the same type — it's an integer for options backed by a real database model (companies, mining assets, commodities, exchanges, countries, provinces, world regions, topics, case categories), and a string for options backed by a static choice enum (statuses, types, phases, and similar closed vocabularies). Don't assume one shape — read the type back from the response and pass it through unchanged as the filter value.

Allowed options paths

{path} isn't arbitrary — only this bounded set of reference-data endpoints is resolvable (the same allowlist Pulse's MCP server enforces for resolve_options):

Options pathResolvesid type
case-categories/optionsSupport case categoriesinteger
commodities/extraction-optionsProduction-extraction commoditiesinteger
commodities/optionsCommoditiesinteger
companies/optionsCompaniesinteger
content-variants/optionsPublication content variantsstring
countries/optionsCountriesinteger
exchanges/optionsStock exchangesinteger
mining-asset-phases/optionsMining asset phasesstring
mining-asset-stages/optionsMining asset stagesstring
mining-assets/optionsMining assetsinteger
provinces/optionsProvinces / statesinteger
topics/optionsPublication topicsinteger
world-regions/optionsWorld regionsinteger

Calling an endpoint outside this allowlist — or one that's excluded because it's per-user data rather than reference data (like a saved company set) — 400s:

{
  "error": {
    "code": "invalid_argument",
    "message": "Unknown options endpoint: bogus/options",
    "options_endpoint": "bogus/options"
  }
}

A non-integer limit also 400s the same way, rather than silently falling back to a default:

{
  "error": {
    "code": "invalid_argument",
    "message": "limit must be an integer, got 'abc'"
  }
}

Worked example: discover, search, resolve

One coherent flow: find what's filterable on companies, build a two-clause search, then resolve an option id for a select field.

1. Discover the vocabulary:

curl https://api.pulseintelligence.com/v1/filters/companies/ \
  -H "Authorization: Bearer pulse_a1b2c3d4_<secret>"

This response (see the filter & column reference for the full listing) tells you market_cap_in_usd takes gte/lte with no option resolution needed, and commodities takes has_any/has_only with options_endpoint: "/commodities/options/".

2. Resolve the commodity you want to filter by:

curl "https://api.pulseintelligence.com/v1/options/commodities/options/?query=gold&limit=3" \
  -H "Authorization: Bearer pulse_a1b2c3d4_<secret>"
{
  "query": "gold",
  "matches": [
    { "id": 1, "label": "Gold (Au)" }
  ]
}

3. Build the two-clause search using the resolved id:

curl https://api.pulseintelligence.com/v1/companies/search/ \
  -H "Authorization: Bearer pulse_a1b2c3d4_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      { "field": "market_cap_in_usd", "operator": "gte", "value": 100000000 },
      { "field": "commodities", "operator": "has_any", "value": [1] }
    ],
    "logic": "all",
    "columns": "name,symbol,market_cap_in_usd,commodities"
  }'

This returns gold companies with a market cap of at least $100M, with just the four requested columns in each result row.

Go deeper

  • Rate limits and errors covers the error envelope shape and quota headers referenced throughout this guide.
  • The API Reference has the full schema for /v1/filters/{resource}/ and /v1/options/{path}/, plus every search and dossier endpoint.

On this page