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 +167 -0
- package/dist/index.d.mts +693 -0
- package/dist/index.d.ts +693 -0
- package/dist/index.js +350 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +307 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +45 -0
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
|