Integrations · OpenAI Agents SDK
OpenAI Agents SDK Enrichment — Function Tools over RichAPI
Two paths: decorate Python functions with @function_tool, or attach our hosted MCP server and skip the wrappers entirely. The SDK's tracing gives you a span per tool call, which pairs with our per-call execution_log to explain exactly why a lookup came back empty.
In short: RichAPI integrates with the OpenAI Agents SDK (the openai-agents Python package) in two ways: as function tools created with the @function_tool decorator wrapping calls to the REST API at https://api.richapi.ai/api/v1/{endpoint} with an x-api-key header, or by attaching RichAPI's hosted Model Context Protocol server at https://mcp.richapi.ai/mcp, which the SDK supports as a streamable-HTTP MCP server. There is no OpenAI-published RichAPI connector. Endpoints include enrich_company and enrich_profile (which take a LinkedIn URL, not a domain), email_finder, email_verifier, phone_finder, people_search, find_linkedin_url_by_email, post_keyword_search and web_tech_stack. The @function_tool decorator derives a strict JSON schema from Python type hints and the docstring. Waterfall endpoints (email_finder, email_verifier, phone_finder) return HTTP 200 with success false and billed false on a miss and cost zero credits; every other endpoint bills on a successful 2xx response. `execution_log` pairs with the SDK's tracing spans. Waterfall calls return one entry per attempt with a status, so the trace shows the tool call and the log shows what happened inside it. Agents should set max_turns to bound credit spend. RichAPI does not send email or write to a CRM. New accounts get 100 free credits with no credit card.
Last updated November 4, 2026
Connect it
1. Create a RichAPI account at app.richapi.ai/signup and copy your API key.
100 free credits, no card.
2. pip install openai-agents, export RICHAPI_KEY alongside your OpenAI key.
3. Wrap each endpoint with @function_tool.
The SDK derives a strict JSON schema from your type hints and docstring, so annotate properly. A `str` where you meant a bare domain is a schema the model will happily satisfy with a full URL.
4. Or attach the MCP server: RichAPI is hosted at https://mcp.richapi.ai/mcp over streamable HTTP with an x-api-key header, and the SDK's MCP support loads the whole catalog as tools.
5. Set max_turns on Runner.run and turn on tracing.
A tool call that spends credits deserves a span you can open.
OpenAI Agents SDK
What you can ask for
Account research agent: given a domain, return a brief with headcount, industry, the senior-most GTM leader, and a verified email, plus a note for any field it could not retrieve.
Triage agent that hands off to a sourcing agent when the inbound lead has no company data attached.
Guardrail that refuses to pass a lead downstream unless email_verifier returned valid, not catch-all.
Agent over post_keyword_search: find who discussed a topic last week, enrich the top 20 by seniority, return a ranked table.
Given a CSV of 200 LinkedIn URLs, enrich each profile and report cost per enriched row at the end.
Why it Matters
The Agents SDK is deliberately thin, which means the interesting behaviour is in the tools and the tracing. A lookup tool that returns its own provider log turns an agent trace from 'the model said it could not find it' into a span you can read and act on.
OpenAI Agents SDK enrichment: thin SDK, so the tool has to be good
The Agents SDK does very little on purpose. Agents, handoffs, guardrails, tracing, and a decorator that turns a Python function into a tool. That thinness is the appeal, and it also means an agent is exactly as good as the tools you hand it. There is no framework layer to paper over a lookup that returns None with no explanation.
There is no OpenAI-published RichAPI connector. Write the function, or attach the MCP server.
Function tools
import os, requests
from dataclasses import dataclass
from agents import Agent, Runner, function_tool
@dataclass
class EmailHit:
email: str | None
status: str | None
found: bool
note: str
@function_tool
def find_work_email(full_name: str, company_domain: str) -> EmailHit:
"""Find a verified work email for a person at a company.
Args:
full_name: The person's full name, e.g. "Jane Okafor".
company_domain: Bare domain only, e.g. "acme.com". Not a URL.
"""
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 EmailHit(None, None, False, f"Waterfall exhausted, sources returned {tried}. Nothing billed.")
d = r["data"]
return EmailHit(d["email"], d["status"], True, "5 credits")
agent = Agent(
name="Account researcher",
instructions="Research the account. Never invent a contact detail. "
"If a lookup misses, say so and report what was tried.",
tools=[find_work_email],
)
result = await Runner.run(agent, "Who runs engineering at acme.com, and what is their email?", max_turns=8)
The decorator reads the type hints and the Google-style docstring and builds a strict JSON schema from them. That is genuinely useful and also the thing people under-use: the Args: block is the only place the model learns that company_domain means acme.com and not https://acme.com/careers.
The note field is deliberate. It carries either the cost or the reason for the miss into the model's context, so the agent's summary can say "no email found, four sources returned no data" instead of quietly omitting the person.
Attaching the MCP server instead
The SDK speaks MCP natively. Ours is hosted, with no local process and no package to install:
from agents.mcp import MCPServerStreamableHttp
richapi = MCPServerStreamableHttp(
params={"url": "https://mcp.richapi.ai/mcp",
"headers": {"x-api-key": os.environ["RICHAPI_KEY"]}},
)
agent = Agent(name="Researcher", mcp_servers=[richapi])
. The transport is standard streamable HTTP and the header is the same one REST uses.
Same key, same credit pool, whole catalog. Use tool_filter to cut it down, because an agent offered the whole catalog picks worse than one offered four.
Tracing is the reason to use this SDK for enrichment
Every run produces a trace with a span per tool call. Open a failed run and you see the arguments the model sent, which is how you find out it has been passing "Jane Okafor, VP Eng" as full_name for three weeks.
Pair that with what we return. A waterfall response includes execution_log showing who ran and what each returned. Put it in the tool's return value and the span shows both halves: what the model asked for, and what actually happened underneath.
We do not publish the source list. It changes, and a per-call record beats a marketing page either way.
Spend control
max_turns bounds the loop. Beyond that, an input guardrail that rejects a request with no domain in it stops the agent from spending a lookup to discover the input was junk, and an output guardrail that checks email_verifier returned valid rather than catch_all stops a bad row reaching your sequencer.
| Endpoint | Cost |
|---|---|
email_finder |
5 credits |
email_verifier |
2 credits |
phone_finder |
25 credits |
enrich_company / enrich_profile |
1 credit |
find_linkedin_url_by_email |
4 credits |
people_search |
0.1 credits per result |
Full price only on results, and only on the waterfall: email_finder, email_verifier, phone_finder cost zero when nothing is found. The rest bill on a successful 2xx, empty payload included. Non-2xx never bills. Tiers on /pricing.
Where we stop with OpenAI Agents
Read-only lookups. No sending, no sequencing, no CRM app, no prospecting UI. A runaway agent costs you credits, not a customer relationship.
Siblings, from the integrations index: LangChain for typed chains, CrewAI for role-based crews, ChatGPT to test the prompt before you write any code, and agent-driven account research for the full recipe.
OpenAI Agents FAQ
Is there an official OpenAI connector for RichAPI? No. Function tools or our MCP server.
Does this work with the Responses API directly? Yes. The tool definitions are plain JSON schemas. The SDK is a convenience, not a requirement.
Can I use a non-OpenAI model? The SDK supports other providers through LiteLLM, and our API does not care who calls it.
Why is phone_finder so much more expensive? Mobile numbers cost more to source than work emails. It also misses more often, and a miss is free, so run it only on accounts worth the attempt.
Does enrich_company accept a domain? No, a LinkedIn company URL. See company enrichment API.
Start with OpenAI Agents
100 free credits, no card. Wire one function tool, run one agent, and read the trace.
FAQ
Frequently asked questions
More from RichAPI
More companies
Browse company profiles with company overviews, key details, and email formats.
Get 100 free credits to explore RichAPI with no card required.
Free tools
Use our Email Permutator, llms.txt Generator, Waterfall Calculator, and live API demo — all free to try.