irvinebroque.com API documentation

Brendan Irvine-Broque’s public, read-only API provides access to curated recommendations across 8,057 items in 15 collections.

Quick Links

Collections

CollectionEndpointCountFilters
Activities/api/activities.json321year sportType q
Movies & TV/api/movies.json320type year genre director actor q
Places/api/places.json171city country category neighborhood q
Events/api/events.json0type city year q
Sporting Events/api/sporting-events.json116team sport league year city venue experience experienceStatus q
Music/api/music.json363genre type q
Concerts/api/concerts.json43year artist venue city q
Videos/api/videos.json352category q
Podcasts/api/podcasts.json27show q
Tweets/api/tweets.json5,130author q
Code/api/code.json803kind repo year q
Photos/api/photos.json62year month
Books/api/books.json163author q
Work/api/work.json168year kind company
Reading/api/reading.json18q domain

Public movie taste summary

Use /api/taste/summary.json for overall movie and TV taste, patterns, breadth, and collection composition. Its counts and distributions cover the complete public, non-deleted movie corpus explicitly marked recommended. Use/api/movies.json for record-level filtering and taste.find for one known item.

returnedCount counts illustrative examples and collectionCountcounts the eligible corpus. Coverage uses that corpus as its denominator. Genre and director are multi-value dimensions, so their shares may sum above 100%. The examples are deterministic illustrations, not a statistical sample. A public miss does not prove anything about private, saved, seen, or unpublished records.

Request:

Shell
curl "https://irvinebroque.com/api/taste/summary.json?collection=movies&exampleLimit=6"

Abbreviated response showing every response field:

JSON
{
  "schemaVersion": 1,
  "outcome": "ok",
  "summary": "Summarized 320 public movie and TV recommendations and returned 6 illustrative examples. Overall claims should use the distributions; the examples are not a statistical sample.",
  "scope": {
    "visibility": "public",
    "corpusId": "public-movie-recommendations",
    "label": "Brendan’s public movie and TV recommendations",
    "definition": "Public, non-deleted movie records explicitly marked recommended.",
    "inclusionRules": ["The canonical collection is movies.", "The publication state is public.", "The preference state is recommended.", "The record has not been deleted."],
    "exclusionMeaning": "Absence does not establish whether Brendan has seen, saved, or privately recorded an item elsewhere.",
    "canonicalUrl": "https://irvinebroque.com/movies"
  },
  "counts": { "returnedCount": 6, "collectionCount": 320 },
  "coverage": {
    "dataAsOf": "2026-08-30T00:00:00.000Z",
    "dimensions": [{ "dimension": "genre", "eligibleCount": 320, "populatedCount": 318, "missingCount": 2 }]
  },
  "distributions": [{
    "dimension": "genre",
    "label": "Most represented genres",
    "multiValue": true,
    "denominator": "eligible_records",
    "denominatorCount": 320,
    "values": [{ "value": "drama", "label": "Drama", "count": 201, "shareOfEligible": 0.628125 }]
  }],
  "examples": [{
    "canonicalId": "itm_0123456789abcdef01234567",
    "collection": "movies",
    "title": "Example title",
    "canonicalUrl": "https://irvinebroque.com/movies/example-title",
    "description": "Movie · 2024 · Drama",
    "evidence": ["Illustrates the drama genre distribution."],
    "evidenceDetails": [{ "code": "summary.genre-example", "text": "Illustrates the drama genre distribution.", "fields": ["genres"] }],
    "data": { "canonicalId": "itm_0123456789abcdef01234567", "title": "Example title", "type": "movie", "year": 2024, "genres": ["Drama"] }
  }],
  "selection": {
    "task": "summarize",
    "method": "deterministic-illustrative-v1",
    "role": "illustrative",
    "supportsPopulationClaims": false,
    "explanation": "Examples are selected deterministically to illustrate leading dimensions. Population claims must use the complete-corpus distributions."
  },
  "generatedAt": "2026-08-30T12:00:00.000Z",
  "renderInput": { "canonicalIds": ["itm_0123456789abcdef01234567"] }
}

