> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raysurfer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Get started with Raysurfer in under 5 minutes

<Note>
  Raysurfer is designed for agents that chain multiple tool calls together in production workflows. If your agent only makes 1-3 tool calls per run, check [Is Raysurfer Right for You?](/is-it-right-for-me) first.
</Note>

## Installation

<CodeGroup>
  ```bash Python theme={null}
  uv add raysurfer
  ```

  ```bash TypeScript theme={null}
  bun add raysurfer
  ```
</CodeGroup>

## Set Your API Key

Get your API key from the [dashboard](https://www.raysurfer.com/dashboard/api-keys) and set it as an environment variable:

```bash theme={null}
export RAYSURFER_API_KEY=your_api_key_here
```

## Drop-in Replacement

Raysurfer wraps the Claude Agent SDK. Swap your client and method names:

<CodeGroup>
  ```python Python theme={null}
  # Before
  from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions

  async with ClaudeSDKClient(options) as client:
      await client.query("Generate quarterly report")
      async for msg in client.receive_response():
          print(msg)

  # After
  from raysurfer import RaysurferClient
  from claude_agent_sdk import ClaudeAgentOptions  # Options come from Claude SDK

  async with RaysurferClient(options) as client:
      await client.query("Generate quarterly report")
      async for msg in client.response():
          print(msg)
  ```

  ```typescript TypeScript theme={null}
  // Before
  import { ClaudeSDKClient } from "@anthropic-ai/claude-agent-sdk";

  const client = new ClaudeSDKClient(options);
  for await (const msg of client.query("Generate quarterly report")) {
    console.log(msg);
  }

  // After
  import { RaysurferClient } from "raysurfer";

  const client = new RaysurferClient(options);
  for await (const msg of client.query("Generate quarterly report")) {
    console.log(msg);
  }
  ```
</CodeGroup>

## Key Differences

| Claude SDK                  | Raysurfer           |
| --------------------------- | ------------------- |
| `ClaudeSDKClient`           | `RaysurferClient`   |
| `client.query()`            | `client.query()`    |
| `client.receive_response()` | `client.response()` |

Options (`ClaudeAgentOptions`) come directly from `claude_agent_sdk` — no Raysurfer-specific options needed.

## Verification Checklist

Follow these three steps to confirm caching is working and understand the similarity threshold for your use case.

### Step 1: Confirm Code Generation on First Run

Run your agent with a unique query for the first time. This run generates code from scratch via the Claude Agent SDK, so it will take the full generation time.

```python theme={null}
await client.query("Generate a quarterly revenue report for Q3 2025")
```

Watch the output — you should see the agent generating code, executing tools, and producing results at normal speed. This first run populates the cache.

### Step 2: Confirm Cache Hit on Second Run

Run the **exact same query** again:

```python theme={null}
await client.query("Generate a quarterly revenue report for Q3 2025")
```

This time, Raysurfer pulls the cached code file instead of regenerating it. You should see the result return near-instantly because the only synchronous work is fetching the cached code — everything else runs asynchronously.

<Check>
  If the second run completes dramatically faster than the first, caching is working. You're in the clear.
</Check>

<Tip>
  Run the same query using `claude-agent-sdk` directly (without Raysurfer) to get a baseline time, then compare it against the cached Raysurfer run to measure your exact speedup.
</Tip>

### Step 3: Explore the Similarity Boundary

Now try **similar but not identical** queries to see how far you can drift before you stop getting cache hits. Start close and gradually move further away:

```python theme={null}
# Very similar — likely cache hit
await client.query("Generate a quarterly revenue report for Q4 2025")

# Moderately similar — may or may not hit cache
await client.query("Generate a monthly revenue summary for Q3 2025")

# Different intent — likely cache miss, generates fresh code
await client.query("Build a customer churn analysis dashboard")
```

For each query, note whether the response time is near-instant (cache hit) or takes the full generation time (cache miss). This tells you how semantically similar your queries need to be to reuse the same cached workflow code.

<Card title="Need help tuning for your use case?" icon="calendar" href="https://calendly.com/raymond-raysurfer/15min">
  Book a 15-minute call and we'll walk through your integration together.
</Card>

## What Gets Cached?

Raysurfer caches:

* Code outputs from agent tool calls
* Generated reports, templates, and documents
* API response patterns
* Any structured output your agent produces

<Note>
  Raysurfer automatically verifies and stores successful code snippets from your runs for future reuse.
</Note>

## Programmatic Tool Calling

Register tools, then either pass in code (primary mode) or optionally generate code in the sandbox with your own key + prompt:

<CodeGroup>
  ```python Python theme={null}
  from raysurfer import AsyncRaySurfer

  rs = AsyncRaySurfer(api_key="your_api_key")

  @rs.tool
  def add(a: int, b: int) -> int:
      """Add two numbers together."""
      return a + b

  user_code = "result = add(5, 3)\nprint(result)"
  result = await rs.execute(
      "Use add to compute 5 + 3, then print the result",
      user_code=user_code,
  )
  print(result.result)     # "8"
  print(result.cache_hit)  # False (reserved field)
  ```

  ```typescript TypeScript theme={null}
  import { RaySurfer } from "raysurfer";

  const rs = new RaySurfer({ apiKey: "your_api_key" });

  rs.tool("add", "Add two numbers", { a: "integer", b: "integer" },
    async (args) => String(Number(args.a) + Number(args.b))
  );

  const userCode = `
  result = add(5, 3)
  print(result)
  `;
  const result = await rs.execute("Use add to compute 5 + 3, then print the result", {
    userCode,
  });
  console.log(result.result);    // "8"
  console.log(result.cacheHit);  // false (reserved field)
  ```
</CodeGroup>

See [Python SDK — Programmatic Tool Calling](/sdk/python#programmatic-tool-calling) or [TypeScript SDK — Programmatic Tool Calling](/sdk/typescript#programmatic-tool-calling) for full details.

In Python, `@rs.tool` can wrap any function and uses its docstring as the tool description in the schema payload.
In TypeScript, `tool(name, description, parameters, callback)` wraps any callback and uses your `description` string the same way.
`execute()` supports two modes: `user_code`/`userCode` (primary) or optional sandbox codegen with a user-provided prompt and provider key.

If you already have an LLM tool loop and just want to wrap it with `.search()` + `.upload()`, use [Wrap Any LLM Tool Flow](/integrations/llm-tool-flows).

## Next Steps

<CardGroup cols={2}>
  <Card title="Python SDK" icon="python" href="/sdk/python">
    Full Python SDK reference and examples
  </Card>

  <Card title="TypeScript SDK" icon="js" href="/sdk/typescript">
    Full TypeScript SDK reference and examples
  </Card>

  <Card title="How It Works" icon="gear" href="/how-it-works">
    Learn about proven code retrieval and reputation scoring
  </Card>

  <Card title="Is It Right for Me?" icon="compass" href="/is-it-right-for-me">
    Check if your agent workflow is a good fit for Raysurfer
  </Card>
</CardGroup>
