Skip to main content
POST
/
search
/
reasoning
curl -X POST https://api.dataforb2b.ai/search/reasoning \
  -H "api_key: YOUR_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Heads of Sales at French fintech scale-ups",
    "category": "people",
    "max_results": 25
  }'
{
  "status": "needs_input",
  "session_id": "rs_8f2c1a9b",
  "reasoning": "Fintech scale-up can mean different sizes — let me confirm.",
  "questions": [
    {
      "id": "company_size",
      "text": "What company size do you consider a scale-up?",
      "suggestions": ["51-200", "201-500", "501-1000"]
    }
  ]
}
An AI agent reasons about your query, probes the database in real time, asks clarifying questions when needed, then runs the best final search. Unlike a one-shot search, Reasoning Search is conversational: you can answer questions and refine results across turns using the same session_id.

Credit Cost

ItemCredits
Agent fee (per completed search)2 credits
Result (enrich_live: false)0.75 credits per result
Result (enrich_live: true)1.5 credits per result
Clarification turns (status: "needs_input") are free — the agent fee is only charged when a search actually runs.

Request Body

query
string
Natural language query (max 3000 characters). Required on the first call. On follow-up calls with a session_id, use it to refine the previous search (e.g. "now only in San Francisco").
session_id
string
Session identifier returned by a previous call. Send it back to answer questions or refine the same search.
answers
object
Answers to the questions of a needs_input turn, as {question_id: answer}.
category
string
default:"people"
What to search: "people" or "companies".
max_results
integer
default:"25"
Number of results for the final search (1–100).
enrich_live
boolean
default:"false"
If true, results are enriched with fresh live data (1.5 credits per result instead of 0.75).
Pagination: this endpoint always returns the first max_results results. To get the next pages, take the applied_filters from the response and call /search/people or /search/companies with them, setting offset (e.g. offset: 25, offset: 50, …). You only pay the agent fee once — pagination is billed at the standard per-result rate.

Flow

1

Send your query

POST /search/reasoning with query. The response is either complete (results) or needs_input (questions).
2

Answer questions (if asked)

Send back session_id + answers. The agent continues and returns results.
3

Refine (optional)

Send session_id + a new query to refine the same search — the agent remembers the conversation.

POST /search/reasoning/stream takes the exact same body but returns Server-Sent Events, so your users see the agent think, probe the database and build the search in real time — instead of staring at a spinner for 20–60 seconds. Events arrive in this order:
EventPayloadWhen
step{type: "thought" | "action" | "observation", ...}Each reasoning step, live
questions{session_id, reasoning, questions}The agent needs clarification
result{session_id, reasoning, applied_filters, category, total, count, has_next_page, credits_used, results}Final results
error{message}Something went wrong
done{}Always last — close the stream
The three step types map naturally to UI elements:
  • thought{type, text}: the agent’s narration. Render as a status line.
  • action{type, tool, filters?, resolve?}: a database probe about to run (resolve_value, find_companies, probe_count, preview_results, then a final search). filters shows the exact filters being tested.
  • observation{type, tool, result}: the probe’s outcome, e.g. {"count": 183} for a probe_count.
Since SSE over POST isn’t supported by EventSource, consume the stream with fetch:
const res = await fetch('https://api.dataforb2b.ai/search/reasoning/stream', {
  method: 'POST',
  headers: { api_key: 'YOUR_api_key', 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: 'CTOs of AI startups in Paris', max_results: 25 })
});

const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  // SSE frames are separated by a blank line
  const frames = buffer.split('\n\n');
  buffer = frames.pop(); // keep the incomplete frame

  for (const frame of frames) {
    const event = frame.match(/^event: (.+)$/m)?.[1];
    const data = JSON.parse(frame.match(/^data: (.+)$/m)?.[1] ?? '{}');

    if (event === 'step' && data.type === 'thought') showStatus(data.text);
    if (event === 'step' && data.type === 'observation') showProbe(data.tool, data.result);
    if (event === 'questions') askUser(data.session_id, data.questions);
    if (event === 'result') showResults(data);
    if (event === 'error') showError(data.message);
  }
}
Python
import json, requests

with requests.post(
    "https://api.dataforb2b.ai/search/reasoning/stream",
    headers={"api_key": "YOUR_api_key", "Content-Type": "application/json"},
    json={"query": "CTOs of AI startups in Paris", "max_results": 25},
    stream=True,
) as r:
    event = None
    for line in r.iter_lines(decode_unicode=True):
        if line.startswith("event: "):
            event = line[7:]
        elif line.startswith("data: "):
            data = json.loads(line[6:])
            if event == "step":
                print(f"[{data['type']}] {data.get('text') or data.get('tool')}")
            elif event == "result":
                print(f"Done: {data['total']} results, {data['credits_used']} credits")
A real stream looks like this:
event: step
data: {"type": "action", "tool": "resolve_value", "resolve": {"type": "category", "q": "artificial intelligence"}}

event: step
data: {"type": "observation", "tool": "resolve_value", "result": {"matches": [{"value": "artificial intelligence", "popularity": "10k-100k"}]}}

event: step
data: {"type": "action", "tool": "find_companies", "filters": {"op": "and", "conditions": [...]}}

event: step
data: {"type": "observation", "tool": "find_companies", "result": {"total": 680, "returned": 680}}

event: step
data: {"type": "action", "tool": "probe_count", "filters": {"op": "and", "conditions": [...]}}

event: step
data: {"type": "observation", "tool": "probe_count", "result": {"count": 183, "is_capped": false}}

event: step
data: {"type": "action", "tool": "search"}

event: result
data: {"session_id": "...", "reasoning": "Found 680 AI startups in Paris, then filtered CTOs (183 matches).", "total": 203, "count": 25, "credits_used": 20.75, "results": [...]}

event: done
data: {}

Response

status
string
"complete" (results below) or "needs_input" (answer the questions).
session_id
string
Send it back to answer questions or refine the search.
reasoning
string
Summary of what the agent searched for and why.
questions
array
Only when status is needs_input. Each question has id, text and optional suggestions (display them as choice chips in your UI).
applied_filters
object
Only when status is complete. The final filters built by the agent. You can reuse them directly on /search/people or /search/companies to paginate.
total
integer
Total number of matching results.
count
integer
Number of results returned.
credits_used
number
Total credits charged (agent fee + results).
results
array
Profiles or companies, same structure as /search/people / /search/companies.
curl -X POST https://api.dataforb2b.ai/search/reasoning \
  -H "api_key: YOUR_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Heads of Sales at French fintech scale-ups",
    "category": "people",
    "max_results": 25
  }'
{
  "status": "needs_input",
  "session_id": "rs_8f2c1a9b",
  "reasoning": "Fintech scale-up can mean different sizes — let me confirm.",
  "questions": [
    {
      "id": "company_size",
      "text": "What company size do you consider a scale-up?",
      "suggestions": ["51-200", "201-500", "501-1000"]
    }
  ]
}