Standards Compliance

This API implements the following standards:

StandardPurpose
RFC 10008Safe, idempotent HTTP QUERY method for public SQL
RFC 6922application/sql request entities
RFC 7946GeoJSON place collection exchange
RFC 5870Provider-neutral geographic location URIs
OGC KML 2.3Map annotation and place interchange
GPX 1.1Waypoint interchange with navigation applications
RFC 5545iCalendar event subscriptions
RFC 7986iCalendar calendar metadata and refresh hints
RFC 9073Structured event venue metadata
RFC 8288Web Linking (Link headers, pagination)
RFC 9652Link-Template Header Field
RFC 8631Service Discovery
RFC 9727API Catalog
ARD 0.9Agentic Resource Discovery
RFC 9457Problem Details (errors)
RFC 9309robots.txt policy publication
IETF AIPREF draftsAI usage preferences in HTTP and robots.txt
IETF RateLimit draftMachine-readable quota policies and exhaustion state
IETF HTTP API privacy draftCredential protection on plaintext transport
RFC 9116security.txt vulnerability disclosure policy
RFC 9745Deprecation Header Field
RFC 8594Sunset Header Field
RFC 9110Conditional Requests (ETag)
RFC 9111HTTP Caching
RFC 3339Date/Time Format
RFC 7231Content Negotiation

Calendar subscriptions

The calendar feeds are public, read-only iCalendar subscriptions. Subscribe to the HTTPS URL in a calendar client to receive later updates; downloading the file is a one-time snapshot. Stable domain-qualified event IDs and revision metadata let clients correlate updates and cancellations. Events include canonical detail links, categories, venue text, coordinates and structured locations when available; concert flyers are published as first-party event images. Exact sporting-event end times are included only when the canonical record supplies one.

Shell
curl -fsS https://irvinebroque.com/calendar.ics -o brendan-events.ics

Content Negotiation

All collection endpoints support multiple response formats via the Accept header:

  • application/json — JSON (default)
  • text/csv — CSV export
  • application/geo+json — GeoJSON (/api/places.json and /api/photos.json)
  • application/vnd.google-earth.kml+xml — Complete KML (/places.kml)
  • application/gpx+xml — Complete GPX waypoints (/places.gpx)
Shell
curl https://irvinebroque.com/places.geojson -o irvinebroque-places.geojson
Shell
curl https://irvinebroque.com/places.kml -o irvinebroque-places.kml
Shell
curl -H "Accept: text/csv" https://irvinebroque.com/api/movies.json
Shell
curl -H "Accept: application/geo+json" https://irvinebroque.com/api/places.json
Shell
curl -H "Accept: text/markdown" https://irvinebroque.com/movies

Public SQL v1 (Read-Only)

The public data explorer is the first-party human interface for this endpoint. It loads the generated profile for schema documentation and deterministic completion, executes only after an explicit action, renders ordered typed rows without local transforms, downloads exact JSON or API-generated CSV, and keeps share state in a bounded URL fragment that is not sent to the server.

QUERY /api/sql accepts one bounded application/sqlstatement and returns ordered row arrays as JSON or RFC 4180 CSV. Version 1 exposespublic.writing plus generated information_schema.tables andinformation_schema.columns. The generated SQL profile is the machine-readable source for relations, columns, types, null encoding, grammar, and limits. REST/OpenAPI remains the canonical public contract.

The profile accepts projection, aliases, typed literals, bounded comparisons and boolean predicates, IS NULL, IN, LIKE,ORDER BY, and LIMIT. It rejects joins, aggregates, subqueries, CTEs, functions, offset, physical tables, owner records, and every write operation. Parameters and grouping are also unavailable. Responses are never cached, and syntax errors never echo SQL, literals, identifiers, or source positions. Limits are 16 KiB per request, 100 rows, 24 returned columns, 512 KiB per encoded response, and 120 requests per 60 seconds; the generated profile owns the complete structural and work budgets.

