> ## Documentation Index
> Fetch the complete documentation index at: https://www.forager.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Forager is a REST API for B2B people, company and job post data: contact lookups (phone numbers, personal emails, work emails), person, company and job post search, and bulk data feeds.
> Server: https://api-v2.forager.ai. Send the API key in the X-API-KEY header. Most paths include {account_id}. Read FORAGER_API_KEY and FORAGER_ACCOUNT_ID from the environment; never put the key in client-side code, logs or prompts.
> To add Forager to a project, follow https://www.forager.ai/docs/guides/agent-quickstart.md. To pick an endpoint, read https://www.forager.ai/docs/guides/choose-an-endpoint.md. For end-to-end builds, see https://www.forager.ai/docs/recipes/overview.md.
> Most requests spend credits. Do not guess costs; use https://www.forager.ai/docs/api-overview/credit-pricing.md and ask the user before spending credits.
> Use only endpoints and fields defined in https://www.forager.ai/docs/openapi.yaml. Forager has no MCP server for the API yet; the /mcp endpoint on this site searches the documentation only.

# AI agent tool calling for B2B data

> Expose two Forager REST operations as tools for an AI agent, then run the tool-calling loop server-side with a call budget and trimmed results.

```text Recipe prompt theme={"theme":{"light":"github-light","dark":"github-dark"}}
Implement this recipe in my project: https://www.forager.ai/docs/recipes/agent-tool-calling.md

Expose two Forager operations, person role search and phone numbers lookup, as tools in our model provider's tool-calling API. Write a server-side loop that sends the tools to the model, executes each tool call against the Forager REST API, and returns trimmed JSON results. Cap the number of calls per run, return not-found and 429 outcomes to the model as data, and treat a 403 as a fatal error that never reaches the model. Keep the API key server-side in FORAGER_API_KEY with FORAGER_ACCOUNT_ID. Add tests with a fake model and stubbed HTTP.
```

![AI agent tool calling: an agent sends a lookup to the Forager REST API](https://mintcdn.com/forager/OnhSHNo_LBQ7yKjR/images/recipes/ai-agent-tool-calling-b2b-data.svg?fit=max\&auto=format\&n=OnhSHNo_LBQ7yKjR\&q=85\&s=18daa567361830e1e8bbd2aa425573d4)

## What you'll build

A server-side loop that lets a model decide when to search for people and when to look up a phone number. The model sees two tool definitions. Your code runs the calls against the Forager REST API, so the key never reaches the model or a browser.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart LR
  A[User request] --> B[Model]
  B -->|tool call| C[Your executor]
  C --> D[Forager REST API]
  D --> C
  C -->|trimmed result| B
  B --> E[Answer]
```

## Steps

**1. Define the tools**

Each tool is a name, a description and a JSON Schema for its arguments. Most model providers accept this shape in their tool-calling API; rename the wrapper keys to match yours. The descriptions matter: they tell the model when a call is worth its credits.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
[
  {
    "name": "search_people_by_role",
    "description": "Find people by job title, optionally limited to company domains. Returns up to one page of matches with person ids. Costs credits per call.",
    "parameters": {
      "type": "object",
      "properties": {
        "role_title": { "type": "string", "description": "Job title. Supports a boolean text query." },
        "role_is_current": { "type": "boolean", "description": "Only current roles." },
        "organization_domains": { "type": "array", "items": { "type": "string" }, "description": "Company domains, for example example.com." },
        "page": { "type": "integer", "description": "Page number." }
      },
      "required": ["role_title"]
    }
  },
  {
    "name": "find_phone_numbers",
    "description": "Get phone numbers for one person by person_id. Costs more credits than a search, so call it only for people the user asked about.",
    "parameters": {
      "type": "object",
      "properties": {
        "person_id": { "type": "integer", "description": "The id from search_people_by_role." }
      },
      "required": ["person_id"]
    }
  }
]
```

**2. Execute a tool call**

Map each tool to one request. These are the two requests your executor sends.

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api-v2.forager.ai/api/$FORAGER_ACCOUNT_ID/datastorage/person_role_search/" \
  -H "X-API-KEY: $FORAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"role_title": "Head of Data", "role_is_current": true, "organization_domains": ["example.com"]}'

curl -X POST "https://api-v2.forager.ai/api/$FORAGER_ACCOUNT_ID/datastorage/person_contacts_lookup/phone_numbers/" \
  -H "X-API-KEY: $FORAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"person_id": 123}'
```

```python Python (requests) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os, time, requests

