Skip to main content
Beta. The Research endpoint is currently in beta. The request/response shape and credit model may change before general availability. Pin your integration to the examples below and watch the changelog for updates.

Overview

The Research endpoint runs a custom, AI-powered analysis over a list of entities. Instead of returning a fixed enrichment schema, you describe the task — either as a natural-language query or as a prompt_template — and define the exact shape of the result with an output_schema. For each entity, the engine resolves its profile, optionally performs real-time web research, and returns structured JSON that conforms to your schema. Research runs on two entity types, each with its own endpoint that follows the standard bulk-enrichment convention:
EntityEndpoint
BusinessesPOST /v1/businesses/research/bulk_enrich
ProspectsPOST /v1/prospects/research/bulk_enrich
Both endpoints accept the same parameters (query / prompt_template + output_schema); they differ only in the entity list and identifier (business_id vs prospect_id). This is ideal when an off-the-shelf enrichment doesn’t exist for your use case, for example:
  • Custom classification — e.g. B2B vs B2C, ICP fit, seniority or buying-role inference.
  • Fit scoring with reasoning — score each account or contact against a value proposition and explain why.
  • Campaign-ready copy — one-line segments or personalized openers tailored per entity. Each entity is processed independently against your prompt and schema, so a single request can research many records at once.

Real-time web research

What sets Research apart from standard enrichments is that the engine can ground its analysis in live web data, not just the data already on file. Per entity, it can:
  • Run a SERP search and use the top results as context for the model.
  • Search and scrape the most relevant web pages and pass that content to the model as grounding context. You steer this directly from your instruction — for example, “…use web search to validate if the description is insufficient” or “…use web search to supplement missing context.” When the resolved profile is enough, the model can answer from it; when it isn’t, the model can reach out to the web.
Because some rows trigger live web lookups, per-entity latency varies and can take several seconds. Size your batches and set client timeouts accordingly.
  • Input: A list of entities (each identified by a business_id or prospect_id, with optional custom_fields) plus a parameters object containing either a query or a prompt_template, and an output_schema (required with prompt_template, optional with query).
  • Processing: For each entity, Explorium resolves the profile and builds a record context. When you pass a query, an internal LLM first generates an optimized prompt from your instruction, the available record fields, and the available research functions — and, if you didn’t supply an output_schema, generates one as well. When you pass a prompt_template, your prompt is used as-is. The engine then runs the prompt — performing web research where instructed — and produces structured output constrained to the schema.
  • Output: One result row per input entity, returned under data. Each successful row contains the generated fields defined by the output_schema; failed rows are returned with an _error field rather than being dropped.
You must provide exactly one of query or prompt_template. Sending both, or neither, returns a validation error.
queryprompt_template
What you writeA natural-language instruction describing the insight you wantA prompt with placeholders that reference fields on the record
Prompt constructionAn internal LLM composes an optimized prompt from your instruction, the available record fields, and the available research functionsYour prompt is passed to the engine as-is, with no modification
Best forEnd-user-facing flows (VP chat, Hub) and quick, general instructionsPrecise, repeatable prompts; power users; programmatic/MCP access where the caller builds the prompt itself
Research functionsSelected automatically by the prompt generatorInvoked inline by the prompt author — up to two per template (see Research functions)
Custom fieldsAvailable to the model as part of the entity contextReferenced explicitly, e.g. {{ record['campaign_name'] }}
output_schemaOptional — generated automatically from your instruction if omittedRequired — omitting it returns a validation error
When rendering a prompt_template (and when the model composes a prompt from a query), each entity is represented by a record object. The record contains:
  • Explorium profile fields resolved from the identifier. For businesses, fields such as record['OrganizationName'] and record['Description']. For prospects, fields such as record['FullName'], record['JobTitle'], and record['OrganizationName'].
  • Any custom_fields you supplied for that entity — for example record['campaign_name'].
