Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ api.available_category()
## Examples

- [Generate a source-linked news briefing](examples/source_linked_briefing/README.md) from a live Search API response or a deterministic offline fixture.
- [Build a company news monitor](examples/company_news_monitor/README.md) with
a JSON watchlist, bounded search window, local state, and deterministic fixture.

## Authentication

Expand Down
115 changes: 115 additions & 0 deletions examples/company_news_monitor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Company news monitor

This example runs a small company, competitor, or industry watchlist against a
bounded Currents Search API window. It writes:

- `report.md`, a source-linked change report for review;
- `report.json`, the same new articles as structured data;
- a local state file containing article keys seen across runs.

Currents provides search results and article metadata. This script owns the
watchlist, date window, local state, deduplication, and report formatting. It is
not a managed monitor or alert service.

## Run the checked-in fixture

Install this repository's dependencies, then run:

```bash
python examples/company_news_monitor/monitor.py \
--fixture examples/company_news_monitor/fixtures/search_responses.json \
--state-file company-monitor-state.json \
--output-dir company-monitor-output
```

The fixture uses fictional companies and `example.com` URLs. It needs no network
request, credentials, or customer data. Its fixed timestamps make the first run
deterministic. Delete the generated state file to reproduce that first run.

Run the same command again with the same state file. The second report contains
no new articles.

## Configure a watchlist

Each watch requires a unique `name` and exactly one search field. Use `keywords`
for standard search or `query` for Boolean syntax. `language` defaults to `en`.
The optional `domain` narrows that watch to one publisher domain.

```json
{
"watches": [
{
"name": "Competitor names",
"query": "\"Northstar Battery\" OR \"Atlas Storage\"",
"language": "en"
},
{
"name": "Industry policy",
"keywords": "\"grid storage\" regulation",
"language": "en",
"domain": "example.com"
}
]
}
```

Use the smallest watchlist that represents a real decision. Search does not
perform entity resolution, so ambiguous company names need additional terms.

## Run a live window

Create a [Currents API key](https://currentsapi.services/en/register?utm_source=github&utm_medium=referral&utm_campaign=content-build-company-news-monitor-currents-search-api),
then set it in your environment:

```bash
export CURRENTS_API_KEY="your-api-key"
```

Run an explicit UTC window:

```bash
python examples/company_news_monitor/monitor.py \
--watchlist examples/company_news_monitor/watchlist.json \
--start-date 2026-08-04T09:00:00Z \
--end-date 2026-08-05T09:00:00Z \
--page-size 20 \
--state-file company-monitor-state.json \
--output-dir company-monitor-output
```

Live mode sends the watch's `keywords` or `query`, plus `language`, `start_date`,
`end_date`, `page_number`, and `page_size`. It also sends `domain` when the
watch defines one.

Use a window and page size allowed by your Currents plan. Search windows,
lookback, page sizes, and retrievable result counts can vary by plan.

## Deduplication and state

The report treats the publisher URL and article ID as aliases. A match on either
alias merges the article and its watch names. The state file stores both aliases
when both are present.

The state file prevents the same article from appearing as new on later runs.
Keep a separate state file for each monitor. Back it up if missing alerts would
matter to your application.

## Limitations

- The script runs once. Your application must schedule it.
- The first run treats every in-window result as new.
- One page is requested per watch. Increase coverage only within plan limits.
- Names and Boolean terms can miss aliases or match unrelated entities.
- Results reflect the Currents index and are not exhaustive.
- Currents does not verify article claims or score their truth.
- API access does not grant publisher-content republication rights.
- Add retries, locking, state recovery, observability, and human review before
using this pattern in a production workflow.

## Test

Run the focused offline tests:

```bash
python -m pytest tests/test_company_news_monitor_example.py
```
45 changes: 45 additions & 0 deletions examples/company_news_monitor/fixtures/search_responses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"_fixture_generated_at": "2026-08-05T09:00:00Z",
"_fixture_start_date": "2026-08-04T09:00:00Z",
"_fixture_end_date": "2026-08-05T09:00:00Z",
"responses": {
"Battery competitors": {
"status": "ok",
"news": [
{
"id": "fictional-factory-1",
"title": "Northstar Battery opens a pilot recycling line",
"description": "A fictional company begins pilot operations.",
"url": "https://example.com/business/northstar-pilot-line",
"published": "2026-08-05T07:30:00Z"
},
{
"id": "fictional-joint-project-1",
"title": "Atlas Storage joins a grid demonstration",
"description": "A fictional storage supplier joins a demonstration.",
"url": "https://example.com/energy/atlas-grid-project",
"published": "2026-08-04T14:00:00Z"
}
]
},
"Industry policy": {
"status": "ok",
"news": [
{
"id": "fictional-policy-1",
"title": "Regulator publishes a grid storage consultation",
"description": "A fictional regulator opens a consultation period.",
"url": "https://example.com/policy/grid-storage-consultation",
"published": "2026-08-05T08:00:00Z"
},
{
"id": "fictional-joint-project-1",
"title": "Atlas Storage joins a grid demonstration",
"description": "A fictional storage supplier joins a demonstration.",
"url": "https://example.com/energy/atlas-grid-project",
"published": "2026-08-04T14:00:00Z"
}
]
}
}
}
Loading
Loading