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.

Base URL: https://open.hnsrd.ai/v1

Quick Start

1

Get an API Key

Register at your local parliament's Hansard portal to receive your API key.

2

Make a Request

Include your API key in the X-API-Key header with each request.

3

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 Required: All API requests must include a 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:

CodeCountry
AIA Anguilla
Getting API Access: To request an API key, contact the Hansard office of the parliament whose data you wish to access. Each parliament manages its own API keys. Once authorized, your key can be granted access to multiple countries.

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"
Security Note: Header authentication is recommended for production applications to avoid exposing your API key in logs and browser history.

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)
See Deployments for a list of active parliaments and how to request API access.

Rate Limits

API usage is rate-limited based on your subscription tier:

Free

100
requests/day

10/minute

Standard

1,000
requests/day

60/minute

Unlimited

no limits

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

200 OK
{
  "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

200 OK
{
  "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

401 Unauthorized
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

HTTP Status Codes

Code Description
200Success
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - API key inactive or expired
404Not Found - Resource doesn't exist
429Too Many Requests - Rate limit exceeded
500Internal Server Error

Error Codes

Code Description
UNAUTHORIZEDAPI key is missing or invalid
FORBIDDENAPI key is inactive
NOT_FOUNDRequested resource not found
BAD_REQUESTInvalid request parameters
RATE_LIMIT_DAILYDaily rate limit exceeded
RATE_LIMIT_MINUTEPer-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).

GET /v1/sittings List all published sittings

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page (max 100)
sortstringSort by: sitting_date, sittingname, created_at
orderstringasc or desc
date_fromstringFrom date (YYYY-MM-DD)
date_tostringTo date (YYYY-MM-DD)
yearintFilter by year
committeestringFilter 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

200 OK
{
  "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
  }
}
GET /v1/sittings/{id} Get sitting details

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"
GET /v1/sittings/{id}/speeches Get all speeches in a sitting

Returns all speeches from a sitting in chronological order.

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page
GET /v1/sittings/{id}/topics Get all topics discussed

Returns all topics discussed in a sitting with speech counts.

GET /v1/sittings/{id}/summary Get AI-generated summary

Returns the AI-generated summary for a sitting.

GET /v1/sittings/{id}/transcript Get full transcript

Returns the full transcript (all speeches concatenated in order).

Speeches

Individual speech/contribution records.

GET /v1/speeches List speeches with filters

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page
member_idstringFilter by member ID
sitting_idstringFilter by sitting ID
topicstringFilter by topic reference
date_fromstringFrom date (YYYY-MM-DD)
date_tostringTo date (YYYY-MM-DD)
yearintFilter by year
GET /v1/speeches/{id} Get speech details

Returns full speech details including complete text.

GET /v1/speeches/search Full-text search

Parameters

ParameterTypeRequiredDescription
qstringYesSearch query (min 3 characters)
pageintNoPage number
per_pageintNoItems 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.

GET /v1/members List all members

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page
sortstringSort by: name, party, district
orderstringasc or desc
partystringFilter by party
constituencystringFilter by constituency
currentstringtrue for current members only
electedstringtrue to exclude clerks/staff
GET /v1/members/{id} Get member profile

Returns member profile with speech and attendance counts.

GET /v1/members/{id}/speeches Get speeches by member

Returns all speeches by a member with pagination and date filtering.

GET /v1/members/{id}/stats Get member statistics

Returns detailed statistics including total speeches, sittings attended, average speech length, and top topics.

GET /v1/members/by-party Group members by party

Returns members grouped by political party.

GET /v1/members/by-constituency Group members by district

Returns members grouped by electoral district.

Topics

Debate topics and subjects.

GET /v1/topics List all topics

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page
typestringFilter by type: bill, motion, procedural, other
date_fromstringFrom date
date_tostringTo date
yearintFilter by year
GET /v1/topics/{ref} Get topic details

Returns topic details by reference.

GET /v1/topics/{ref}/speeches Get speeches for topic

Returns all speeches related to a topic.

Bills

Legislation tracking.

GET /v1/bills List all bills

Parameters

ParameterTypeDescription
pageintPage number
per_pageintItems per page
date_fromstringFrom date
date_tostringTo date
yearintFilter by year

Response

200 OK
{
  "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
    }
  ]
}
GET /v1/bills/in-progress Bills in progress

Returns bills that have not yet received royal assent (still in readings).

GET /v1/bills/passed Passed bills

Returns bills that have received royal assent.

GET /v1/bills/{reference} Get bill details

Returns bill details with complete reading history.

Response

200 OK
{
  "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.

GET /v1/committees List committee types

Returns all committee/meeting types with sitting counts.

Constituencies

Electoral districts.

GET /v1/constituencies List electoral districts

Returns all electoral districts with member counts.

Calendar

Sitting schedule.

GET /v1/calendar Calendar overview

Returns years with sitting counts.

GET /v1/calendar/{year} Sittings by year

Returns monthly breakdown and all sittings for a year.

GET /v1/calendar/{year}/{month} Sittings by month

Returns sittings for a specific month.

Statistics

Parliamentary statistics.

GET /v1/stats Get statistics

Returns comprehensive parliamentary statistics including totals, top speakers, yearly breakdowns, and party composition.

Response

200 OK
{
  "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