Reference these in a prompt_template using {{ record['FieldName'] }}. Custom fields are merged into the same record namespace as the profile fields, so they’re referenced the same way. See Record fields for the full list of supported fields per entity type.

Record fields

Inside a prompt_template, reference any of the fields below with {{ record['FieldName'] }}. When you pass a query instead, these same fields are made available to the prompt generator as the entity’s context. The available fields depend on the endpoint you call.

Businesses

Available on the businesses endpoint.
FieldDescription
OrganizationNameThe company’s full organization name.
nameThe company’s common or display name.
UrlThe company’s website URL.
DescriptionA free-text description of the company and what it does.
LinkedInIndustryThe company’s industry as classified on LinkedIn.
GoogleCategoryThe company’s business category as classified by Google.
EmployeeRangeThe company’s employee headcount range (e.g. 51-200).
RevenueRangeThe company’s estimated annual revenue range.
OperationalStatusWhether the company is currently active or operational.
FoundingYearThe year the company was founded.
NaicsDescriptionThe company’s NAICS industry classification, as a text description.
SpecialtiesThe company’s stated areas of focus or specialties.
LocationThe company’s primary or headquarters location.

Prospects

Available on the prospects endpoint.
FieldDescription
FullNameThe prospect’s full name.
JobTitleThe prospect’s current job title.
JobTitleLevelThe seniority level of the prospect’s role (e.g. Manager, Director, VP, C-Suite).
JobDepartmentThe department or function the prospect works in (e.g. Sales, Engineering).
SummaryA short professional summary or bio for the prospect.
OrganizationNameThe name of the company the prospect currently works at.
UrlThe website URL of the prospect’s current company.
JobCompanyIndustryThe industry of the prospect’s current company.
JobCompanySizeThe employee-size range of the prospect’s current company.
JobCompanyDescriptionA description of the prospect’s current company.
LocationNameThe prospect’s location (city, region, and/or country).
LinkedInUrlThe URL of the prospect’s LinkedIn profile.
SkillsThe prospect’s listed skills.

Custom fields

Any key you attach in custom_fields is merged into the same record object and can be referenced exactly like a profile field — for example {{ record['campaign_name'] }}. There is no fixed list: use whatever keys you send per entity. Values are treated as strings.
Profile fields are populated from the entity’s resolved Explorium profile, so a given field may be empty when that attribute isn’t available for the entity. Write prompts that degrade gracefully when a field is missing, and instruct the model to use web research to fill gaps where appropriate.

Research functions

Inside a prompt_template, you can call research functions to ground the analysis in live web data. Render them with {{ ... }} just like a record field: the function runs first, and its output — SERP results or scraped page text — is injected into the prompt as context before the model reasons over it. Their arguments are typically built from record fields (e.g. record['OrganizationName'], record['Url']). You can use up to two research functions in a single prompt_template.
These functions apply to prompt_template only. When you pass a query, the prompt generator selects and invokes the appropriate research functions automatically based on your instruction — you don’t call them explicitly.
Runs a SERP search for query and scrapes the top max_results result pages, injecting their combined content into the prompt as grounding context. Use it when the company or person isn’t well described by the resolved profile alone and you want fresh, broad web context.
query
string
required
The search query to run. Commonly built from a record field, e.g. record['OrganizationName'].
max_results
integer
The number of top SERP results to fetch and scrape.
{{ ReadMostRelevantLinks(query=record['OrganizationName'], max_results=4) }}
{
  "business_id": "8adce3ca1cef0c986b22310e369a0793",
  "parameters": {
    "prompt_template": "{{ ReadMostRelevantLinks(query=record['OrganizationName'], max_results=4) }}\n\nUsing the company description {{ record['Description'] }} and website {{ record['Url'] }}, classify whether {{ record['OrganizationName'] }} primarily sells to businesses (B2B) or consumers (B2C). Explain your reasoning.",
    "output_schema": {
      "type": "object",
      "properties": {
        "btb_btc": {
          "type": "string",
          "enum": ["B2B", "B2C", "Both"],
          "description": "Whether the company is primarily B2B, B2C, or both"
        },
        "reasoning": {
          "type": "string",
          "description": "Brief explanation for the classification"
        }
      },
      "required": ["btb_btc", "reasoning"]
    }
  }
}