BASE = f"https://api-v2.forager.ai/api/{os.environ['FORAGER_ACCOUNT_ID']}/datastorage"
HEADERS = {"X-API-KEY": os.environ["FORAGER_API_KEY"]}
ENDPOINTS = {
    "search_people_by_role": "person_role_search/",
    "find_phone_numbers": "person_contacts_lookup/phone_numbers/",
}

def execute(name, args):
    if name not in ENDPOINTS:
        return {"error": "unknown_tool"}
    for attempt in range(2):
        r = requests.post(BASE + "/" + ENDPOINTS[name], headers=HEADERS, json=args, timeout=30)
        if r.status_code == 429 and attempt == 0:
            time.sleep(2)
            continue
        break
    if r.status_code == 403:
        raise RuntimeError("Forager rejected the API key")  # fatal: never show the model
    if r.status_code == 429:
        return {"error": "rate_limited"}
    r.raise_for_status()
    data = r.json() if r.content else []
    if name == "search_people_by_role":
        return {"total": data["total_search_results"], "people": [
            {"person_id": h["person"]["id"], "name": h["person"]["full_name"],
             "title": h["role_title"], "company": h["organization"]["name"]}
            for h in data["search_results"]]}
    return {"phone_numbers": [row["phone_number"] for row in data]}
```

```javascript JavaScript (fetch) theme={"theme":{"light":"github-light","dark":"github-dark"}}
const BASE = `https://api-v2.forager.ai/api/${process.env.FORAGER_ACCOUNT_ID}/datastorage/`;
const ENDPOINTS = {
  search_people_by_role: "person_role_search/",
  find_phone_numbers: "person_contacts_lookup/phone_numbers/",
};

async function execute(name, args) {
  if (!(name in ENDPOINTS)) return { error: "unknown_tool" };
  let res;
  for (let attempt = 0; attempt < 2; attempt++) {
    res = await fetch(BASE + ENDPOINTS[name], {
      method: "POST",
      headers: { "X-API-KEY": process.env.FORAGER_API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify(args),
    });
    if (res.status !== 429 || attempt === 1) break;
    await new Promise((ok) => setTimeout(ok, 2000));
  }
  if (res.status === 403) throw new Error("Forager rejected the API key");
  if (res.status === 429) return { error: "rate_limited" };
  if (!res.ok) throw new Error(`Forager ${res.status}`);
  const text = await res.text();
  const data = text ? JSON.parse(text) : [];
  if (name === "find_phone_numbers") return { phone_numbers: data.map((r) => r.phone_number) };
  return {
    total: data.total_search_results,
    people: data.search_results.map((h) => ({
      person_id: h.person.id, name: h.person.full_name,
      title: h.role_title, company: h.organization.name,
    })),
  };
}
```

**3. Run the loop**

`call_model` is a function you write (the code below has a placeholder) around your model provider's tool-calling API. It sends the messages and the tool definitions, and returns the reply's text and any tool calls, each with an `id`, a `name` and `arguments` as a dict.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json

MAX_CALLS = 6  # per run, to cap credit spend

def call_model(messages, tools):
    # Replace with a call to your model provider's tool-calling API.
    raise NotImplementedError

def run_agent(messages, tools):
    calls_made = 0
    while True:
        reply = call_model(messages, tools)
        if not reply.tool_calls:
            return reply.text
        messages.append(reply.raw)  # the assistant turn, in your provider's format
        for call in reply.tool_calls:
            calls_made += 1
            if calls_made > MAX_CALLS:
                result = {"error": "call_budget_reached"}
            else:
                result = execute(call.name, call.arguments)
            messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
```

The role and field names for tool results differ between providers. Adapt the two `messages.append` lines to yours.

## Handle the edge cases

* **Not found.** An empty `people` or `phone_numbers` list is a result, not an error. Return it so the model can tell the user.
* **403.** The key is missing or invalid. The code raises, so the failure reaches you and not the model.
* **429.** The code retries once, then returns `rate_limited` as data so the model can stop or wait. See [errors and rate limits](https://www.forager.ai/docs/errors-and-rate-limits.md).
* **Credits.** A role search costs 1 credit per request and a phone number lookup 15 credits per lookup, so a loop with no cap can spend fast. Keep a per-run call cap and log every call. See [credit pricing](https://www.forager.ai/docs/api-overview/credit-pricing.md).
* **Trimmed results.** Return only the fields the model needs. Full person records are large and slow the model down.

## Related

> [card] [Build with AI agents](https://www.forager.ai/docs/guides/build-with-agents.md)
>
> Docs and the OpenAPI spec for agents.

> [card] [Search people by role](https://www.forager.ai/docs/guides/search-people-by-role.md)
>
> Every role search filter.

> [card] [Person role search](https://www.forager.ai/docs/openapi/people/datastorage_person_role_search_create.md)
>
> Request and response reference.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.