Announcing CIMD support for MCP Client registration
Learn more

Exa MCP vs Exa API for AI Agents (2026)

Nityashree Yadunath
Product Marketing Manager

TL;DR

  • Exa's hosted MCP server currently exposes four tools: web_search_exa and web_fetch_exa are on by default, web_search_advanced_exa and agent_run are opt-in via a tools query parameter. The REST API adds Contents, Answer, Monitors, Websets, and team key management, none of which have an MCP tool.
  • Exa MCP is not OAuth-only. It runs unauthenticated on a free tier, accepts an x-api-key header, and supports browser OAuth sign-in. Headless agents work on both paths, so the auth question is not "can I run this without a user" but "whose key is this call burning."
  • Exa's unit of identity is a team API key, not a delegated user grant. In a multi-tenant B2B agent, that means N API keys to store, scope, and revoke regardless of whether you chose MCP or REST.
  • The MCP path is the faster route for interactive research assistants and coding agents. The REST API is the right foundation for scheduled monitors, large-scale list building with Websets, and any pipeline where you need explicit control over search type, content extraction, and cost.
  • Scalekit ships two Exa connectors, one for the API and one for the MCP surface, and both resolve a per-user Exa key from a vault at call time. The MCP vs API decision does not change your credential infrastructure.

Your agent needs to search and read the web. Exa ships a hosted MCP server at mcp.exa.ai and a full REST API at api.exa.ai, and unlike most tools in this series, the MCP path here does not lock you out of headless execution. Exa MCP accepts a static API key in a header. That removes the usual "MCP cannot run without a browser" blocker and replaces it with a subtler one: capability coverage and per-user key isolation. Here's how to pick.

What Exa MCP and Exa API actually are

These are two access layers over the same index, maintained by the same team, with very different surface areas. The distinction that matters is not protocol; it is how much of Exa's platform each one lets your agent reach.

Exa MCP

Exa MCP is an open-source, Exa-maintained server available as a hosted endpoint at https://mcp.exa.ai/mcp and as an npm package for local execution. It speaks Streamable HTTP and installs into Cursor, VS Code, Claude Code, Codex, and most other MCP clients with a single URL.

Authentication is unusually flexible for a hosted MCP server. Exa introduced a free unauthenticated tier in February 2026, rate-limited to 3 QPS and 150 calls per day; past that you attach your own key via an x-api-key header, and OAuth sign-in is supported for usage-based tools. Official documentation lives on the Exa MCP reference page, with the product overview at exa.ai/mcp and source at the exa-labs/exa-mcp-server repository.

The Exa API

The Exa REST API is a multi-surface platform, not a single search endpoint. It covers /search (six search types from instant through deep-reasoning), /contents for extraction from known URLs, /answer for synthesized answers with citations, /agent/runs for long-running research and enrichment, /monitors for scheduled searches with webhook delivery, the Websets API for large-scale list building, and a team-management API for programmatic key issuance.

Authentication is a bearer credential: pass the key as x-api-key or as Authorization: Bearer. There is no per-end-user OAuth grant model on the API surface. Start with the Exa Search API guide and the full /search reference.

Comparing them where it matters for agents

Four dimensions decide this for a production agent: what the tools can do, which credential model you inherit, what you own operationally, and where each path wins.

What your agent can actually do

The MCP server covers the search-and-read loop well and deliberately stops there. Everything stateful, scheduled, or bulk lives on the API.

Capability
Exa MCP (hosted)
Exa API
Web search with clean content
Yes (web_search_exa)
Yes (/search)
Fetch full page content by URL
Yes (web_fetch_exa)
Yes (/contents)
Category, domain, and date filters
Opt-in (web_search_advanced_exa)
Yes, full parameter set
Search type selection
Not exposed as a tool parameter
Yes
Structured synthesized output via outputSchema
Only inside agent_run
Yes, on /search directly
Server-sent event streaming
No
Yes (stream: true)
Multi-step research and enrichment runs
Opt-in (agent_run)
Yes (/agent/runs)
Run lifecycle control (poll, cancel, delete, replay events)
No
Yes
Find similar pages to a URL
No
Yes
Scheduled recurring search with webhooks
No
Yes (Monitors)
Large-scale list building and enrichment
No
Yes (Websets)
Programmatic API key issuance and usage analytics
No
Yes (team management)

Where the MCP surface stops

Three gaps are structural rather than temporary. Monitors are the clearest: if your agent needs to react to new funding announcements or competitor launches over time, that requires Exa Monitors with a webhook endpoint, and there is no MCP tool for it.

Websets are the second. Discovering and verifying thousands of entities is an asynchronous, multi-object workflow spanning searches, items, enrichments, and imports. The Websets API guide describes a surface the MCP server does not model at all.

