Backend for saving news URLs and querying them with AI-powered search.
I posted about this project on LinkedIn:
https://www.linkedin.com/posts/md-sohail-230141205_github-sohail3080linkmind-activity-7428166732424527872-Cib0?utm_source=share&utm_medium=member_desktop&rcm=ACoAADQq6AQBraEbiT14ztiatVqIq2dy3pG4Gus
You can test all endpoints using the Postman collection below:
Postman Collection:
https://www.postman.com/myselfmdsohail-1533277/linkmind
Start the FastAPI server using Uvicorn:
uvicorn main:app --reload| Route | Method | Use |
|---|---|---|
/v1/api/save-url |
POST | Ingest news URLs; content is scraped, chunked, embedded, and stored in Qdrant. |
/v1/api/query |
POST | Search stored news and (with backend: "custom") get an AI answer from your LLM. |
{
"urls": [
"https://example.com/article1",
"https://example.com/article2"
]
}Headers
api_key: YOUR_API_KEY
Body
{
"query": "Your question",
"backend": "custom",
"model": "your-model-id",
"custom_url": "https://your-llm-api/completions"
}Currently, the backend supports only a custom LLM endpoint.
Your custom LLM server must accept the following request structure:
{
"model": "your-model-id",
"messages": [
{
"role": "system",
"content": "System instructions with retrieved context"
},
{
"role": "user",
"content": "User query"
}
],
"max_tokens": 300
}Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonThe backend does not enforce a strict response schema for the custom LLM.
Whatever valid JSON response is returned by the external LLM endpoint will be forwarded directly to the client without modification.
This means:
- The backend does not validate or transform the response structure.
- Any valid JSON format returned by your LLM server will be passed through as-is.
Deployment to Vercel was attempted but failed.
Vercel uses a serverless architecture with execution time limits.
The /v1/api/save-url route performs:
- URL scraping
- Content parsing
- Chunking
- Embedding generation
- Vector storage in Qdrant
These operations are long-running and CPU/network intensive, which exceed Vercel’s serverless timeout limits.
As a result:
- Requests timeout
- Functions terminate early
- Embeddings are not fully stored
This architecture is not ideal for heavy ingestion pipelines.
The backend is successfully deployed on Render:
However:
- The free tier has limited CPU resources.
- Long-running ingestion (
/v1/api/save-url) may still timeout. - Scaling is not available on the free tier.
Because of architectural limitations (small chunk size, embedding generation, multiple vector writes), ingestion may fail under constrained resources.
To fully support ingestion reliably, the project would require:
- Higher compute plan
- Increased timeout limits
Currently:
- Only
backend: "custom"is supported. - OpenAI integration is not enabled because no API key was available during development/testing.
- Claude (Anthropic) integration is also not implemented for the same reason.
This means:
- You must provide your own compatible LLM endpoint.
- The backend assumes an OpenAI-style
/chat/completionsinterface.
Future versions may include:
- Native OpenAI integration
- Native Claude integration
- OpenRouter support
Since this project relies on a user-provided LLM endpoint:
- You are responsible for hosting and maintaining the LLM server.
- You must ensure it follows the required request/response format.
- Authentication must be handled via the
api_keyheader.
If you don’t have your own hosted LLM server, you can use OpenRouter to test this project for free.
-
Create a free account at: https://openrouter.ai
-
Go to the API Keys section and generate a new API key.
-
Browse available models and search for:
- Free models
- Models labeled as
:free
-
Use the OpenRouter Chat Completions endpoint:
https://openrouter.ai/api/v1/chat/completions
- When calling
/v1/api/query, set:
{
"query": "Your question",
"backend": "custom",
"model": "openrouter-model-id",
"custom_url": "https://openrouter.ai/api/v1/chat/completions"
}- Add your OpenRouter API key in the request header:
api_key: YOUR_OPENROUTER_API_KEY
Note:
- Make sure the selected model supports chat completions.
- Free models may have rate limits.
- Model availability may change over time.
- Chunk size is currently small (200 characters).
- No chunk overlap is used.
- Retrieval limit is capped at 20 vectors.
- Context is truncated to 4000 characters before being sent to the LLM.
This may:
- Reduce answer completeness
- Affect long-article understanding
- Limit deep contextual reasoning
These values can be tuned for better performance.
- UUIDs are used for vector IDs.
- Repeated ingestion of the same URLs will create duplicate embeddings.
- There is currently no deduplication or cleanup mechanism.
For production use, you may want:
- URL hashing
- Duplicate detection
- Collection reset endpoint
- Metadata-based filtering
QdrantClientURL— Qdrant Cloud URLQdrantClientAPIKey— Qdrant API key
- YouTube
- Virtual environments | FastAPI
- FastAPI Async API Guide
- What is OpenRouter
- Deploy FastAPI on Vercel
- FastAPI Testing Events
- Qdrant Cloud Quickstart
- Related deployment issue I went through
- Deploy to Render for free
Architecture Notes
- RAG (Retrieval-Augmented Generation) architecture
- Vector-based semantic search with Qdrant
- Chunk → Embed → Store embedding pipeline
- Stateless FastAPI backend
- LLM integration with context augmentation
Additional Note
This is a beginner-level learning project built to understand how RAG (Retrieval-Augmented Generation), FastAPI, vector databases (Qdrant), and custom LLM integrations work together. It is intended for experimentation and getting started with AI-powered backend systems, not for production use.
Apology & feedback
Sorry for any mistakes, oversights, or rough edges in this project whether in the code, docs, or setup. If you spot a bug, have a suggestion, or want to report an issue, please feel free to reach out or open an issue; your feedback is welcome and appreciated.