ReadUrlText

Scrapes the text content of a single URL and injects it into the prompt as grounding context. Use it when you already know the exact page to read — most often the entity’s own website via record['Url'].
url
string
required
The URL of the page to scrape, e.g. record['Url'].
{{ ReadUrlText(url=record['Url']) }}
{
  "prospect_id": "5d86c0d8eae6bdd60515685670a4a2ad49981945",
  "parameters": {
    "prompt_template": "{{ ReadUrlText(url=record['Url']) }}\n\nEvaluate whether {{ record['FullName'] }} ({{ record['JobTitle'] }} at {{ record['OrganizationName'] }}) is a strong fit for an enterprise data platform. Use summary {{ record['Summary'] }}, skills {{ record['Skills'] }}, and company website content from {{ record['Url'] }}.",
    "output_schema": {
      "type": "object",
      "properties": {
        "fit_score": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10,
          "description": "Prospect fit score from 1 to 10"
        },
        "reasoning": {
          "type": "string",
          "description": "Brief explanation for the score"
        }
      },
      "required": ["fit_score", "reasoning"]
    }
  }
}

Using two functions together

You can combine both functions in one prompt_template (the two-function maximum). Here the model reads the company’s own site with ReadUrlText and supplements it with broader web results from ReadMostRelevantLinks before answering.
{
  "business_id": "8adce3ca1cef0c986b22310e369a0793",
  "parameters": {
    "prompt_template": "{{ ReadUrlText(url=record['Url']) }}\n\n{{ ReadMostRelevantLinks(query=record['OrganizationName'], max_results=3) }}\n\nUsing the company's own website and the most relevant web results, summarize what {{ record['OrganizationName'] }} does and identify its primary target market.",
    "output_schema": {
      "type": "object",
      "properties": {
        "summary": {
          "type": "string",
          "description": "One-paragraph summary of what the company does"
        },
        "target_market": {
          "type": "string",
          "description": "The company's primary target market"
        }
      },
      "required": ["summary", "target_market"]
    }
  }
}
A prompt_template may reference at most two research functions. Adding a third returns a validation error. Each function that runs performs live web I/O, so it adds latency to that row — prefer ReadUrlText when you know the exact page, and reserve ReadMostRelevantLinks for cases that need broader discovery.

Endpoints

POST https://api.explorium.ai/v1/businesses/research/bulk_enrich
POST https://api.explorium.ai/v1/prospects/research/bulk_enrich
Authenticate with your API key in the api_key header. See the Authentication Guide for how to obtain one.

Request

The request body has two top-level keys: the entity list and the parameters. The entity list key depends on the endpoint:
  • Businesses: businesses, where each item has a business_id.
  • Prospects: prospects, where each item has a prospect_id. In both cases each item may include an optional custom_fields object, and the parameters object is identical across both endpoints.
businesses / prospects
object[]
required
The list of entities to research. Each entity is processed independently, and results are returned in the same order. Use businesses for the businesses endpoint and prospects for the prospects endpoint.
parameters
object
required
Controls how the analysis is generated and what shape it takes. Identical for both endpoints.
Provide either query or prompt_template, never both and never neither. Requests that violate this return a 422 validation error.

Examples

{
  "businesses": [
    {
      "business_id": "8adce3ca1cef0c986b22310e369a0793",
      "custom_fields": { "campaign_name": "Q1 enterprise push" }
    },
    {
      "business_id": "340c8040bd50cbab9c7df718bbe51cc9",
      "custom_fields": { "campaign_name": "SMB awareness" }
    }
  ],
  "parameters": {
    "prompt_template": "For {{ record['OrganizationName'] }}, write a one-sentence segment for campaign {{ record['campaign_name'] }}. Use description: {{ record['Description'] }}.",
    "output_schema": {
      "type": "object",
      "properties": {
        "segment": { "type": "string", "description": "One-sentence campaign segment" },
        "campaign_fit": { "type": "string", "description": "Brief assessment of campaign fit" }
      },
      "required": ["segment", "campaign_fit"]
    }
  }
}