The third is run control. agent_run executes the whole loop in a single call and returns when it finishes. The Agent API lets you create, poll, stream, cancel, and replay events on a run. For a background pipeline that needs to abort a run when a user closes a session, that difference is not cosmetic.

The auth path each one puts you on

Exa MCP supports three authentication postures: anonymous free tier, static API key in an x-api-key header or exaApiKey query parameter, and browser OAuth sign-in for usage-based tools like agent_run. The REST API supports one: a bearer API key.

This inverts the usual MCP tradeoff. For Notion, Slack, and Salesforce, the hosted MCP forces an interactive OAuth flow and disqualifies background agents. Exa MCP does not. A nightly research pipeline can call mcp.exa.ai with a header and no browser in the loop.

Why the multi-tenant problem is worse here, not better

Because Exa's credential is a team-scoped API key rather than a per-user delegated grant, there is no provider-side mechanism that makes one user's agent see only that user's entitlements. Whatever isolation you get, you build.

That has a specific production consequence. Exa's documented rate limits apply per key: 10 QPS on /search, 100 QPS on /contents, 10 QPS on /answer, and Agent concurrency at one fifth of your account QPS, which is two simultaneous runs on a default pay-as-you-go account. One shared key across forty customers means one shared QPS ceiling and one shared spend line. A single tenant running a Websets job starves everyone else. This is the same structural problem described in how tool calling auth changes when you move from single-tenant to multi-tenant.

What you own in production

On the MCP path, Exa maintains the server, the tool schemas, and the tool descriptions. That is real operational value: when Exa shipped agent_run into MCP in July 2026, clients picked it up without a redeploy. You still own key storage, per-tenant isolation, quota attribution, and revocation.

On the API path you own all of that plus request construction, retries, pagination for Websets, webhook signature verification for Monitors, and per-surface error handling. You gain explicit control over search type, content extraction limits, and freshness, which is what determines cost per call.

Schema stability cuts both ways

MCP tool schemas are unversioned and change when Exa updates the hosted server. Exa has already deprecated eight MCP tools, including get_code_context_exa, company_research_exa, and crawling_exa, keeping them available only for backwards compatibility.

The API surface moves too, and it moves in public. The Exa changelog records the /research endpoint retiring in April 2026 in favor of type: "deep-reasoning" on /search, the people category replacing linkedin, the publication category replacing research paper, and startCrawlDate and endCrawlDate becoming silent no-ops. Any prebuilt tool schema layer, Exa's or a third party's, lags these deprecations. Read the changelog before you trust a parameter.

Cost is a first-class design constraint

Exa bills per request plus per content item returned, which means the same search costs different amounts depending on what you ask back. As of Exa's March 2026 pricing update, search with contents was 7 dollars per 1,000 requests with the first 10 results included, summaries 1 dollar per 1,000, and Deep Reasoning 15 dollars per 1,000. Agent runs price separately in compute units.

The design implication is direct: include_text with a high max_characters on 20 results is not a neutral choice. Cap extraction, prefer highlights over full text, and set num_results deliberately. The API exposes every one of these knobs. The default MCP tools expose two. This cost-control discipline matters even more when you consider that MCP can be significantly more expensive than CLI-based approaches at scale.

When to use MCP, when to use the API

Use Exa MCP when:

  • You are building an interactive research assistant or coding agent in Claude Code, Cursor, or a similar client where search and page reads are the whole job
  • You want Exa's LLM-tuned tool descriptions rather than writing your own schemas over /search
  • You are prototyping and the free unauthenticated tier at 3 QPS is enough to prove the concept
  • Your agent orchestrates several MCP servers and you want one consistent tool discovery path across all of them

Use the Exa API directly when:

  • Your agent needs to react to the web over time, which means Monitors with webhook delivery
  • You are building lists at scale with Websets, or enriching entities across thousands of URLs
  • You need explicit control over search type, outputSchema, streaming, or content extraction limits because cost per call is a budget line
  • You need to create, poll, cancel, and replay Agent runs rather than fire one blocking call
  • You are issuing and rotating Exa keys programmatically per tenant via the team-management API

The credential problem that exists on both paths

Both paths hand your agent an Exa API key. Neither hands you a vault, a rotation path, or a revocation flow. That gap is where production agents break, and it is identical on either side of the MCP decision.

N users means N Exa keys

In a multi-tenant B2B agent, every customer brings their own Exa account and their own key. Fifty customers is fifty keys to store encrypted, isolate per tenant, and invalidate when a customer churns. The MCP path gives you a key in a header. The API path gives you a key in a header. Neither gives you the layer underneath. For a deeper look at this problem, see who holds the token: credential ownership across agent tool-calling patterns.

A shared key collapses attribution and quota

