Integrations · OpenAI Agents SDK
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.
Last updated September 22, 2026
Create a RichAPI account at app.richapi.ai/signup and copy your API key.
25 free credits, no card.
pip install openai-agents, export RICHAPI_KEY alongside your OpenAI key.
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.
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.
Set max_turns on Runner.run and turn on tracing.
A tool call that spends credits deserves a span you can open.
Copy, paste, edit the brackets, run.
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.
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.
```python 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, providers 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 providers returned no data" instead of quietly omitting the person.
The SDK speaks MCP natively. Ours is hosted, with no local process and no package to install: ```python 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.
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 provider list. It changes, and a per-call record beats a marketing page either way.
`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](/pricing).
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](/integrations): [LangChain](/integrations/langchain) for typed chains, [CrewAI](/integrations/crewai) for role-based crews, [ChatGPT](/integrations/chatgpt) to test the prompt before you write any code, and [agent-driven account research](/use-cases/agent-driven-account-research) for the full recipe.
**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](/api/company-enrichment).
25 free credits, no card. Wire one function tool, run one agent, and read the trace.