Exa (tracing)
Exa provides search, contents, grounded-answer, tool, and Agent APIs. Respan’s native Exa instrumentations turn those SDK calls into connected OpenTelemetry spans with provider-neutral names and canonical Respan metadata.
Set up Respan
Create an account at platform.respan.ai and grab an API key .
Package source and example projects
The first release of the Python respan-instrumentation-exa package is pending and is not currently available from PyPI. The Python installation steps below use a Respan source checkout. The JavaScript @respan/instrumentation-exa package is available from npm.
This is an SDK tracing integration, not a Respan Gateway provider route. Respan Gateway does not currently route Exa search, contents, answer, Agent, or Research APIs. EXA_API_KEY is used directly by the official Exa SDK; RESPAN_BASE_URL configures trace export only.
The Python package requires Python >=3.11,<3.14, supports exa-py>=2.20.0,<3.0.0, and is tested with exa-py==2.20.0. The JavaScript package requires Node.js 18 or newer, supports exa-js>=2.19.0 <3.0.0, and is tested with the npm-stable exa-js@2.19.0.
Paste this into your coding agent:
Help me set up Respan tracing with the Exa integration.
Follow https://www.respan.ai/docs/integrations/exa.mdSetup
Install packages
pip install respan-ai respan-instrumentation-exa "exa-py>=2.20,<3"Set environment variables
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
export EXA_API_KEY="YOUR_EXA_API_KEY"
# Optional: override the Respan API base URL.
export RESPAN_BASE_URL="https://api.respan.ai/api"Initialize Respan and call Exa
Initialize the explicit instrumentor before making Exa SDK calls, and shut Respan down when the application exits so pending spans are flushed.
import os
from exa_py import Exa
from respan import Respan
from respan_instrumentation_exa import ExaInstrumentor
respan = Respan(
api_key=os.environ["RESPAN_API_KEY"],
base_url=os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api"),
app_name="exa-tracing",
instrumentations=[ExaInstrumentor()],
)
exa = Exa(api_key=os.environ["EXA_API_KEY"])
try:
result = exa.search(
"recent advances in retrieval",
type="auto",
num_results=3,
contents={"highlights": True},
)
print(result.results)
finally:
respan.shutdown()View your trace
Open the Traces page and inspect the tool.search span, its input and output, status, timing, and Exa metadata.
Captured SDK surfaces
The integration is explicit-only: pass ExaInstrumentor in instrumentations as shown above. Exa can execute OpenAI or Anthropic tool adapters internally, so it is not enabled as direct-LLM auto-instrumentation.
| Surface | Python | JavaScript | Respan shape |
|---|---|---|---|
| Search | search(), search_and_contents(), find_similar(), find_similar_and_contents() | search(), searchAndContents(), findSimilar(), findSimilarAndContents() | Provider-neutral tool spans such as tool.search and tool.find_similar |
| Contents | get_contents() | getContents() | tool.get_contents with URLs/options as input and retrieved content as output |
| Grounded answers | answer(), stream_answer() | answer(), streamAnswer() | Chat spans; the model is recorded only when the SDK request or response supplies one |
| Search streaming | stream_search() | streamSearch() | One span whose output aggregates the consumed chunks and citations |
| Agent runs | Sync and async agent.runs create, stream, get, list, cancel, stop, delete, poll, wait, and event-list operations | agent.runs and beta Agent run create, stream, get, list, cancel, stop, delete, poll, wait, and event-list operations | agent.run plus lifecycle task spans |
| Tool helpers | tools.web_search(), Python 2.20 tools.get_contents(), and the OpenAI Chat/Responses and Anthropic helper adapters | tools.webSearch() plus the OpenAI Chat/Responses and Anthropic helper adapters | The helper’s underlying core Exa call is traced once, without a duplicate wrapper span |
| Legacy Research | Sync and async research.create(), get(), list(), and poll_until_finished() | research.create(), get(), list(), and pollUntilFinished() | agent.research plus lifecycle task spans, marked as legacy metadata |
Native SDK operation names, language, stream state, result counts, request IDs, resolved search type, cost, citations, and legacy Research markers are stored in the canonical respan.metadata JSON attribute. Successful and failed calls also retain their real status and error information. API keys and authorization-like fields are always redacted.
Streaming lifecycle
Streaming spans end when the iterator is exhausted, iteration raises an error, or the iterator is explicitly closed. JavaScript early exit from a for await loop calls the wrapped iterator’s return() and closes the span. Python exposes the SDK’s close behavior through the wrapped iterator, including the synchronous close() method on exa-py 2.20 async stream response objects.
If an application abandons a stream without exhausting or closing it, the instrumentation cannot record an exact completion timestamp. Always exhaust the stream or close it explicitly.
Content and privacy controls
Request and response content is captured by default. Disable it per instrumentor:
ExaInstrumentor(capture_content=False)Or disable content capture for the process:
export TRACELOOP_TRACE_CONTENT=falseWith content capture disabled, query, result, prompt, completion, citation, and stream payloads are omitted. Operation, stream state, status, timing, and other non-content metadata remain available.
Limitations
- Websets, Search Monitor CRUD, and beta Agent Monitor operations are not patched; they are long-lived control-plane APIs rather than in-process AI operations.
exa-js@2.19.0supports coregetContents(), but does not ship atools.getContents()helper. The Python 2.20 SDK does shiptools.get_contents().- Legacy Research remains instrumented for compatibility, but Exa recommends deep search (
type="deep-reasoning") for new research flows. - Helper calls are deduplicated against the core SDK methods they invoke; activating overlapping provider instrumentations may still add their own lower-level model spans.