Local knowledge for your AI tools
Connect Cursor, VS Code, Codex or Claude Code. Free MCP access, no key needed.
Quickstart
No key and no sign-up: every public data endpoint is a plain GET on https://princegeorgepulse.ca/api/v1. These examples use discovery endpoints available on every city instance. Use the reference for enabled local datasets.
curl #
# Public pages and local search
curl --fail-with-body "https://princegeorgepulse.ca/api/v1/pages?limit=5"
curl --fail-with-body "https://princegeorgepulse.ca/api/v1/search?q=parks&per=3"
# A compact result, or readable Markdown
curl --fail-with-body "https://princegeorgepulse.ca/api/v1/pages?limit=5&fields=title,url"
curl --fail-with-body -H "Accept: text/markdown" "https://princegeorgepulse.ca/api/v1/pages?limit=5"
# Read the ETag, then send it back to check for changes
curl -I "https://princegeorgepulse.ca/api/v1/pages?limit=5"
curl -i -H 'If-None-Match: W/"replace-with-etag-from-above"' "https://princegeorgepulse.ca/api/v1/pages?limit=5"A conditional request can return 304 with no body; reuse the response you already cached. Missing or stale data is not zero: check meta.stale, meta.updatedAt and each source's status.
JavaScript #
This example reads a paginated collection with a deadline per request, at most three attempts per page, and a page cap. It follows only this API's pagination links. A rate limit that exceeds the retry budget is returned to your application rather than retried forever.
const BASE = 'https://princegeorgepulse.ca/api/v1'
async function all(path, maxPages = 100) {
const out = [], seen = new Set()
let next = new URL(path, BASE + '/')
for (let page = 0; next && page < maxPages; page++) {
if (next.origin !== new URL(BASE).origin || !next.pathname.startsWith('/api/v1/')) {
throw new Error('Unexpected pagination destination')
}
if (seen.has(next.href)) throw new Error('Repeated pagination link')
seen.add(next.href)
let response
for (let attempt = 0; attempt < 3; attempt++) {
response = await fetch(next, { signal: AbortSignal.timeout(15000), redirect: 'error' })
if (![429, 503].includes(response.status) || attempt === 2) break
const delay = Number(response.headers.get('Retry-After') || 5)
if (!Number.isFinite(delay) || delay < 1 || delay > 60) break
await response.body?.cancel()
await new Promise(resolve => setTimeout(resolve, delay * 1000))
}
if (!response.ok) throw new Error(`API returned HTTP ${response.status}`)
const body = await response.json()
if (!Array.isArray(body.data)) throw new Error('Expected a list endpoint')
if (body.meta?.stale) console.warn('Using stale data:', body.meta.attribution)
out.push(...body.data)
next = body.links?.next ? new URL(body.links.next, BASE + '/') : null
}
if (next) throw new Error('Page cap reached; narrow the filters or resume explicitly')
return out
}
const pages = await all('pages?limit=100')
console.log(pages.length, 'public pages')Python #
import requests
BASE = "https://princegeorgepulse.ca/api/v1"
response = requests.get(BASE + "/pages", params={"limit": 5}, timeout=15)
response.raise_for_status()
body = response.json()
print("Source:", body["meta"]["attribution"])
for page in body["data"]:
print(page.get("title"), page.get("url"))
print("Next page:", body["links"].get("next"))Keep the source credit and licence when storing or displaying results. Cache successful responses for the advertised lifetime; honour Retry-After when you receive 429 or 503.
For AI agents #
- Connect through MCP: install links and configuration for Cursor, VS Code, Codex and Claude Code.
- Start at llms.txt for discovery.
- Read any public page as Markdown by adding .md, for example https://princegeorgepulse.ca/about.md.
- Use format=md on a public data API call for readable output.
- Use the OpenAPI document for parameter and response schemas. Prefer targeted MCP discovery over loading the entire specification into a prompt.