# 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](https://princegeorgepulse.ca/developers/reference/) for enabled local datasets.

## curl

```bash
# 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.

```js
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

```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](https://princegeorgepulse.ca/developers/mcp/): install links and configuration for Cursor, VS Code, Codex and Claude Code.
- Start at [llms.txt](https://princegeorgepulse.ca/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](https://princegeorgepulse.ca/api/v1/openapi.json) for parameter and response schemas. Prefer targeted MCP discovery over loading the entire specification into a prompt.
