Overview

GET /api/v1/search powers a single “search bar” experience: one query hits Elasticsearch (Searchkick) for each visible record type and returns a small, flat row per hit. Results are always intersected with Pundit policy scopes so Elasticsearch never bypasses authorization.

When to use it
  • Ambiguous intent — brand name, email fragment, or domain could be an advertiser, deal, contact, or publisher.
  • MCP / agents — the curated universal_search tool maps directly to this endpoint (first tool in tools/list).
  • Follow-ups — after you pick a row, call the narrower tool (get_advertiser, get_deal, etc.) for full detail.

Endpoint

HTTP
GET /api/v1/search

Requires X-Api-Key (or supported bearer / legacy query param per API key docs).

Record types

Type slugs for types (subset only):

advertisers, publishers, agencies, deals, contacts, users

The users bucket is only returned for admin callers; non-admins never see it. contacts requires a non–free account with publisher (or admin).

Model Context Protocol (MCP)

MCP clients should call the universal_search tool (same query params as REST). The executor sends GET /api/v1/search with your API key. Arguments query and search are merged into q when q is omitted.

See MCP server documentation for session setup. Example tools/call payload:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "universal_search",
    "arguments": {
      "q": "nike",
      "per_type_limit": 5,
      "types": ["advertisers", "deals"]
    }
  }
}

API Playground

Try live JSON with your API key. MCP JSON-RPC examples use POST /mcp with the same arguments inside tools/call. Sign in to enable live testing with your API key.

Action Method Endpoint Description
Universal search GET /api/v1/search?q=... Omnibox query; add types=advertisers,deals and per_type_limit as needed.
MCP tool call POST /mcp tools/call with name universal_search and arguments { q, types?, per_type_limit? }.
GET /api/v1/search?q=demo&per_type_limit=5 v1

Baseline omnibox call — adjust q for your data.


    
GET /api/v1/search?q=brand&types=advertisers,deals&per_type_limit=3 v1

Restrict to advertisers and deals only with a low per-type cap.


    
POST /mcp — tools/call universal_search MCP

Same search via MCP; types sent as a JSON array in the tool body.


    

Query parameters

Parameter Required Description
q Yes Search string. Whitespace-only values return 400 MISSING_PARAMETER.
types No Comma-separated type slugs (e.g. advertisers,deals) or repeated array-style params. Invalid slugs are ignored.
per_type_limit No Max hits per type (default 10, max 50).

Response shape

Successful 200 JSON includes query, results (one key per type with data and total), and meta with per_type_limit, resolved types, and searchkick_available.

{
  "query": "nike",
  "results": {
    "advertisers": { "data": [ { "id": 1, "type": "advertiser", "title": "…" } ], "total": 1 }
  },
  "meta": {
    "per_type_limit": 10,
    "types": ["advertisers"],
    "searchkick_available": true
  }
}

Errors

Missing q returns 400 with MISSING_PARAMETER (standard API error envelope). When Searchkick is unavailable, the API still returns 200 with empty data arrays and meta.searchkick_available: false so clients can degrade gracefully.

Ask Dex AIIntegration help

If this page feels TLDR, ask Dex AI.

Dex AI speaks your language, and all the other languages you may not. It will write the integration for you with the right endpoint and headers in one plain-English answer.