Skip to content

Commit 901d1e6

Browse files
committed
Add company news monitor example
1 parent b31b09d commit 901d1e6

6 files changed

Lines changed: 1007 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ api.available_category()
6060
## Examples
6161

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

6466
## Authentication
6567

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Company news monitor
2+
3+
This example runs a small company, competitor, or industry watchlist against a
4+
bounded Currents Search API window. It writes:
5+
6+
- `report.md`, a source-linked change report for review;
7+
- `report.json`, the same new articles as structured data;
8+
- a local state file containing article keys seen across runs.
9+
10+
Currents provides search results and article metadata. This script owns the
11+
watchlist, date window, local state, deduplication, and report formatting. It is
12+
not a managed monitor or alert service.
13+
14+
## Run the checked-in fixture
15+
16+
Install this repository's dependencies, then run:
17+
18+
```bash
19+
python examples/company_news_monitor/monitor.py \
20+
--fixture examples/company_news_monitor/fixtures/search_responses.json \
21+
--state-file company-monitor-state.json \
22+
--output-dir company-monitor-output
23+
```
24+
25+
The fixture uses fictional companies and `example.com` URLs. It needs no network
26+
request, credentials, or customer data. Its fixed timestamps make the first run
27+
deterministic. Delete the generated state file to reproduce that first run.
28+
29+
Run the same command again with the same state file. The second report contains
30+
no new articles.
31+
32+
## Configure a watchlist
33+
34+
Each watch requires a unique `name` and `keywords`. `language` defaults to `en`.
35+
The optional `domain` narrows that watch to one publisher domain.
36+
37+
```json
38+
{
39+
"watches": [
40+
{
41+
"name": "Competitor names",
42+
"keywords": "\"Northstar Battery\" OR \"Atlas Storage\"",
43+
"language": "en"
44+
},
45+
{
46+
"name": "Industry policy",
47+
"keywords": "\"grid storage\" regulation",
48+
"language": "en",
49+
"domain": "example.com"
50+
}
51+
]
52+
}
53+
```
54+
55+
Use the smallest watchlist that represents a real decision. Search does not
56+
perform entity resolution, so ambiguous company names need additional terms.
57+
58+
## Run a live window
59+
60+
Create a [Currents API key](https://currentsapi.services/en/register), then set
61+
it in your environment:
62+
63+
```bash
64+
export CURRENTS_API_KEY="your-api-key"
65+
```
66+
67+
Run an explicit UTC window:
68+
69+
```bash
70+
python examples/company_news_monitor/monitor.py \
71+
--watchlist examples/company_news_monitor/watchlist.json \
72+
--start-date 2026-08-04T09:00:00Z \
73+
--end-date 2026-08-05T09:00:00Z \
74+
--page-size 20 \
75+
--state-file company-monitor-state.json \
76+
--output-dir company-monitor-output
77+
```
78+
79+
Live mode sends `keywords`, `language`, `start_date`, `end_date`, `page_number`,
80+
and `page_size` for every watch. It also sends `domain` when the watch defines
81+
one.
82+
83+
Use a window and page size allowed by your Currents plan. Search windows,
84+
lookback, page sizes, and retrievable result counts can vary by plan.
85+
86+
## Deduplication and state
87+
88+
The report treats the publisher URL and article ID as aliases. A match on either
89+
alias merges the article and its watch names. The state file stores both aliases
90+
when both are present.
91+
92+
The state file prevents the same article from appearing as new on later runs.
93+
Keep a separate state file for each monitor. Back it up if missing alerts would
94+
matter to your application.
95+
96+
## Limitations
97+
98+
- The script runs once. Your application must schedule it.
99+
- The first run treats every in-window result as new.
100+
- One page is requested per watch. Increase coverage only within plan limits.
101+
- Names and Boolean terms can miss aliases or match unrelated entities.
102+
- Results reflect the Currents index and are not exhaustive.
103+
- Currents does not verify article claims or score their truth.
104+
- API access does not grant publisher-content republication rights.
105+
- Add retries, locking, state recovery, observability, and human review before
106+
using this pattern in a production workflow.
107+
108+
## Test
109+
110+
Run the focused offline tests:
111+
112+
```bash
113+
python -m pytest tests/test_company_news_monitor_example.py
114+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"_fixture_generated_at": "2026-08-05T09:00:00Z",
3+
"_fixture_start_date": "2026-08-04T09:00:00Z",
4+
"_fixture_end_date": "2026-08-05T09:00:00Z",
5+
"responses": {
6+
"Battery competitors": {
7+
"status": "ok",
8+
"news": [
9+
{
10+
"id": "fictional-factory-1",
11+
"title": "Northstar Battery opens a pilot recycling line",
12+
"description": "A fictional company begins pilot operations.",
13+
"url": "https://example.com/business/northstar-pilot-line",
14+
"published": "2026-08-05T07:30:00Z"
15+
},
16+
{
17+
"id": "fictional-joint-project-1",
18+
"title": "Atlas Storage joins a grid demonstration",
19+
"description": "A fictional storage supplier joins a demonstration.",
20+
"url": "https://example.com/energy/atlas-grid-project",
21+
"published": "2026-08-04T14:00:00Z"
22+
}
23+
]
24+
},
25+
"Industry policy": {
26+
"status": "ok",
27+
"news": [
28+
{
29+
"id": "fictional-policy-1",
30+
"title": "Regulator publishes a grid storage consultation",
31+
"description": "A fictional regulator opens a consultation period.",
32+
"url": "https://example.com/policy/grid-storage-consultation",
33+
"published": "2026-08-05T08:00:00Z"
34+
},
35+
{
36+
"id": "fictional-joint-project-1",
37+
"title": "Atlas Storage joins a grid demonstration",
38+
"description": "A fictional storage supplier joins a demonstration.",
39+
"url": "https://example.com/energy/atlas-grid-project",
40+
"published": "2026-08-04T14:00:00Z"
41+
}
42+
]
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)