parliament-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # parliament-api
2
+
3
+ Typed TypeScript client for the UK Parliament APIs.
4
+
5
+ Covers three public APIs:
6
+ - **Members API** — MPs, Lords, parties, constituencies
7
+ - **Questions & Statements API** — Written questions, written statements, daily reports
8
+ - **Hansard API** — Debates, divisions, contributions, search
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install parliament-api
14
+ ```
15
+
16
+ Requires Node.js 18+ (uses native `fetch`).
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { ParliamentAPI, House } from 'parliament-api';
22
+
23
+ const api = new ParliamentAPI();
24
+
25
+ // Search current MPs
26
+ const mps = await api.members.search({ house: House.Commons, isCurrentMember: true });
27
+
28
+ // Get a specific member
29
+ const member = await api.members.getById(4514);
30
+ console.log(member.value.nameDisplayAs); // "Sir Keir Starmer"
31
+
32
+ // Search written questions
33
+ const questions = await api.questions.searchWrittenQuestions({
34
+ askingMemberId: 4514,
35
+ tabledWhenFrom: '2025-01-01',
36
+ });
37
+ console.log(questions.totalResults);
38
+ console.log(questions.results[0].value.heading);
39
+
40
+ // Search Hansard
41
+ const hansard = await api.hansard.search({
42
+ searchTerm: 'climate change',
43
+ house: 'Commons',
44
+ });
45
+ console.log(hansard.TotalContributions);
46
+
47
+ // Auto-paginate through all results
48
+ for await (const page of api.members.searchAll({ house: House.Commons, isCurrentMember: true })) {
49
+ for (const mp of page) {
50
+ console.log(mp.value.nameDisplayAs);
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Configuration
56
+
57
+ ```typescript
58
+ const api = new ParliamentAPI({
59
+ concurrency: 10, // max concurrent requests (default: 5)
60
+ retries: 5, // retry attempts on 429/5xx (default: 3)
61
+ retryDelay: 2000, // base delay in ms for backoff (default: 1000)
62
+ });
63
+ ```
64
+
65
+ You can also use individual clients directly:
66
+
67
+ ```typescript
68
+ import { MembersClient, QuestionsClient, HansardClient } from 'parliament-api';
69
+
70
+ const members = new MembersClient({ concurrency: 10 });
71
+ const questions = new QuestionsClient();
72
+ const hansard = new HansardClient();
73
+ ```
74
+
75
+ ## API Reference
76
+
77
+ ### `api.members`
78
+
79
+ | Method | Description |
80
+ |---|---|
81
+ | `search(params)` | Search current MPs/Lords |
82
+ | `searchHistorical(params)` | Search former members |
83
+ | `searchAll(params)` | Auto-paginating search (async generator) |
84
+ | `getById(id)` | Single member details |
85
+ | `getBiography(id)` | Career history, posts, committees |
86
+ | `getContact(id)` | Contact details |
87
+ | `getContributionSummary(id, params)` | Activity breakdown |
88
+ | `getEdms(id, params)` | Early Day Motions |
89
+ | `getExperience(id)` | Member experience |
90
+ | `getFocus(id)` | Policy interest areas |
91
+ | `getLatestElectionResult(id)` | Election result + majority |
92
+ | `getRegisteredInterests(id)` | Financial interests |
93
+ | `getStaff(id)` | Staff list |
94
+ | `getSynopsis(id)` | Member synopsis |
95
+ | `getVoting(id, params)` | Voting record |
96
+ | `getWrittenQuestions(id, params)` | Written questions |
97
+ | `getPortraitUrl(id)` | Portrait image URL |
98
+ | `getThumbnailUrl(id)` | Thumbnail image URL |
99
+ | `getGovernmentPosts()` | Government posts + holders |
100
+ | `getOppositionPosts()` | Opposition posts + holders |
101
+ | `getSpokespersons()` | Spokespersons |
102
+ | `getActiveParties(house)` | Active parties |
103
+ | `getStateOfTheParties(house, date)` | Party seat counts |
104
+ | `searchConstituencies(params)` | Search constituencies |
105
+ | `getConstituency(id)` | Constituency details |
106
+ | `getConstituencyElectionResults(id)` | Election results |
107
+
108
+ ### `api.questions`
109
+
110
+ | Method | Description |
111
+ |---|---|
112
+ | `searchWrittenQuestions(params)` | Search written questions |
113
+ | `searchAllWrittenQuestions(params)` | Auto-paginating search (async generator) |
114
+ | `getWrittenQuestion(id)` | Single written question |
115
+ | `searchWrittenStatements(params)` | Search written statements |
116
+ | `searchAllWrittenStatements(params)` | Auto-paginating search (async generator) |
117
+ | `getWrittenStatement(id)` | Single written statement |
118
+ | `getDailyReports(params)` | Daily report listings |
119
+
120
+ ### `api.hansard`
121
+
122
+ | Method | Description |
123
+ |---|---|
124
+ | `search(params)` | Full-text search across Hansard |
125
+ | `searchContributions(type, params)` | Search by contribution type |
126
+ | `getMemberContributionSummary(id, params)` | Member contribution summary |
127
+ | `searchDebates(params)` | Search debates |
128
+ | `searchDivisions(params)` | Search divisions |
129
+ | `searchMembers(params)` | Search members |
130
+ | `getDebate(id)` | Full debate with contributions |
131
+ | `getDebateSpeakers(id)` | Speaker list for debate |
132
+ | `getDebateDivisions(id)` | Divisions in debate |
133
+ | `getMemberDebateContributions(id)` | Member's debate contributions |
134
+ | `getSittingDays(params)` | Historic sitting days |
135
+ | `getLastSittingDate()` | Most recent sitting date |
136
+ | `getSectionsForDay(params)` | Hansard sections for a date |
137
+
138
+ ## Response shapes
139
+
140
+ The Members API and Questions API wrap responses differently:
141
+
142
+ ```typescript
143
+ // Members API: items in PaginatedResponse
144
+ const mps = await api.members.search({ house: House.Commons });
145
+ mps.items // MemberApiItem<Member>[]
146
+ mps.totalResults // number
147
+
148
+ // Questions API: results in QuestionsApiResponse
149
+ const qs = await api.questions.searchWrittenQuestions({ askingMemberId: 4514 });
150
+ qs.results // MemberApiItem<WrittenQuestion>[]
151
+ qs.totalResults // number
152
+
153
+ // Individual items are wrapped in { value, links }
154
+ const member = await api.members.getById(4514);
155
+ member.value // Member
156
+ member.links // Link[]
157
+
158
+ // Hansard API: flat response with PascalCase fields
159
+ const h = await api.hansard.search({ searchTerm: 'climate' });
160
+ h.TotalContributions // number
161
+ h.Contributions // HansardContribution[]
162
+ h.Debates // HansardDebateSummary[]
163
+ ```
164
+
165
+ ## License
166
+
167
+ MIT