---
name: vector-fastapi
description: "Provisions an Upstash Vector index for a FastAPI app with `npx extraorbital add vector` (no signup, no card) and wires it into FastAPI with `upstash-vector`. Use when a FastAPI project needs Vector index, embeddings, semantic search, RAG, a vector database or UPSTASH_VECTOR_REST_URL, or when UPSTASH_VECTOR_REST_URL is missing or unset in .env."
---

# Vector index in FastAPI

<!-- Generated from ExtraOrbital's service registry by `pnpm skills:build` in platform/app. Edit the generator, not this file. -->

ExtraOrbital provisions an Upstash Vector index in one command and writes the credentials into the project's env file. For anything this recipe does not cover, use the `extraorbital` skill or https://extraorbital.dev/llms.txt.

## 1. Provision

```bash
npx extraorbital add vector
```

Run it from the project root. It is idempotent (a second run returns the same resource) and writes to `.env.local` if the project has one, otherwise `.env`, keeping that file out of git. Add `--json` to parse the result; credential values are redacted there, and you should never print them either.

| Variable | |
| --- | --- |
| `UPSTASH_VECTOR_REST_URL` |  |
| `UPSTASH_VECTOR_REST_TOKEN` |  |

Free on the Prototype plan: 10K queries + 10K updates/day.

## 2. Install the client

```bash
pip install pydantic-settings upstash-vector
```

## 3. Connect

`pydantic-settings` reads `.env` and `.env.local` itself; if `app/config.py` already exists, add the fields to its `Settings`.

**`app/config.py`**

```python
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    # ExtraOrbital writes .env.local when the project has one, else .env; the later file wins.
    model_config = SettingsConfigDict(env_file=(".env", ".env.local"), extra="ignore")

    UPSTASH_VECTOR_REST_URL: str
    UPSTASH_VECTOR_REST_TOKEN: str


settings = Settings()
```

**`app/vector.py`**

```python
from upstash_vector import AsyncIndex

from app.config import settings

vector_index = AsyncIndex(
    url=settings.UPSTASH_VECTOR_REST_URL, token=settings.UPSTASH_VECTOR_REST_TOKEN
)
```

## 4. Use it

**`app/api/vector.py`**

```python
from fastapi import APIRouter

from app.vector import vector_index

router = APIRouter()
# In app/main.py: from app.api import vector; app.include_router(vector.router)


@router.get("/api/vector")
async def check_vector():
    info = await vector_index.info()
    return {"vectors": info.vector_count, "dimension": info.dimension}
```

Store and search:

```python
# Each vector must have the index's dimension (1536 unless provisioned otherwise).
await vector_index.upsert(vectors=[("doc-42", embedding, {"title": "Q3 report"})])
hits = await vector_index.query(vector=query_embedding, top_k=5, include_metadata=True)
```

## 5. Verify

```bash
npx extraorbital list        # the resource is listed as active
fastapi dev app/main.py
curl http://localhost:8000/api/vector
```

A JSON answer means the credentials, the client and the route all work. Delete the route afterwards if the app does not need it.

## Pitfalls

- Dimensions and the similarity function are fixed at creation. Match the embedding model: `npx extraorbital add vector --dimensions 3072` (default 1536, `COSINE`); to change them, provision a new `--slug` and re-index.
- `provision .` creates the index at 1536 dimensions when it detects the variables; run `add vector --dimensions …` first if your model differs.
- Embeddings come from your model; for an API key to call one, see the `ai-gateway-*` skills.
- A blocking client (`boto3`, `stripe`) belongs in a plain `def` route, which FastAPI runs in a threadpool; in `async def` it stalls every other request.
- With `uv`, `uv add` the same packages instead of `pip install`.
- Exit code 3 means the account is past its free allowance: show the human the link the command printed and wait. Do not retry or work around it.

Docs: https://extraorbital.dev/docs/resources/vector.md
