Introduction
The Open Parliament API provides programmatic access to parliamentary data including sittings, speeches, members, bills, and more. Build applications that help citizens engage with their democracy.
https://open.hnsrd.ai/v1
Quick Start
Get an API Key
Register at your local parliament's Hansard portal to receive your API key.
Make a Request
Include your API key in the X-API-Key header with each request.
Explore the Data
Browse sittings, speeches, members, bills, and more through our endpoints.
Your First Request
curl -H "X-API-Key: your_api_key" "https://open.hnsrd.ai/v1/sittings?country=AIA"
country parameter with a valid ISO 3166-1 alpha-3 code (e.g., AIA for Anguilla, BHS for Bahamas). Your API key is authorized for specific countries based on your license.
Deployments
The Open Parliament API provides unified access to parliamentary data from multiple jurisdictions. Each parliament operates its own Hansard system and issues API keys independently.
Active Deployments
The following countries are currently live on the network:
| Code | Country |
|---|---|
AIA |
Anguilla |
Authentication
All API requests require authentication via an API key. You can pass your key in two ways:
Header Authentication (Recommended)
curl -H "X-API-Key: hnsrd_your_api_key_here" "https://open.hnsrd.ai/v1/sittings?country=AIA"
Query Parameter
curl "https://open.hnsrd.ai/v1/sittings?api_key=hnsrd_your_api_key_here&country=AIA"
Country Access
API keys are tied to specific country deployments through your license. Each request must include the country parameter.
| Parameter | Type | Required | Description |
|---|---|---|---|
country |
string | Yes | ISO 3166-1 alpha-3 country code (e.g., AIA, BHS, JAM) |
Rate Limits
API usage is rate-limited based on your subscription tier:
Free
10/minute
Standard
60/minute
Premium
300/minute
Unlimited
Enterprise
Rate Limit Headers
Every response includes rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1702080000
Response Format
All responses are returned in JSON format.
Success Response
{
"success": true,
"data": {
"id": "abc123",
"name": "Budget Debate 2024",
"date": "2024-03-15"
},
"meta": {
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
}
}
List Response
{
"success": true,
"data": [
{ "id": "abc123", "name": "Item 1" },
{ "id": "def456", "name": "Item 2" }
],
"meta": {
"total": 50,
"page": 1,
"per_page": 20,
"total_pages": 3
}
}
Error Handling
Error Response Format
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}
HTTP Status Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - API key inactive or expired |
404 | Not Found - Resource doesn't exist |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error |
Error Codes
| Code | Description |
|---|---|
UNAUTHORIZED | API key is missing or invalid |
FORBIDDEN | API key is inactive |
NOT_FOUND | Requested resource not found |
BAD_REQUEST | Invalid request parameters |
RATE_LIMIT_DAILY | Daily rate limit exceeded |
RATE_LIMIT_MINUTE | Per-minute rate limit exceeded |
Pagination
List endpoints support pagination via query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
page |
integer | 1 | - | Page number |
per_page |
integer | 20 | 100 | Items per page |
Filtering
| Parameter | Format | Description |
|---|---|---|
date_from |
YYYY-MM-DD | Filter from date (inclusive) |
date_to |
YYYY-MM-DD | Filter to date (inclusive) |
year |
YYYY | Filter by year |
sort |
string | Field to sort by |
order |
asc/desc | Sort direction |
Sittings
Parliamentary sittings (sessions/meetings).
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page (max 100) |
sort | string | Sort by: sitting_date, sittingname, created_at |
order | string | asc or desc |
date_from | string | From date (YYYY-MM-DD) |
date_to | string | To date (YYYY-MM-DD) |
year | int | Filter by year |
committee | string | Filter by committee type |
Example Request
curl -H "X-API-Key: your_key" \
"https://open.hnsrd.ai/v1/sittings?country=AIA&year=2024&per_page=10"
Response
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "House Meeting - March 15, 2024",
"date": "2024-03-15",
"committee": "House Meetings",
"video_url": "https://youtube.com/watch?v=...",
"slug": "2024-03-15",
"is_completed": true,
"published_at": "2024-03-16T10:00:00Z"
}
],
"meta": {
"total": 150,
"page": 1,
"per_page": 10,
"total_pages": 15
}
}
Returns detailed information about a specific sitting including summary, speech count, and topic count.
Example Request
curl -H "X-API-Key: your_key" \
"https://open.hnsrd.ai/v1/sittings/550e8400-e29b-41d4-a716-446655440000?country=AIA"
Returns all speeches from a sitting in chronological order.
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page |
Returns all topics discussed in a sitting with speech counts.
Returns the AI-generated summary for a sitting.
Returns the full transcript (all speeches concatenated in order).
Speeches
Individual speech/contribution records.
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page |
member_id | string | Filter by member ID |
sitting_id | string | Filter by sitting ID |
topic | string | Filter by topic reference |
date_from | string | From date (YYYY-MM-DD) |
date_to | string | To date (YYYY-MM-DD) |
year | int | Filter by year |
Returns full speech details including complete text.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Yes | Search query (min 3 characters) |
page | int | No | Page number |
per_page | int | No | Items per page |
Example Request
curl -H "X-API-Key: your_key" \
"https://open.hnsrd.ai/v1/speeches/search?country=AIA&q=education%20budget"
Members
Parliament members and speakers.
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page |
sort | string | Sort by: name, party, district |
order | string | asc or desc |
party | string | Filter by party |
constituency | string | Filter by constituency |
current | string | true for current members only |
elected | string | true to exclude clerks/staff |
Returns member profile with speech and attendance counts.
Returns all speeches by a member with pagination and date filtering.
Returns detailed statistics including total speeches, sittings attended, average speech length, and top topics.
Returns members grouped by political party.
Returns members grouped by electoral district.
Topics
Debate topics and subjects.
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page |
type | string | Filter by type: bill, motion, procedural, other |
date_from | string | From date |
date_to | string | To date |
year | int | Filter by year |
Returns topic details by reference.
Returns all speeches related to a topic.
Bills
Legislation tracking.
Parameters
| Parameter | Type | Description |
|---|---|---|
page | int | Page number |
per_page | int | Items per page |
date_from | string | From date |
date_to | string | To date |
year | int | Filter by year |
Response
{
"success": true,
"data": [
{
"bill_reference": "BILL-2024-001",
"title": "Education Reform Act 2024",
"status": "second_reading",
"introduced_date": "2024-01-15",
"last_action_date": "2024-03-10",
"latest_reading": "second",
"sitting_count": 3,
"speech_count": 45
}
]
}
Returns bills that have not yet received royal assent (still in readings).
Returns bills that have received royal assent.
Returns bill details with complete reading history.
Response
{
"success": true,
"data": {
"bill_reference": "BILL-2024-001",
"title": "Education Reform Act 2024",
"status": "second_reading",
"introduced_date": "2024-01-15",
"passed_date": null,
"readings": [
{
"reading_type": "first",
"sitting_id": "sitting-uuid-1",
"sitting_name": "House Meeting - Jan 15, 2024",
"sitting_date": "2024-01-15",
"speech_count": 12
},
{
"reading_type": "second",
"sitting_id": "sitting-uuid-2",
"sitting_name": "House Meeting - Mar 10, 2024",
"sitting_date": "2024-03-10",
"speech_count": 33
}
]
}
}
Committees
Committee and meeting types.
Returns all committee/meeting types with sitting counts.
Constituencies
Electoral districts.
Returns all electoral districts with member counts.
Search
Global search across all content.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Yes | Search query (min 3 characters) |
type | string | No | Filter: all, speeches, members, sittings, topics |
Example Request
curl -H "X-API-Key: your_key" \
"https://open.hnsrd.ai/v1/search?country=AIA&q=education&type=all"
Calendar
Sitting schedule.
Returns years with sitting counts.
Returns monthly breakdown and all sittings for a year.
Returns sittings for a specific month.
Statistics
Parliamentary statistics.
Returns comprehensive parliamentary statistics including totals, top speakers, yearly breakdowns, and party composition.
Response
{
"success": true,
"data": {
"overview": {
"total_sittings": 450,
"total_speeches": 12500,
"total_members": 35,
"total_topics": 890,
"first_sitting": "2019-01-15",
"last_sitting": "2024-03-15"
},
"top_speakers": [
{ "name": "Hon. John Smith", "id": "uuid", "speech_count": 450 }
],
"sittings_by_year": [
{ "year": 2024, "sitting_count": 45 }
],
"sittings_by_committee": [
{ "committee": "House Meetings", "sitting_count": 300 }
],
"members_by_party": [
{ "party": "Progressive Party", "member_count": 15 }
],
"generated_at": "2024-03-15T10:00:00Z"
}
}
Code Examples
# List recent sittings (country=AIA for Anguilla)
curl -H "X-API-Key: hnsrd_your_key" \
"https://open.hnsrd.ai/v1/sittings?country=AIA&per_page=10"
# Search speeches
curl -H "X-API-Key: hnsrd_your_key" \
"https://open.hnsrd.ai/v1/speeches/search?country=AIA&q=education%20budget"
# Get member statistics
curl -H "X-API-Key: hnsrd_your_key" \
"https://open.hnsrd.ai/v1/members/member-uuid/stats?country=AIA"
# Get bills in progress
curl -H "X-API-Key: hnsrd_your_key" \
"https://open.hnsrd.ai/v1/bills/in-progress?country=AIA"
# Global search
curl -H "X-API-Key: hnsrd_your_key" \
"https://open.hnsrd.ai/v1/search?country=AIA&q=healthcare&type=all"
const API_KEY = 'hnsrd_your_key';
const BASE_URL = 'https://open.hnsrd.ai/v1';
const COUNTRY = 'AIA'; // Your authorized country code
// Fetch helper - always includes country parameter
async function apiRequest(endpoint, params = {}) {
const url = new URL(BASE_URL + endpoint);
params.country = COUNTRY; // Required on all requests
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
const response = await fetch(url, {
headers: { 'X-API-Key': API_KEY }
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
// Get recent sittings
async function getSittings(page = 1, year = null) {
const params = { page, per_page: 20 };
if (year) params.year = year;
return apiRequest('/sittings', params);
}
// Search speeches
async function searchSpeeches(query) {
const data = await apiRequest('/speeches/search', { q: query });
return data.data.results;
}
// Get member statistics
async function getMemberStats(memberId) {
const data = await apiRequest(`/members/${memberId}/stats`);
return data.data;
}
// Get bills in progress
async function getBillsInProgress() {
const data = await apiRequest('/bills/in-progress');
return data.data.bills;
}
// Usage
(async () => {
// Get 2024 sittings
const sittings = await getSittings(1, 2024);
console.log(`Found ${sittings.meta.total} sittings in 2024`);
// Search for education speeches
const speeches = await searchSpeeches('education budget');
console.log(`Found ${speeches.length} matching speeches`);
// Get bills in progress
const bills = await getBillsInProgress();
bills.forEach(bill => {
console.log(`${bill.bill_reference}: ${bill.title} (${bill.status})`);
});
})();
import requests
from typing import Optional, Dict, Any, List
class OpenParliamentAPI:
def __init__(self, api_key: str, country: str):
self.api_key = api_key
self.country = country # Required: ISO 3166-1 alpha-3 code
self.base_url = "https://open.hnsrd.ai/v1"
self.session = requests.Session()
self.session.headers.update({"X-API-Key": api_key})
def _get(self, endpoint: str, params: Optional[Dict] = None) -> Dict[str, Any]:
if params is None:
params = {}
params["country"] = self.country # Always include country
response = self.session.get(f"{self.base_url}{endpoint}", params=params)
response.raise_for_status()
data = response.json()
if not data.get("success"):
raise Exception(data.get("error", {}).get("message", "Unknown error"))
return data
def get_sittings(self, page: int = 1, year: Optional[int] = None) -> Dict:
params = {"page": page}
if year:
params["year"] = year
return self._get("/sittings", params)
def get_sitting(self, sitting_id: str) -> Dict:
return self._get(f"/sittings/{sitting_id}")
def search_speeches(self, query: str, page: int = 1) -> Dict:
return self._get("/speeches/search", {"q": query, "page": page})
def get_members(self, page: int = 1, party: Optional[str] = None) -> Dict:
params = {"page": page}
if party:
params["party"] = party
return self._get("/members", params)
def get_member_stats(self, member_id: str) -> Dict:
return self._get(f"/members/{member_id}/stats")
def get_bills(self, status: Optional[str] = None) -> Dict:
endpoint = f"/bills/{status}" if status else "/bills"
return self._get(endpoint)
def search(self, query: str, type: str = "all") -> Dict:
return self._get("/search", {"q": query, "type": type})
def get_stats(self) -> Dict:
return self._get("/stats")
# Usage - specify your authorized country code
api = OpenParliamentAPI("hnsrd_your_key", country="AIA")
# Get 2024 sittings
sittings = api.get_sittings(year=2024)
print(f"Found {sittings['meta']['total']} sittings in 2024")
# Search speeches
results = api.search_speeches("education budget")
for speech in results['data']['results'][:5]:
print(f"{speech['speaker_name']}: {speech['text_preview'][:80]}...")
# Get bills in progress
bills = api.get_bills(status="in-progress")
for bill in bills['data']['bills']:
print(f"{bill['bill_reference']}: {bill['title']} ({bill['status']})")
# Get parliamentary stats
stats = api.get_stats()
overview = stats['data']['overview']
print(f"\nParliamentary Statistics:")
print(f" Total Sittings: {overview['total_sittings']}")
print(f" Total Speeches: {overview['total_speeches']}")
print(f" Total Members: {overview['total_members']}")
<?php
class OpenParliamentAPI {
private string $apiKey;
private string $country;
private string $baseUrl = 'https://open.hnsrd.ai/v1';
public function __construct(string $apiKey, string $country) {
$this->apiKey = $apiKey;
$this->country = $country; // Required: ISO 3166-1 alpha-3 code
}
private function request(string $endpoint, array $params = []): array {
$params['country'] = $this->country; // Always include country
$url = $this->baseUrl . $endpoint;
if ($params) {
$url .= '?' . http_build_query($params);
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $this->apiKey],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("API error: HTTP $httpCode");
}
$data = json_decode($response, true);
if (!$data['success']) {
throw new Exception($data['error']['message'] ?? 'Unknown error');
}
return $data;
}
public function getSittings(int $page = 1, ?int $year = null): array {
$params = ['page' => $page];
if ($year) $params['year'] = $year;
return $this->request('/sittings', $params);
}
public function searchSpeeches(string $query, int $page = 1): array {
return $this->request('/speeches/search', ['q' => $query, 'page' => $page]);
}
public function getMembers(int $page = 1, ?string $party = null): array {
$params = ['page' => $page];
if ($party) $params['party'] = $party;
return $this->request('/members', $params);
}
public function getMemberStats(string $id): array {
return $this->request("/members/$id/stats");
}
public function getBills(?string $status = null): array {
$endpoint = $status ? "/bills/$status" : '/bills';
return $this->request($endpoint);
}
public function search(string $query, string $type = 'all'): array {
return $this->request('/search', ['q' => $query, 'type' => $type]);
}
public function getStats(): array {
return $this->request('/stats');
}
}
// Usage - specify your authorized country code
$api = new OpenParliamentAPI('hnsrd_your_key', 'AIA');
// Get 2024 sittings
$sittings = $api->getSittings(1, 2024);
echo "Found {$sittings['meta']['total']} sittings in 2024\n";
// Search speeches
$results = $api->searchSpeeches('education budget');
foreach (array_slice($results['data']['results'], 0, 5) as $speech) {
echo "{$speech['speaker_name']}: " . substr($speech['text_preview'], 0, 80) . "...\n";
}
// Get bills in progress
$bills = $api->getBills('in-progress');
foreach ($bills['data']['bills'] as $bill) {
echo "{$bill['bill_reference']}: {$bill['title']} ({$bill['status']})\n";
}
require 'net/http'
require 'json'
require 'uri'
class OpenParliamentAPI
BASE_URL = 'https://open.hnsrd.ai/v1'
def initialize(api_key, country)
@api_key = api_key
@country = country # Required: ISO 3166-1 alpha-3 code
end
def get_sittings(page: 1, year: nil)
params = { page: page }
params[:year] = year if year
request('/sittings', params)
end
def get_sitting(id)
request("/sittings/#{id}")
end
def search_speeches(query, page: 1)
request('/speeches/search', { q: query, page: page })
end
def get_members(page: 1, party: nil)
params = { page: page }
params[:party] = party if party
request('/members', params)
end
def get_member_stats(id)
request("/members/#{id}/stats")
end
def get_bills(status: nil)
endpoint = status ? "/bills/#{status}" : '/bills'
request(endpoint)
end
def search(query, type: 'all')
request('/search', { q: query, type: type })
end
def get_stats
request('/stats')
end
private
def request(endpoint, params = {})
params[:country] = @country # Always include country
uri = URI("#{BASE_URL}#{endpoint}")
uri.query = URI.encode_www_form(params) unless params.empty?
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = @api_key
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end
data = JSON.parse(response.body)
raise "API Error: #{data['error']['message']}" unless data['success']
data
end
end
# Usage - specify your authorized country code
api = OpenParliamentAPI.new('hnsrd_your_key', 'AIA')
# Get 2024 sittings
sittings = api.get_sittings(year: 2024)
puts "Found #{sittings['meta']['total']} sittings in 2024"
# Search speeches
results = api.search_speeches('education budget')
results['data']['results'].first(5).each do |speech|
puts "#{speech['speaker_name']}: #{speech['text_preview'][0..80]}..."
end
# Get bills in progress
bills = api.get_bills(status: 'in-progress')
bills['data']['bills'].each do |bill|
puts "#{bill['bill_reference']}: #{bill['title']} (#{bill['status']})"
end