Shell
curl --request QUERY https://irvinebroque.com/api/sql \
  -H 'Content-Type: application/sql; charset=utf-8' \
  -H 'Accept: application/json' \
  -H 'SQL-API-Version: 1.1.0' \
  --data "SELECT title, url FROM public.writing WHERE published_at >= DATE '2025-01-01' ORDER BY published_at DESC LIMIT 20"
Shell
curl --request QUERY https://irvinebroque.com/api/sql \
  -H 'Content-Type: application/sql' \
  -H 'Accept: text/csv' \
  --data "SELECT table_schema, table_name FROM information_schema.tables"

GraphQL v1 (Read-Only)

/graphql is the typed multi-root query adapter over the same website-owned records and query engine as REST. It supports one JSON operation per POST and query operations over GET. Introspection is enabled, the exact SDL is available at/graphql/schema.graphql, and browser visits open GraphiQL. There is no mutation or subscription root.

Every connection accepts first from 1 through 100 and an opaqueafter cursor. Reuse pageInfo.endCursor without decoding it. Typed filter, ordered orderBy, and includeFacetsarguments map to the canonical REST behavior. Use contentLookup for aContentLookupSuccess or typed PublicGraphQLError; transport and unexpected execution errors continue to use the GraphQL errors array. The schema’s @cost and @rateLimit directives describe the demand controls that are also reported in response headers.

GraphQL evolves additively within v1. Retiring fields and enum values carry@deprecated replacement guidance for at least 90 days, and breaking removals require a new major GraphQL-API-Version. The current API performs only synchronous reads. Its reserved AsyncJob/AsyncJobStatus types are the required model for any future long-running operation; adding such work also requires a status query and an explicit product decision before a mutation or subscription root exists.

Shell
curl https://irvinebroque.com/graphql \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/graphql-response+json' \
  --data '{"query":"query Oakland { places(filter: {query: \"Oakland\"}, first: 10) { nodes { canonicalId name city googleMapsUrl } pageInfo { hasNextPage endCursor } } }"}'

REST/OpenAPI remains the canonical language-neutral contract and the efficient choice for direct CSV, GeoJSON, KML, and GPX downloads. Use GraphQL for typed selections and several related collection roots in one request; Cap’n Web for TypeScript HTTP batching; and MCP for task-shaped recommendation, lookup, planning, and interactive rendering.

Portable music taste profile

/.well-known/taste-profile.json publishes a bounded version 1 preference projection with the media type application/taste-profile+json; version=1. It is derived from the same canonical D1 music records as the complete music API. The profile is not a complete timeline; use /music, Markdown negotiation, or /api/music.json when every public record is needed.

Shell
curl -sS -H 'Accept: application/taste-profile+json; version=1' \
  https://irvinebroque.com/.well-known/taste-profile.json

POST /api/music/intersections.json is a read-only, 32 KiB structured query. It returns canonical IDs, grounded reason codes, unmatched counts, and abstention instead of a compatibility percentage. Clients must confirm a listener profile before sending it. Cap’n Web exposes the same operation asapi.taste.music.intersect(input), returning an intersection capability whose matches, tracks, summary, and candidate IDs can be pipelined or passed to other methods.

Shell
curl -sS https://irvinebroque.com/api/music/intersections.json \
  -H 'Content-Type: application/json' \
  --data '{"profile":{"schemaVersion":"1","music":{"artists":[{"name":"Talking Heads","stance":"love"}],"recordings":[],"albums":[],"genres":[],"notes":[]}}}'

Version 1 ignores unknown external-ID schemes during normalization but rejects unknown structural fields and all contract overflows. Additive fields require a new documented version before they affect matching; supported versions receive the service’s normal deprecation and sunset notices.

