FastAPI service for semantic document search using OpenAI embeddings and pgvector cosine similarity.
- FastAPI — async REST API
- PostgreSQL + pgvector — vector storage and cosine similarity search
- SQLAlchemy 2.0 — async ORM
- OpenAI
text-embedding-3-small— 1536-dim embeddings - Alembic — async database migrations
semantic_search_api/
├── app/
│ ├── main.py # FastAPI app setup
│ ├── schemas.py # Pydantic request/response models
│ └── routers/
│ └── documents.py # Document and search endpoints
├── db/
│ ├── base.py # SQLAlchemy declarative base
│ ├── models.py # Document and DocumentChunk models
│ ├── session.py # Async engine and session factory
│ └── repositories/
│ └── document_repository.py # DB operations
├── services/
│ └── embedding.py # OpenAI embedding client + text chunker
├── alembic/ # Migration scripts
├── config.py # Pydantic settings (env vars)
└── requirements.txt
- Python 3.13+
- PostgreSQL with pgvector extension enabled
- OpenAI API key
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate a .env file:
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/dbname
OPENAI_API_KEY=sk-...Enable pgvector in PostgreSQL:
CREATE EXTENSION IF NOT EXISTS vector;Run migrations:
alembic upgrade headuvicorn app.main:app --reloadAPI available at http://localhost:8000. Docs at http://localhost:8000/docs.
POST /documents/
Stores a document, chunks it (500 chars, 50-char overlap), and generates embeddings for each chunk.
Request:
{
"name": "my-doc",
"content": "Full document text..."
}Response:
{
"id": "uuid",
"name": "my-doc",
"status": "pending",
"created_at": "2026-06-03T00:00:00"
}GET /documents/
Returns all uploaded documents.
POST /documents/search
Embeds the query and returns the top 5 most similar chunks via cosine distance.
Request:
{
"query": "your search query"
}Response:
[
{
"id": "uuid",
"chunk": "matching text chunk...",
"chunk_index": 2,
"document_id": "uuid",
"created_at": "2026-06-03T00:00:00"
}
]| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | string | Document name |
| status | enum | pending, processing, completed, failed |
| created_at | datetime | Auto-set on creation |
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| document_id | UUID (FK) | Parent document |
| chunk | text | Text chunk content |
| chunk_index | int | Position within document |
| embedding | vector(1536) | OpenAI embedding |
| created_at | datetime | Auto-set on creation |