# Use Xantly with Raycast (AI extensions)

[Raycast](https://raycast.com) is a macOS-native launcher with a rich AI extension ecosystem. Raycast AI itself (the built-in chat) is hosted on Raycast's infra and doesn't currently support custom base URLs. But Raycast **extensions** that call LLMs via the Node SDK can route through Xantly, which unlocks any open-source extension that uses `openai`, `@anthropic-ai/sdk`, or the `ai` SDK.

## Two audiences

1. **You use Raycast extensions that hit OpenAI/Anthropic APIs**: set the env vars below and extensions will pick up Xantly automatically.
2. **You build Raycast extensions**: use the OpenAI SDK with Xantly's base URL directly.

## Prerequisites

- Raycast installed on macOS
- A Xantly API key, [create one](/dashboard/api-keys)
- (Extension users) An extension that uses a standard LLM SDK and reads env vars

## Setup, as a Raycast extension user

Most Raycast extensions read `OPENAI_API_KEY` (sometimes `OPENAI_API_BASE`) from the environment. Set them in your shell:

```bash
export OPENAI_API_BASE=https://api.xantly.com/v1
export OPENAI_API_KEY=xantly_sk_...
```

Then restart Raycast (or reload the extension). The extension's OpenAI calls will now route through Xantly.

**macOS launchers don't inherit shell env vars by default.** Use `launchctl setenv` or create `~/Library/LaunchAgents/xantly.plist` to export them globally:

```bash
launchctl setenv OPENAI_API_BASE https://api.xantly.com/v1
launchctl setenv OPENAI_API_KEY xantly_sk_...
```

(This persists across reboots if you add it to a LaunchAgent plist.)

## Setup, as an extension author

In your `package.json`:

```json
{
  "dependencies": {
    "@raycast/api": "^1.70.0",
    "openai": "^4.0.0"
  }
}
```

In your extension source:

```ts
import { getPreferenceValues } from '@raycast/api'
import OpenAI from 'openai'

interface Preferences {
  apiKey: string
  baseURL: string
  model: string
}

const prefs = getPreferenceValues<Preferences>()

const client = new OpenAI({
  baseURL: prefs.baseURL || 'https://api.xantly.com/v1',
  apiKey: prefs.apiKey,
})

const resp = await client.chat.completions.create({
  model: prefs.model || 'xantly/auto-value',
  messages: [{ role: 'user', content: 'Hello from Raycast' }],
})
```

And in your `package.json` preferences schema:

```json
"preferences": [
  {
    "name": "baseURL",
    "type": "textfield",
    "required": false,
    "title": "Base URL",
    "description": "LLM API base URL (Xantly default)",
    "default": "https://api.xantly.com/v1"
  },
  {
    "name": "apiKey",
    "type": "password",
    "required": true,
    "title": "API Key",
    "description": "Xantly or OpenAI key"
  },
  {
    "name": "model",
    "type": "dropdown",
    "required": true,
    "title": "Model",
    "default": "xantly/auto-value",
    "data": [
      { "title": "Xantly Auto Quality", "value": "xantly/auto-quality" },
      { "title": "Xantly Auto Value", "value": "xantly/auto-value" },
      { "title": "Xantly Auto Speed", "value": "xantly/auto-speed" },
      { "title": "Claude Sonnet 4.6", "value": "anthropic/claude-sonnet-4.6" },
      { "title": "GPT-5.4", "value": "openai/gpt-5.4" }
    ]
  }
]
```

## Popular Raycast extensions that work with Xantly

- **Ray AI** community forks, open-source, reads env vars.
- **AI Assistant** extensions, any that bundle the `openai` SDK.
- **Prompt library extensions**: often read `OPENAI_API_KEY` directly.
- **Custom snippets with AI rewriting**: Xantly works here too.

The pattern: *if the extension lets you paste an OpenAI API key and optionally a base URL, Xantly works.*

## Model choice

| Model ID | When |
|---|---|
| `xantly/auto-speed` | Raycast extensions, you want sub-second responses for launcher UX. |
| `xantly/auto-value` | Balanced quality/speed. |
| `xantly/auto-quality` | For heavy-weight extensions (summarization, writing help). |
| `groq/llama-3.3-70b` | Fastest inference for short completions. |

Default to speed tier, Raycast is a launcher; users want snap-response.

## Verify

Run any Raycast AI extension (summarize clipboard, chat assistant, etc.) and check your Xantly dashboard, the call should log with cost + model + cache status.

## What you get

- **Every Raycast extension that uses OpenAI.** Works transparently once env vars are set.
- **Cache hits on repeat prompts.** Launcher workflows (same "summarize X" prompt all day) hit cache instantly.
- **Waterfall fallback.** Extension doesn't crash on provider outages.
- **Cost per Raycast command.** Tag API keys per-extension (create a Xantly key named `raycast-quicknote`, etc.) for fine-grained cost tracking.

## Gotchas

**Raycast AI built-in is not Xantly-compatible.** The chat panel in Raycast Pro uses Raycast's hosted AI. Custom base URL isn't supported for the built-in, only for extensions.

**Extension env var discovery.** Some extensions use env vars, some require preferences, some hardcode. Check the extension's README.

**Extension authors: respect user's own key.** If the user already has `OPENAI_API_KEY` set for their direct OpenAI usage, don't clobber it. Read your own `XANTLY_API_KEY` pref or fall back to env.

**`launchctl setenv` scope.** Applies to future apps launched. Restart Raycast after setting.

## Next steps

- [OpenAI SDK (TypeScript)](/docs/use-with-openai-sdk-typescript), the SDK Raycast extensions typically use.
- [Cost-Optimized Routing](/docs/cost-optimized-routing), keep launcher-cadence usage cheap.
- [Bring Your Own Key](/docs/bring-your-own-key), use your own OpenAI credits through Xantly.
- [Vercel AI SDK](/docs/use-with-vercel-ai-sdk), alternative TS SDK popular for extensions.
