Integrations · CrewAI
Wrap RichAPI endpoints as CrewAI tools, or point the crew at our hosted MCP server. The thing that bites is budget: an autonomous crew with a research loop will call a tool more times than you expected, so cap iterations and give the agent the execution_log so it can tell a miss from a bad input.
Last updated September 22, 2026
Create a RichAPI account at app.richapi.ai/signup and copy your API key.
25 free credits, no card.
Export it as RICHAPI_KEY.
Never inline a key in a tool's source. CrewAI serialises tool definitions into prompts and traces.
Write a BaseTool subclass per endpoint, or one tool with an endpoint argument.
Two or three narrow tools beat one generic HTTP tool: the agent picks better when the tool name says what it does.
Alternatively skip the wrappers.
CrewAI can consume an MCP server, and ours is hosted at https://mcp.richapi.ai/mcp with an x-api-key header. Same endpoints, same key, same credit pool.
Set max_iter on any agent that holds an enrichment tool, and return the credit cost in the tool's output string so the agent's own reasoning includes what it just spent.
Copy, paste, edit the brackets, run.
Why it matters
A crew that reasons over invented company facts is a very expensive hallucination generator. Wiring one live lookup tool into the researcher role turns the crew's output from plausible to checkable, and the per-call receipt means the agent can explain a gap instead of filling it in.
CrewAI's whole premise is roles. A researcher gathers, an analyst judges, a writer drafts. The premise falls over at the first step, because a researcher agent with no data tool researches by recalling training data, and training data does not know who runs marketing at a 40-person company that raised in March. There is no RichAPI tool in `crewai-tools`. You write the wrapper, which is about fifteen lines, or you attach our MCP server and write none.
```python import os, requests from crewai.tools import BaseTool from pydantic import BaseModel, Field class FindEmailArgs(BaseModel): full_name: str = Field(description="Person's full name") company_domain: str = Field(description="Company domain, e.g. acme.com") class FindWorkEmail(BaseTool): name: str = "find_work_email" description: str = ( "Find a verified work email from a full name and company domain. " "Returns the address and its verification status, or reports a miss " "with the list of providers that were tried." ) args_schema: type[BaseModel] = FindEmailArgs def _run(self, full_name: str, company_domain: str) -> str: r = requests.post( "https://api.richapi.ai/api/v1/email_finder", headers={"x-api-key": os.environ["RICHAPI_KEY"]}, json={"full_name": full_name, "company_domain": company_domain}, ).json() if not r.get("success"): tried = [a.get("status") for a in r.get("execution_log", [])] return f"No email found for {full_name} at {company_domain}. Providers tried: {tried}. Nothing was billed." return f"{r['data']['email']} (status: {r['data']['status']}, cost: 5 credits)" @agent def researcher(self) -> Agent: return Agent(config=self.agents_config["researcher"], tools=[FindWorkEmail()], max_iter=6) ``` Two details in there are doing real work. The miss branch returns the provider statuses as prose. A tool that returns `null` teaches the agent nothing, so it retries with the same arguments and gets the same nothing. A tool that says *four providers ran, three had no data, one timed out* gives the agent something to reason with: retry, try the LinkedIn URL instead, or move on. The success branch names the cost. The agent's own context now contains what it spent, which is the cheapest budget control that exists in an autonomous loop.
Skip the wrappers entirely. We host an MCP server at `https://mcp.richapi.ai/mcp`, authenticated with the same `x-api-key` header, exposing the same endpoints on the same credit pool. CrewAI can consume MCP tool sources, so the whole catalog arrives without you maintaining a Python class per endpoint. The transport is standard streamable HTTP, and the config format on the CrewAI side moves faster than this page does. Write wrappers when you want a narrow, well-named tool surface and full control over what the agent sees on a miss. Use MCP when you want the whole catalog and do not care to curate it.
This is the part that surprises people coming from a plain script. A sequential chain calls `email_finder` once per row. A crew with an agent that decides for itself calls it once, gets a miss, decides to try `find_linkedin_url_by_name` first, then calls it again. That is often the right call, and it is also three lookups where you budgeted one. Three controls, in order of how much they help: 1. **`max_iter` on any agent holding a paid tool.** Bounds the loop. Do this one. 2. **Cost in the tool's return string**, as above. The agent factors spend into its own decisions. 3. **A narrow tool surface.** An agent given `find_work_email` and `enrich_company` behaves. An agent given a generic `call_richapi(endpoint, body)` will explore. | Endpoint | Cost | | --- | --- | | `email_finder` | `5 credits` | | `email_verifier` | `2 credits` | | `phone_finder` | `25 credits` | | `enrich_company` / `enrich_profile` | `1 credit` | | `people_search` | `0.1 credits per result` | Full price only on results, scoped to the waterfall endpoints: `email_finder`, `email_verifier` and `phone_finder` cost zero when nothing is found. The rest bill on a successful 2xx even when the payload is thin. Tiers on [/pricing](/pricing).
We are the lookup layer. No sending, no sequencing, no CRM writes, no prospecting UI, so the worst outcome of a badly scoped crew is spent credits, not an email to a customer with `{{first_name}}` in the subject line. Neighbours, and the full [integrations index](/integrations): [LangChain](/integrations/langchain) if you want typed tool schemas and chains rather than roles, [OpenAI Agents SDK](/integrations/openai-agents) for handoffs and tracing, [LangGraph](/integrations/langgraph) when the loop needs to be a graph you can see. Or [Claude](/integrations/claude) to prove the prompt by hand before you build the crew.
**Is there an official RichAPI tool in crewai-tools?** No. You write the `BaseTool`, or attach the MCP server. **Does `enrich_company` take a domain?** No, a LinkedIn URL. Give the agent `find_website_by_company_name` or a search step if it only has a name. Full shape in [company enrichment API](/api/company-enrichment). **Can the crew spend my whole balance?** It can spend what the tool costs times the number of calls, which is why `max_iter` exists and why we give you 25 free credits to watch it behave first. **Can I hand the crew a list?** Use `kickoff_for_each`, or do the bulk pass outside the crew and let the crew reason over the results. See [bulk enrich without seats](/use-cases/bulk-enrich-without-seats). **Does it work with a local model?** The API does not care what is calling it. Anything that can issue an HTTP POST works.
25 free credits, no card. Paste the tool above into your crew and give the researcher something real to research.