See the news. Not the spin.
A self-hosted personal news reader that uses AI to rewrite sensationalised headlines, generate TLDR summaries, and analyse sentiment across US and UK news sources. You run it locally with your own API keys - no hosted service, no account, no tracking.
-
Sensationalised headlines are rewritten to be calm and factual
-
Every article gets a short TLDR summary
-
Sentiment analysis shows the tone of each story
-
Good News filter surfaces genuinely positive stories
-
Content guardrails hide war, death, and other distressing topics
-
Country and category filters let you focus on what matters
-
No ads, no tracking, no account required
You need Python 3.11+, the repo-pinned Node 22.17.0 runtime (see .nvmrc), and an OpenAI API key for the AI analysis. A free NewsAPI key is optional during setup and only needed when you want to fetch fresh headlines.
git clone https://github.com/ThomasJButler/NewsPerspective.git
cd NewsPerspective
python3 -m venv src/backend/.venv
source src/backend/.venv/bin/activate
pip install -r src/backend/requirements.txtCopy .env.template to .env at the repo root and add your OpenAI key:
OPENAI_API_KEY=your_openai_api_key
OPENAI_MODEL=gpt-4o-mini
DATABASE_URL=sqlite:///./newsperspective.dbYour NewsAPI key is never stored on the server. You enter it in the browser and it stays in your browser's local storage.
source src/backend/.venv/bin/activate
uvicorn src.backend.main:app --reload --port 8000In a second terminal:
cd src/frontend
npm install
npm run devOpen http://localhost:3000. Read-only browsing works without a saved NewsAPI key, but a fresh local database starts empty. Enter your NewsAPI key in the inline setup card or settings dialog and then hit refresh to fetch headlines, or seed cached demo data first with python -m src.backend.scripts.seed_manual_integration_data.
Browser (Next.js 16 + React 19 + ShadCN UI)
│
│ /api/* proxy
▼
Backend (FastAPI + SQLite)
│
├── NewsAPI /v2/top-headlines (US + UK, 7 categories)
│
└── OpenAI chat completions (one call per article)
→ sentiment, rewrite decision, TLDR, good-news flag
- You hit refresh. The frontend sends your NewsAPI key in the
X-News-Api-Keyheader. - The backend fetches headlines from NewsAPI across both US and UK sources in 7 categories (general, sports, technology, science, health, business, entertainment).
- Each new article gets a single OpenAI analysis call that decides whether the headline needs rewriting, generates a TLDR, scores sentiment, and flags good news.
- Processed articles are stored in SQLite and served to the frontend. Read-only browsing works without any API key.
The AI prompt that analyses each article lives in src/backend/services/ai_service.py. You can customise it to match your reading preferences.
Here is a Claude/ChatGPT prompt you can use to generate a personalised version:
I'm setting up NewsPerspective (https://github.com/ThomasJButler/NewsPerspective).
The AI system prompt is in src/backend/services/ai_service.py.
Help me customise the analysis prompt for my preferences:
- Topics I care about: [e.g. technology, climate, local UK news]
- Topics I want to avoid: [e.g. celebrity gossip, US politics]
- Tone I prefer: [e.g. formal and concise / casual and explanatory]
- Good news criteria: [e.g. only scientific breakthroughs / any positive community story]
- TLDR length: [e.g. 1 sentence / 2-3 sentences / detailed paragraph]
Generate a replacement system prompt that keeps the JSON response
format but adjusts the analysis rules and rewrite style to match.
The free NewsAPI plan gives you 100 requests per day with articles delayed by ~24 hours and restricted to localhost requests. NewsPerspective fetches 7 categories across 2 countries, so each refresh uses ~14 requests. That means roughly 7 refreshes per day on the free plan.
This works well for the app's purpose: it gives the AI time to analyse and contextualise stories rather than racing to publish raw headlines. The free tier is localhost-only, which is why NewsPerspective is designed as a self-hosted app rather than a live web service.
Note on UK headlines: NewsAPI recently restricted /v2/top-headlines?country= to us only. NewsPerspective works around this by fetching UK headlines via specific source IDs (BBC News, The Guardian, Independent, Financial Times, BBC Sport, Daily Mail, TalkSport, Business Insider UK) — all batched into a single request. UK coverage still works on the free Developer tier from localhost. To add or remove UK outlets, edit UK_SOURCE_CATEGORIES in src/backend/services/news_fetcher.py.
A Docker Compose workflow is included for quick local testing:
# Start the app stack (seeds demo data automatically)
docker compose -f src/frontend/compose.yaml up --build app
# Run Playwright e2e tests against the stack
docker compose -f src/frontend/compose.yaml run --rm playwright
# Stop
docker compose -f src/frontend/compose.yaml downBackend tests (116 tests across 6 modules):
source src/backend/.venv/bin/activate
python -m unittest src.backend.tests.test_api_smoke -v
python -m unittest src.backend.tests.test_refresh_processing -v
python -m unittest src.backend.tests.test_manual_integration_evidence -v
python -m unittest src.backend.tests.test_config -v
python -m unittest src.backend.tests.test_comparison -v
python -m unittest src.backend.tests.test_custom_guardrails -vFrontend checks:
cd src/frontend
npm run lint
npm run typecheck
npm run test:e2eIf ports 3000/8000 are already in use from a running local stack, use npm run test:e2e:reuse instead.
src/
├── backend/
│ ├── main.py # FastAPI app entrypoint
│ ├── models.py # SQLAlchemy article model
│ ├── routers/ # API route handlers
│ ├── services/ # AI, news fetching, article processing
│ ├── utils/ # Good news rules, content guardrails
│ └── tests/ # Backend test suite
└── frontend/
├── app/ # Next.js App Router pages
├── components/ # React components (ShadCN UI)
├── hooks/ # Custom React hooks
├── lib/ # API client, utilities
├── types/ # TypeScript type definitions
└── tests/e2e/ # Playwright browser tests
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, ShadCN UI, Tailwind CSS 4 |
| Backend | FastAPI, SQLAlchemy, OpenAI Python SDK |
| Database | SQLite |
| News source | NewsAPI (free tier compatible) |
| AI | OpenAI chat completions (default: gpt-4o-mini) |
| Testing | Python unittest, Playwright |
This is a personal project, but contributions are welcome. Please open an issue first to discuss what you would like to change.
AGPLv3 — free to use, modify, and distribute. If you run a modified version as a network service, you must share your changes under the same license.
Built by Tom Butler.