A single service key looks correct in a demo. In production, every search burns quota against one account, per-tenant rate limits become one shared ceiling, and the audit trail shows a service account instead of the person whose agent ran the query. When finance asks which customer generated 40 percent of last month's Exa spend, there is no answer in the logs.

Where Scalekit fits

Scalekit's Exa connectors resolve the correct per-user key server-side on every tool call, so search runs under the identity that authorized it and the credential never enters agent runtime or LLM context. The same connected-account model works whether you build against MCP or the REST API, which means the path decision does not change your auth infrastructure.

Building Exa agents with Scalekit

Scalekit ships Exa as two separate connectors, matching the two access layers. Both authenticate with an API key, so neither requires an OAuth redirect or an authorization link.

Two connectors, one auth model

Dimension
Scalekit exa connector
Scalekit examcp connector
Backs onto
Exa REST API
Exa hosted MCP server
Tool surface
Search, crawl, answer, find-similar, research, Websets management
examcp_web_search_exa, examcp_web_fetch_exa
Auth type
API key, vaulted per user
API key, vaulted per user
Arbitrary endpoint access
Yes, via actions.request()
No

Pick exa when the agent needs the full platform. Pick examcp when the agent only searches and reads, and you want the tightest possible tool surface in context. The connector overview page for both sits at scalekit.com/connectors/exa.

Set up the connection and register a user's key

Create the connection once per environment in AgentKit > Connections, then note the connection name. That string must match the connection_name you pass in code exactly; a mismatch here is the single most common integration error.

pip install scalekit langchain-openai
# .env SCALEKIT_ENV_URL=<your-environment-url> SCALEKIT_CLIENT_ID=<your-client-id> SCALEKIT_CLIENT_SECRET=<your-client-secret>

Register a connected account when your user pastes their Exa key into your settings page. Because Exa is an API-key connector, there is no redirect and the account is usable immediately.

import os from scalekit.client import ScalekitClient from dotenv import load_dotenv load_dotenv() scalekit_client = ScalekitClient( env_url=os.getenv("SCALEKIT_ENV_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions # connection_name must match the connection configured in the Scalekit dashboard actions.upsert_connected_account( connection_name="exa", identifier="user_123", credentials={"api_key": "user-supplied-exa-api-key"}, )

Retrieve the authorized tool surface before you call anything

The agent should not load a connector catalog. It should load the tools this user's connected account is authorized to call, which is a scoped and deterministic surface, not an exploration. Scalekit returns those tools already shaped as LangChain StructuredTool objects, so no schema reshaping is needed.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tools = actions.langchain.get_tools( identifier="user_123", connection_names=["exa"], page_size=100, # Exa exposes ~10 tools; page_size=100 avoids truncation ) tool_map = {t.name: t for t in tools}

Run the LangChain research agent

With the surface scoped, the loop is ordinary LangChain. The user's Exa key is resolved server-side on each execute_tool call and never appears in the model context. For more on how LangChain tool calling fits into a production agent architecture, see LangChain tool calling: how it works, where it stops, and how Scalekit completes it.

llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [HumanMessage( "Find the three most cited papers on retrieval-augmented generation " "published since January 2026, then summarize each in two sentences." )] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Tool names come from the connector's tool list, and exa_search alone carries 19 parameters covering category, domain filters, freshness, and extraction caps. If you are unsure which name to use, list the tools for the current user first rather than guessing.

TypeScript: a Claude SDK agent on the MCP connector

When the agent only needs search and fetch, the examcp connector gives you a two-tool surface. That is the smallest useful Exa context footprint you can hand a model, and it is the right default for an assistant that reads the web and nothing else.

npm install @scalekit-sdk/node @anthropic-ai/sdk
import { ScalekitClient } from '@scalekit-sdk/node'; import Anthropic from '@anthropic-ai/sdk'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const anthropic = new Anthropic(); // connectionName must match the connection configured in the Scalekit dashboard await scalekit.actions.upsertConnectedAccount({ connectionName: 'examcp', identifier: 'user_123', credentials: { api_key: 'user-supplied-exa-api-key' }, });

Retrieve the authorized surface, convert it to Anthropic's tool format, and run the loop in full.

const { tools } = await scalekit.tools.listScopedTools('user_123', { filter: { connectionNames: ['examcp'] }, pageSize: 100, }); const llmTools = tools.map(t => ({ name: t.tool.definition.name, description: t.tool.definition.description, input_schema: t.tool.definition.input_schema, })); const messages: Anthropic.MessageParam[] = [ { role: 'user', content: 'What changed in the MCP spec this quarter? Cite sources.' }, ]; while (true) { const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, tools: llmTools, messages, }); if (response.stop_reason === 'end_turn') { const text = response.content.find(b => b.type === 'text'); if (text?.type === 'text') console.log(text.text); break; } const toolResults: Anthropic.ToolResultBlockParam[] = []; for (const block of response.content) { if (block.type === 'tool_use') { const result = await scalekit.actions.executeTool({ toolName: block.name, identifier: 'user_123', toolInput: block.input as Record<string, unknown>, }); toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result.data), }); } } messages.push({ role: 'assistant', content: response.content }); messages.push({ role: 'user', content: toolResults }); }

