Apollo Cap Search

Limits every Apollo search to 50 results per page, so a prospecting search can't turn into a dump of Apollo's contact database.

On this page
  1. What it does
  2. Compliance alignment
  3. Why ingress
  4. How it matches
  5. Examples
  6. Clamped
  7. Untouched
  8. Composition
  9. Known limitations
  10. Policy source (Rego)
  11. Related policies
Direction
ingress (tool_pre_invoke)
Default
allow (transform-only)
Rego package
apollo.ingress.cap_search
App
Apollo
Bundles
Gtm Stack HubspotGtm Stack Salesforce
Published
Minimum gateway
1.0.0b24
Schema version
1.0.0
Checksum
sha256:2c068c401da6c0bf9377cfb45146d044da52c5cf1aea5857cc40a7209440a32e

What it does

Limits every Apollo search to 50 results per page, so a prospecting search can't turn into a dump of Apollo's contact database. Apollo allows up to 100 per page. The agent still gets results; it just pages through them in smaller batches.

This is a transform, not a block: the policy rewrites per_page before the call reaches Apollo and never denies.

  • per_page above 50 → set to 50.
  • per_page missing, zero, negative or not a number → set to 50, so every search has a known page size rather than relying on Apollo's default.
  • per_page from 1 to 50 → untouched.

Compliance alignment

  • SOC 2 CC6.1 — limits the volume of personal data the agent channel can pull per call.
  • GDPR Art. 5(1)(c) — data minimisation: prospect searches return bounded pages.
  • ISO 27001 A.5.15 — access control: enforces a per-call volume limit at a technical control point.

Why ingress

The page size is a request argument. Rewriting it before the call means Apollo never returns more than the cap, rather than returning 100 records that then have to be cut down in the response.

How it matches

A tool is in scope when its (lowercased) name starts with apollo and has search as one of its - / _-separated tokens — for example apollo-apollo_mixed_people_api_search, apollo-apollo_mixed_companies_search and apollo-apollo_contacts_search. Change max_per_page at the top of the policy to fit your team.

Examples

Clamped

{
  "input": {
    "action": "tool_pre_invoke",
    "resource": { "name": "apollo-apollo_mixed_people_api_search", "type": "tool" },
    "payload": { "args": { "person_titles": ["VP Sales"], "per_page": 100 } }
  }
}

allow = true; transform.transformed_payload = the same args with per_page: 50.

Untouched

The same call with per_page: 25 passes through unchanged.

Composition

Pairs with the CRM cap-bulk-export policy in the same bundle, which caps search pages the same way on the CRM side. Apollo's bulk enrich calls already take at most 10 records, so they need no cap.

Known limitations

  • Per page, not per session. The agent can still page through a large result set. This policy bounds each call, not the total.
  • Argument name. Assumes Apollo's per_page argument, as in its REST API. Confirm against the tool schema on your gateway.
  • Prefix scope. Only tools whose name starts with apollo are in scope. Change the prefix if your gateway registers the server under another name.

Compliance note. This policy supports alignment with the cited framework controls on the MCP path only. No policy or bundle makes an organization compliant with any framework; web-UI, native-API, and in-app access are outside the gateway's reach by design. Validate against your own compliance program before relying on it.

Policy source (Rego)

package apollo.ingress.cap_search

import future.keywords.if
import future.keywords.in

# Transform-only policy — never denies, only clamps search page size.
default allow := true

max_per_page := 50

_tool := lower(input.resource.name)

_args := object.get(object.get(input, "payload", {}), "args", {})

_is_search_tool if {
    startswith(_tool, "apollo")
    "search" in regex.split(`[-_]`, _tool)
}

_per_page := object.get(_args, "per_page", null)

_needs_clamp if _per_page == null

_needs_clamp if {
    is_number(_per_page)
    _per_page > max_per_page
}

_needs_clamp if {
    is_number(_per_page)
    _per_page < 1
}

_needs_clamp if {
    _per_page != null
    not is_number(_per_page)
}

transform := {"transformed_payload": object.union(_args, {"per_page": max_per_page})} if {
    input.action == "tool_pre_invoke"
    _is_search_tool
    _needs_clamp
}

Canonical source: policy.md on GitHub · raw · raw on this site (.md)

↑↓ to navigate ↵ to open

Get in touch

Tell us what you need to govern.

A few lines is plenty — what you're deploying, and what you need control over. We read every one.