POST /api/music/station.json is the strict 24 KiB, stateless queue contract. It accepts canonical affinities, direct votes, hard exclusions, recent IDs, optional short intent, and closed context, then returns at most six D1-rehydrated playable tracks. A dependency failure is explicit as status: degraded; fallback stays bounded and preserves every exclusion.

Shell
curl -sS https://irvinebroque.com/api/music/station.json \
  -H 'Content-Type: application/json' \
  --data '{"schemaVersion":"1","stationSeed":"example","affinities":[],"votes":[],"hardExclusions":[],"recentCanonicalIds":[],"limit":6}'

Cap’n Web (Stable Read-Only)

The single Cap’n Web endpoint lives at /api/rpc. It exposes a direct root capability with service, content, discovery, taste, and feed objects over HTTP batch and WebSocket. REST and OpenAPI remain the canonical, language-neutral record contract.

The endpoint is unauthenticated and read-only. A batch may contain at most32 operations within a65536-byte request. Public clients open up to 100 sessions per 60 seconds per source and should honor the Retry-After header on HTTP 429 admission responses. Accepted sessions retain separate call and weighted-work budgets.

Clients do not select or send an API version. The service publishes a generatedTypeScript declaration andconsumer metadata for the one live API. It does not expose callbacks, subscriptions, owner authority, generic execution, raw repository rows, or raw vector matches.

HTTP batch sessions are single-use. Queue calls synchronously, then await them together; start a new session for later work. Resume pages from info().endCursor withpageAfter() on that new session. next() and feed streams require WebSocket.

Available methods:

  • api.service — Service description and health capabilities
  • api.content.<collection> — Typed collection, immutable selection, page, and item capabilities
  • api.discovery — Search and recommendation capabilities with passable candidate sets
  • api.taste — Movie summaries, music profiles, intersections, and programmable stations
  • api.feed — Finite resumable feed selections, with pages over HTTP batch and streams over WebSocket

TypeScript HTTP batch example:

client.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

const rpcRequest = () => new Request('https://irvinebroque.com/api/rpc');
using api = newHttpBatchRpcSession<BrendanPublicApi>(rpcRequest());

const movies = api.content.movies
  .matching({ genre: 'Drama' })
  .orderedBy([{ field: 'releaseDate', direction: 'desc' }])
  .first(10);

// Queue dependent work before the first await. Cap'n Web pipelines it as one batch.
const infoPromise = movies.info();
const recordsPromise = movies.records();
const [info, records] = await Promise.all([infoPromise, recordsPromise]);

// HTTP batches are single-use. Resume pagination with a new root and endCursor.
if (info.endCursor) {
  using nextApi = newHttpBatchRpcSession<BrendanPublicApi>(rpcRequest());
  const nextRecords = await nextApi.content.movies
    .matching({ genre: 'Drama' })
    .orderedBy([{ field: 'releaseDate', direction: 'desc' }])
    .pageAfter(info.endCursor, 10)
    .records();
  consume(nextRecords);
}

Capability-passing dependent HTTP pipeline:

pipeline.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

using api = newHttpBatchRpcSession<BrendanPublicApi>(
  new Request('https://irvinebroque.com/api/rpc'),
);

// A remote CandidateSet is passed by capability, not copied back as IDs.
const hits = api.discovery.search({ query: 'patient late-night instrumentals' });
const recommendations = api.discovery.recommend(
  { intent: 'patient late-night instrumentals', limit: 5 },
  hits,
);

const recordsPromise = recommendations.records();
const explanationsPromise = recommendations.items().map((item) =>
  recommendations.explain(item)
);

const [records, explanations] = await Promise.all([
  recordsPromise,
  explanationsPromise,
]);

Complete-or-fail selection export with explicit cancellation:

stream.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

using api = newHttpBatchRpcSession<BrendanPublicApi>(
  new Request('https://irvinebroque.com/api/rpc'),
);

