Castus APIcloud.castus.tv ↗
Retrieve Michigan Senate floor sessions and committee meeting schedules by date, including live, scheduled, and completed airings with HLS stream URLs.
What is the Castus API?
The cloud.castus.tv API provides a single get_schedule endpoint that returns every Michigan Senate video airing for a given calendar day, exposing up to 9 response fields per item including start_time_utc, event_id, title, and playable HLS stream URLs. Items cover live channels currently on air, programmed channel blocks, and completed recordings, all ordered oldest-first in America/Detroit time.
curl -X GET 'https://api.parse.bot/scraper/bdf80593-28fe-4e4e-9a82-a74b80592f6b/get_schedule?date=20261001' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace cloud-castus-tv-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: Michigan Senate TV schedule — bounded, re-runnable."""
from parse_apis.cloud_castus_tv_api import CastusTv, InputFormatInvalid
client = CastusTv()
# List all airings for a given day, capped at 5 items.
for event in client.events.list(date="20261001", limit=5):
print(event.title, event.status, event.start_time_utc)
# Drill into one event to inspect stream and duration.
event = client.events.list(date="20261001", limit=1).first()
if event is not None:
print(event.event_id, event.type, event.duration_minutes, "min")
print("stream:", event.stream_url)
print("thumbnail:", event.thumbnail)
# Demonstrate the typed error for a malformed date.
try:
client.events.list(date="not-a-date", limit=1).first()
except InputFormatInvalid as e:
print("bad date format:", e.message)
print("exercised: events.list / InputFormatInvalid")
Returns every Michigan Senate airing for one calendar day in America/Detroit, oldest first: live channels currently on air (status 'live'; only when the requested day is today and a channel is recording), programmed channel items for that day (status 'scheduled'/'live'/'completed' by clock time; the channel programme window only covers roughly yesterday through three days ahead, and outside the Senate's own events it carries only default signage, which is excluded), and every recording published that day (status 'completed', one item per video). The day is derived from each video's publish timestamp converted to America/Detroit, which is the date the site displays. 'type' is 'session' when the title contains the word 'session', otherwise 'committee'. 'description', 'location', 'category', 'series_title' and 'program_id' are null unless the source supplies them (recordings had none during testing). Each call makes a fixed number of round trips (live + 3 channel programmes + a binary search over the newest-first video archive, at most 12 archive pages). Pagination is local: offset/limit slice the full day's list, total_available is the whole day's count, has_more tells whether items remain after this page; limit omitted means the whole day (capped at 200 per page). A day with nothing aired returns items [] with total 0.
| Param | Type | Description |
|---|---|---|
| daterequired | string | Calendar day as YYYYMMDD, interpreted in America/Detroit (e.g. 20261001). Echoed back as data.date. |
| limit | integer | Maximum items in the returned page (1-200). Omitted returns the whole day in one page. |
| offset | integer | Number of items of the day's list to skip before the returned page. |
{
"type": "object",
"fields": {
"date": "requested day echoed back as YYYYMMDD",
"items": "array of airings for the page, each with event_id, title, date (YYYY-MM-DD), time (local display), start_time_utc (ISO-8601 Z), status (live|scheduled|completed), institution ('senate'), type (session|committee), description, duration (H:MM:SS display or null), duration_minutes (integer or null), url (Castus page), player_url, stream_url (HLS .m3u8 or null), video_file (playable media URL or null), location, thumbnail, category, series_title, program_id",
"limit": "page size applied (equals total_available when limit was omitted; 0 on an empty day)",
"total": "number of items in this page",
"offset": "offset applied",
"has_more": "true when items remain after this page",
"total_available": "number of airings for the whole day before pagination"
},
"sample": {
"data": {
"date": "20260922",
"items": [
{
"url": "https://cloud.castus.tv/vod/misenate/video/6ab28c51f4c8330002b0ebcd",
"date": "2026-09-22",
"time": "10:10 AM",
"type": "session",
"title": "Senate Session 26-09-22",
"status": "completed",
"category": null,
"duration": "0:08:57",
"event_id": "6ab28c51f4c8330002b0ebcd",
"location": null,
"thumbnail": "https://dlttx48mxf9m3.cloudfront.net/outputs/6ab28c51f4c8330002b0ebcd/Default/Thumbnails/out_003.png",
"player_url": "https://cloud.castus.tv/vod/misenate/video/6ab28c51f4c8330002b0ebcd?page=HOME",
"program_id": null,
"stream_url": "https://dlttx48mxf9m3.cloudfront.net/outputs/6ab28c51f4c8330002b0ebcd/Default/HLS/out.m3u8",
"video_file": "https://dlttx48mxf9m3.cloudfront.net/outputs/6ab28c51f4c8330002b0ebcd/Default/HLS/out.m3u8",
"description": null,
"institution": "senate",
"series_title": null,
"start_time_utc": "2026-09-22T14:10:23Z",
"duration_minutes": 8
}
],
"limit": 1,
"total": 1,
"offset": 1,
"has_more": true,
"total_available": 6
},
"status": "success"
}
}About the Castus API
What the API Returns
The get_schedule endpoint returns an ordered list of Michigan Senate video airings for a single calendar day interpreted in the America/Detroit timezone. Each item in the items array carries an event_id, title, display time, date (YYYY-MM-DD), start_time_utc (ISO-8601 UTC), and a status field that distinguishes live, scheduled, and completed airings. Live items only appear when the requested day matches today and a channel is actively recording.
Inputs and Pagination
The required date parameter accepts an eight-digit string in YYYYMMDD format (e.g. 20261001) and is echoed back in the response as data.date. Optional limit (1–200) and offset integers control pagination. When limit is omitted, all airings for the day are returned in one page. The response includes total_available (full-day count), total (items in this page), has_more (boolean), and offset so you can walk through large days incrementally.
Stream Access
Each airing object includes a playable HLS stream URL, making it straightforward to build a video player or archival pipeline directly from schedule data. Because start times are provided in both local display format and ISO-8601 UTC, you can accurately slot airings into calendars or timeline UIs without timezone conversion guesswork.
Coverage Scope
Content is scoped to the Michigan Senate's video channel hosted at cloud.castus.tv. Floor sessions and committee meetings that air on that channel on the requested date appear in results. Historical days with no airings return an empty items array with limit: 0.
The Castus API is a managed, monitored endpoint for cloud.castus.tv — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when cloud.castus.tv changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official cloud.castus.tv API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Build a daily digest of Michigan Senate committee hearings using
titleandstart_time_utcfields - Detect when a Senate floor session goes live by polling
get_schedulefor today and checking forstatus: 'live'items - Embed a live Senate stream in a civic-tech app using the HLS URL returned per airing
- Archive completed Senate recordings by collecting
event_idand stream URLs for eachstatus: 'completed'item - Power a legislative calendar widget showing scheduled Senate airings for any date using
date,time, andtitle - Paginate through a busy session day with
limitandoffsetto process airings in batches
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does cloud.castus.tv have an official developer API?+
What does the `get_schedule` endpoint return for a day with no Senate activity?+
items array, total_available: 0, and limit: 0. The date field still echoes back the requested day in YYYYMMDD format.Can I retrieve schedules for Michigan House sessions or other state legislature chambers?+
How do live items differ from scheduled or completed items in the response?+
status: 'live' only appear when the requested date is today and a channel is actively recording at query time. Items with status: 'scheduled' are programmed but have not yet aired. Items with status: 'completed' have finished airing and their HLS stream URLs reference recorded content.Does the API expose metadata like bill numbers, speaker names, or committee membership?+
event_id, title, date, time, start_time_utc, status, and an HLS stream URL, but no structured legislative metadata such as bill numbers or participants. You can fork it on Parse and revise it to add an endpoint that pulls richer legislative detail from another public source.