Reaching endpoints that have no prebuilt tool

Monitors, Websets sub-resources, and the Agent run lifecycle have no MCP tool and, in some cases, no prebuilt Scalekit tool either. The proxy call closes that gap: you get raw endpoint access while the user's key stays vaulted and the call stays attributed.

# Create a scheduled monitor on behalf of a specific user's Exa account result = actions.request( connection_name="exa", identifier="user_123", path="/monitors", method="POST", json={ "name": "Competitor launch tracker", "search": {"query": "Acme Corp product launches and partnerships", "numResults": 10}, "trigger": {"type": "interval", "period": "1d"}, "webhook": {"url": "https://your-app.com/hooks/exa"}, "metadata": {"tenant_id": "acme", "user_id": "user_123"}, }, ) print(result.data)

This is the practical answer to the capability gap. You do not have to choose between vaulted per-user credentials and the full Exa platform.

Scoping and observability for multi-tool agents

Two problems appear once your Exa agent stops being a single-connector demo: the tool surface grows, and nobody can reconstruct what the agent did. Both have infrastructure answers rather than prompting answers.

Virtual MCP servers keep the tool surface honest

A standard MCP server exposes everything it has. Give a summarizer agent the full exa connector and it can also create Websets, which is a spend risk it never needed. Virtual MCP servers enforce least privilege at the tool level: you declare which connections and which tools the endpoint exposes, and the agent sees nothing else.

The token math matters too. A server with 40 tools at roughly 200 tokens each burns about 8,000 tokens before the agent does any work; scoping to 5 or 10 tools cuts that overhead by around 80 percent. The fix is not better prompting. It is surface reduction. This is also why understanding what an MCP gateway does matters before you ship a production agent.

Define the server once per agent role

You create the server once, not once per user. The response returns a static mcp_server_url you reuse for every session.

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = scalekit_client.actions.mcp.create_config( name="market-research-agent", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="exa", tools=["exa_search", "exa_crawl", "exa_answer"], # no Websets, no deletes ), McpConfigConnectionToolMapping( connection_name="slack", tools=["slack_post_message"], ), ], ) config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url

Mint a per-user session token before each run

The endpoint is static; the identity is not. Before every agent run, confirm the user's connections are active, then mint a short-lived token bound to that user. Never reuse a token across sessions.

from datetime import timedelta accounts = scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier="user_123", include_auth_link=True, ) for account in accounts.connected_accounts: if account.connected_account_status != "ACTIVE": print(f"{account.connection_name} needs auth: {account.authentication_link}") token_response = scalekit_client.actions.mcp.create_session_token( mcp_config_id=config_id, identifier="user_123", expiry=timedelta(minutes=30), ) mcp_server = { "url": mcp_server_url, "headers": {"Authorization": f"Bearer {token_response.token}"}, }

One server definition serves every user. No credential sharing between tenants, and no per-user server configuration to maintain.

Downstream tool-call observability

Exa's own logs know one thing: a key made a request. They do not know which of your customers triggered it or which agent run it belonged to. Scalekit logs every tool call with the identity that authorized it, retained for 90 days and exportable to your SIEM. That is what turns "Exa spend doubled this month" into a query you can answer per tenant, and what lets you show an auditor which user's credential backed a given search. It is also how you catch a revoked key: the connection fails closed on the next call with a clear error, and the event is logged, instead of the agent quietly returning empty results. For an in-depth look at this problem, see agent tool observability: your agent is running, is it actually working?

Which one to build against

If your Exa agent is interactive and its job is searching and reading, build on the MCP path. The tool surface is small, Exa maintains the schemas, and the API-key header means it still works headlessly when you need it to.

If your agent schedules recurring searches, builds lists at scale, controls Agent run lifecycles, or needs cost control over search type and extraction, build against the REST API. Those surfaces do not exist in the MCP server and are not going to.

Most production research agents end up using both. The credential layer underneath is identical either way, and that is the part that needs production-grade infrastructure rather than a key in an environment variable.

Get help building your Exa agent

Working through per-user key isolation, Websets cost control, or Virtual MCP scoping for a multi-tenant research agent? Two ways to reach us.

Join the Scalekit Slack community to compare notes with other agent builders, or talk to an engineer if you want architecture help on a specific deployment.

Browse the Scalekit Exa connector: scalekit.com/connectors/exa

Read the connector docs: Exa and Exa MCP

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
zero upfront cost.

Every feature unlocked. No hidden fees.