const stream = await api.content.places
  .matching({ country: 'United States' })
  .export({ format: 'geojson' });

try {
  for await (const chunk of stream) {
    consume(chunk);
  }
} finally {
  await stream.cancel();
}

HTTP batch export bodies are limited to 131072 bytes. Use WebSocket for larger complete exports, or pass an explicit limit only when a partial export is intentional.

WebSocket root ownership and complete reconstruction after a broken connection:

websocket.tsTypeScript
import { newWebSocketRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

let api: ReturnType<typeof newWebSocketRpcSession<BrendanPublicApi>> | undefined;

function reconnect() {
  api?.[Symbol.dispose]();
  api = newWebSocketRpcSession<BrendanPublicApi>(
    new WebSocket('wss://irvinebroque.com/api/rpc'),
  );
  api.onRpcBroken(() => {
    // Every derived capability belongs to the old connection. Rebuild the graph.
    api?.[Symbol.dispose]();
    api = undefined;
    setTimeout(reconnect, 1000);
  });
}

reconnect();
  • There is one public Cap’n Web API. The root is the API capability itself; open(), query(), getMeta(), and getStatus() are not part of it.
  • Use typed properties and derived capabilities: api.content.movies.matching(...).orderedBy(...).first(...).
  • Selections are immutable values. Pass CandidateSet capabilities directly into recommendation methods instead of copying candidate IDs through the client.
  • HTTP batch roots are single-use. Resume pages from info().endCursor with pageAfter() on a new root; next() is available only on WebSocket.
  • WebSocket roots are long-lived, but every derived capability must be discarded and rebuilt after onRpcBroken().
  • Dates and URLs arrive as native Date and URL values. Selection exports are finite cancelable streams on both transports; feed streams require WebSocket.
  • An export without limit must contain the complete selection or fail with RESOURCE_LIMIT. Pass an explicit limit only when a bounded partial export is intentional.

MCP (Public Read-Only)

The public MCP endpoint lives at /mcp and uses Model Context Protocol (2025-11-25) over Streamable HTTP. Discovery is available at /.well-known/mcp/server-card.json and/.well-known/mcp.json.

Browser HTML requests to /mcp redirect to the human setup page. Protocol clients continue to send JSON-RPC requests directly to /mcp.

The public MCP lane is unauthenticated and read-only. It complements the REST API rather than replacing it; authenticated owner writes use /owner/mcp on the same Worker.

Available tools:

  • taste.summarize — Complete-corpus public movie and TV counts, distributions, coverage, and deterministic illustrative examples
  • taste.recommend — Ranked recommendations for a stated need; broad or signal-free requests abstain before retrieval
  • taste.profile — Portable version 1 music taste profile derived from canonical D1 records
  • music.intersect — Grounded intersection of a confirmed compatible profile with Brendan’s canonical tracks
  • taste.find — Known-item lookup with explicit public-corpus scope and neutral no-match evidence
  • taste.get — Canonical detail rehydration for bounded item IDs
  • places.plan — Approximate place planning without device-location or live-routing access
  • sports.list — Filter sporting events Brendan attended in person or watched, plus planned experiences, by experience, status, team, sport, league, year, city, or venue
  • taste.render_recommendations — Taste Explorer list for model-selected canonical IDs
  • places.render_map — Taste Explorer real map for model-selected canonical place IDs
  • sports.render_events — Scorecard view for model-selected in-person, watched, and planned sporting events

Migration notes:

  • Code Mode and its Worker Loader binding are removed; task-shaped read tools are the stable public contract.
  • Data tools stand alone in non-App clients. Render tools receive bounded canonical IDs and rehydrate trusted records server-side.
  • Five generated Agent Skills are available through the draft Skills over MCP extension for submission-time import; this extension is experimental, not part of the stable MCP contract.
  • Owner writes are absent from /mcp and appear only on OAuth-protected /owner/mcp with taste:write.
  • Read tools on /owner/mcp retain the same public corpus unless a tool schema explicitly authorizes an owner-only scope.
  • `total`, `resultVersion`, and string `evidence` remain compatibility aliases while schema version 2 clients adopt `counts`, `generatedAt`, `selection`, and `evidenceDetails`.

Initialize request example:

JSON
curl -X POST https://irvinebroque.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-11-25",
      "capabilities": {
        "extensions": {
          "io.modelcontextprotocol/ui": {
            "mimeTypes": ["text/html;profile=mcp-app"]
          }
        }
      },
      "clientInfo": { "name": "curl", "version": "1.0.0" }
    }
  }'