Example request (cURL)

curl --request POST \
  --url https://api.explorium.ai/v1/businesses/research/bulk_enrich \
  --header 'accept: application/json' \
  --header 'api_key: your_api_key_here' \
  --header 'content-type: application/json' \
  --data '{
    "businesses": [
      { "business_id": "8adce3ca1cef0c986b22310e369a0793" },
      { "business_id": "340c8040bd50cbab9c7df718bbe51cc9" }
    ],
    "parameters": {
      "query": "Classify whether this company primarily sells to businesses (B2B) or consumers (B2C). Explain your reasoning.",
      "output_schema": {
        "type": "object",
        "properties": {
          "btb_btc": { "type": "string", "enum": ["B2B", "B2C", "Both"], "description": "Whether the company is primarily B2B, B2C, or both" },
          "reasoning": { "type": "string", "description": "Brief explanation for the classification" }
        },
        "required": ["btb_btc", "reasoning"]
      }
    }
  }'

Response

A successful request returns a 200 with a data array — one entry per input entity, in input order — plus a total_results count. Each successful result contains the entity identifier (business_id or prospect_id) and the generated fields defined by the output_schema. Rows that could not be processed are not dropped; they are returned with an _error field describing what went wrong.
data
object[]
One result per input entity, in input order.
total_results
integer
The total number of results returned in data.

Example response

{
  "data": [
    {
      "business_id": "8adce3ca1cef0c986b22310e369a0793",
      "btb_btc": "B2B",
      "reasoning": "The company sells software and services to enterprise organizations rather than individual consumers."
    },
    {
      "business_id": "340c8040bd50cbab9c7df718bbe51cc9",
      "btb_btc": "B2C",
      "reasoning": "Its products are marketed and sold directly to individual end users."
    }
  ],
  "total_results": 2
}

Example failed row

{
  "business_id": "f12c9a77b0e34d51a9c0b8e2d7f4a6c3",
  "_error": "Unable to resolve a profile for this identifier."
}

Per-call credit usage

The Research endpoints support the opt-in Per-Call Credit Usage header. Send credit-usage: true to receive a credit_usage object alongside the response, reporting the exact credits deducted for the call. This is the recommended way to track research costs while you tune prompts and schemas during the beta.
credit-usage: true

Best practices

  • Be explicit in your schema. Use clear field descriptions, and enum / minimum / maximum constraints, so the model returns consistent, parseable values.
  • Keep outputs focused. Smaller, well-scoped schemas produce more reliable results than large free-form objects.
  • Tell the model when to use the web. If a task may need information beyond the resolved profile, say so in your instruction (e.g. “use web search to supplement missing context”).
  • Prefer prompt_template for repeatable pipelines where you need full control over wording, injected fields, and inline research functions; use query for quick, natural-language instructions.
  • Resolve identifiers first with Match Businesses or Match Prospects so the engine has a profile to work from.
  • Attach context with custom_fields (e.g. campaign name, segment) rather than hard-coding it into every prompt.
  • Handle _error rows. Iterate over data and check for _error before reading the generated fields.
Rate limits are counted per query, not per request. Each entity in the businesses / prospects array counts as a separate query toward your rate limit. A single request that researches 50 entities consumes 50 queries from your 200-queries-per-minute limit — not 1. Batching reduces HTTP/network overhead, but it does not reduce the number of queries counted against your rate limit. Size your batches accordingly.

Errors

422 Validation Error
object
Returned when the request body is invalid — for example when both query and prompt_template are provided, when neither is provided, when prompt_template is used without an output_schema, or when the output_schema is malformed.
See Error Handling for the full list of status codes and retry guidance.