List tools example:

JSON
curl -X POST https://irvinebroque.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list"
  }'

Submission-time Agent Skill import is available through the experimentalio.modelcontextprotocol/skills extension:

JSON
curl -X POST https://irvinebroque.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "skills/list",
    "params": {}
  }'

OpenCode remote MCP config example:

JSON
{
  "mcp": {
    "brendan-website": {
      "type": "remote",
      "url": "https://irvinebroque.com/mcp"
    }
  }
}

Claude Code command:

Shell
claude mcp add --transport http brendan-website https://irvinebroque.com/mcp

In ChatGPT developer mode, connect `https://irvinebroque.com/mcp` for anonymous read tools and Taste Explorer, or `https://irvinebroque.com/owner/mcp` for the owner OAuth lane.

Pagination

Collection endpoints support cursor-based pagination:

  • limit — Items per page (default: 50, max: 100)
  • cursor — Pagination cursor from links.next
  • sort — Sort field and direction (e.g., year:desc)
  • facets — Optional facet metadata in meta.facets when set to 1

Pagination links are provided in both the response body and Link header (RFC 8288).

Endpoints also publish a Link-Template header (RFC 9652) with query templates for pagination and capability-specific filters.

Versioning, rate limits, and retries

REST responses publish API-Version: 1.0.0. Clients may omit the request header or send that value explicitly; unsupported versions receive RFC 9457 Problem Details.

RateLimit-Policy and RateLimit-Limit describe the public Cloudflare Worker abuse brake. An exhausted policy returns 429 withRateLimit and Retry-After.

The public REST contract is read-only. Collection operations are safe and intrinsically idempotent; music intersection uses POST only to carry a bounded structured query and never mutates a record.Idempotency-Key is accepted for clients that attach it uniformly, but is not required and does not create a replay cache for read responses.

Shell
curl -i -H 'API-Version: 1.0.0' -H 'Idempotency-Key: probe-2026-08-16'   'https://irvinebroque.com/api/places.json?limit=1'

Conditional Requests

Use If-None-Match or If-Modified-Since headers to receive 304 Not Modified responses:

Shell
curl -H "If-None-Match: "abc123"" https://irvinebroque.com/api/movies.json

Deprecation and Sunset

Endpoints scheduled for lifecycle changes include Deprecation and optional Sunset headers, with a rel="deprecation" link to migration guidance.

No endpoints are currently scheduled for deprecation.

CORS

Cross-origin requests are enabled for all origins. The following headers are exposed:

  • Link — Pagination and discovery links
  • Link-Template — RFC 9652 parameterized collection query template
  • ETag — Entity tag for conditional requests
  • Last-Modified — Modification timestamp on projections with a reliable source date
  • Content-Usage — IETF AIPREF AI usage preference dictionary
  • Content-Signal — Compatibility AI usage preferences
  • X-Total-Count — Total items in collection

Errors

Errors are returned as RFC 9457 Problem Details with stable type URIs:

JSON
{
  "type": "https://irvinebroque.com/problems/invalid-cursor",
  "title": "Bad Request",
  "status": 400,
  "detail": "The cursor parameter must be a valid opaque cursor value returned by this API."
}

See /problems/ for the full registry.