Claude Code
claude mcp add --transport http buffalo-jobs https://buffaloprojects.com/api/jobs/mcp
claude mcp add --transport http buffalo-events https://buffaloprojects.com/api/events/mcpDevelopers
Current openings and events across Buffalo and Western New York, from a JSON API, MCP servers for AI assistants, and open CSV files. Free, and no account or API key is needed.
What you can use
GET /api/jobs
Current Buffalo and Western New York openings as JSON, with filters and cursor pagination.
/api/jobs/mcp
Let Claude, ChatGPT, or your own agent search openings with search_jobs, get_job, and list_job_sources.
/api/events/mcp
Upcoming events from their organizers with search_events, get_event, and list_event_sources.
/mcp
One server with the jobs and events tools plus Organizations, Spaces, and public cohort summaries.
/data/jobs
CSV files under CC BY 4.0: today's jobs, and every posting since August 10, 2026 with listed and removed dates.
GET /api/organizations/{idOrSlug}
One Organization page as JSON, with the published jobs and events credited to it.
/reports
Monthly counts of Western New York job postings, each with a CSV download.
Quickstart
Ten current nursing openings that state their pay:
curl 'https://buffaloprojects.com/api/jobs?q=nurse&payDisclosed=true&limit=10'When pagination.hasMore is true, pass pagination.nextCursor to get the next page:
curl 'https://buffaloprojects.com/api/jobs?q=nurse&payDisclosed=true&limit=10&cursor=NEXT_CURSOR'MCP
The MCP servers are read-only and use Streamable HTTP. There is no sign-in: leave authentication off when your client asks.
claude mcp add --transport http buffalo-jobs https://buffaloprojects.com/api/jobs/mcp
claude mcp add --transport http buffalo-events https://buffaloprojects.com/api/events/mcpIn Claude, open Settings, then Connectors, and choose Add custom connector. Name it Buffalo Jobs and paste the Jobs URL above. Do the same for Events if you want both.
Open ChatGPT Plugins, select +, then Add custom MCP server, and paste https://buffaloprojects.com/mcp. It carries the jobs and events tools together. Step-by-step ChatGPT setup
Add a remote server with the Streamable HTTP URL. In a JSON configuration it looks like this:
{
"mcpServers": {
"buffalo-jobs": {
"type": "http",
"url": "https://buffaloprojects.com/api/jobs/mcp"
}
}
}search_jobs: describe what you want in plain language in query, or use explicit filters. The result's interpreted field shows how the query was read.get_job: one opening by the ID from a search.list_job_sources: each source, its health, when it was checked, and how many postings it has.search_events, get_event, and list_event_sources do the same for events.https://buffaloprojects.com/mcp adds Organization, Space, and public cohort tools such as network_find_organization.To test without a client, call a tool directly:
curl https://buffaloprojects.com/api/jobs/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"search_jobs","arguments":{"query":"nurse","limit":2}}}'Send search criteria only. The servers don't accept resumes, names, or contact details, and they never apply, register, or contact anyone.
Jobs API
Returns active openings, newest first. Every parameter is optional. An invalid value returns HTTP 400 with an error message that lists the allowed values.
| Parameter | Values |
|---|---|
| q | Words matched against title, employer, and location. Up to 120 characters. Aliases: query, search. |
| kind | job, internship, apprenticeship, civil_service_job, civil_service_exam, training, contract, temporary, unknown. Alias: opportunityKind. |
| sector | education, healthcare, public_service, technology, manufacturing, construction_trades, and the other values in the job schema. |
| workMode | onsite, hybrid, remote, unknown. |
| seniority | intern, entry, mid, senior, lead, executive. |
| payDisclosed | true returns only openings whose posting states pay. |
| minPay | Lowest stated pay, compared in the posting's own period. Hourly and yearly figures are not converted, so read facts.pay.value.period. |
| noDegreeRequired | true returns openings that say a degree is not required. |
| occupation | O*NET-SOC code or prefix, such as "15-1252" or "29". |
| skill | One skill tag, up to 40 characters. |
| techOnly | true returns technology roles only. |
| sourceTier | canonical-employer, official-public, official-program, staffing-agency, local-board, secondary, unknown. |
| newSince | ISO date or time. Only openings first seen after it. Pass the previous response's coverage.checkedAt to get what's new. |
| sort | newest (default) or builder-fit. |
| limit | 1 to 50. Default 20. Larger values return 50. |
| cursor | pagination.nextCursor from the previous page. |
A real response from October 26, 2026, trimmed to one job and the main coverage fields. jobs, pagination, and coverage are always present.
{
"jobs": [
{
"id": "roswell-park-comprehensive-cancer-center-…:r-21607",
"externalId": "R-21607",
"title": "Distribution Clerk",
"employer": "Roswell Park Comprehensive Cancer Center",
"opportunityKind": "job",
"sector": "healthcare",
"workMode": "unknown",
"employmentType": "full_time",
"sourceTier": "official-public",
"freshness": "current",
"checkedAt": "2026-10-26T11:17:19.720Z",
"firstSeenAt": "2026-10-26T11:17:19.720Z",
"postedAt": "2026-10-26",
"deadline": null,
"applicationUrl": "https://roswellpark.wd5.myworkdayjobs.com/ExternalCareers/job/Hospital/Distribution-Clerk_R-21607",
"location": {
"display": "Buffalo, NY",
"city": "Buffalo",
"region": "NY",
"country": null,
"workMode": "unknown"
},
"compensation": {
"original": null,
"min": null,
"max": null,
"period": "unknown",
"provenance": "unknown"
},
"facts": {
"pay": {
"value": { "min": 20.43, "max": null, "period": "hour", "currency": "USD" },
"quote": "The starting hourly for this position is $20.43 and this can include a full comprehensive benefits package.",
"origin": "description"
},
"experience": {
"value": { "minYears": 1, "maxYears": null },
"quote": "… at least **one (1) year of continuous experience** in shipping, receiving, and inventory control …",
"origin": "description"
},
"workMode": null,
"deadline": null,
"skills": []
},
"attributes": {
"workMode": null,
"degree": { "value": "not_required", "basis": "classified", "confidence": 1 },
"seniority": null,
"builderFit": 0.19
},
"source": {
"organization": "Roswell Park Comprehensive Cancer Center",
"sourceUrl": "https://roswellpark.wd5.myworkdayjobs.com/ExternalCareers",
"sourceTier": "official-public",
"verifiedAt": "2026-08-10"
}
}
],
"pagination": {
"limit": 2,
"cursor": null,
"nextCursor": "eyJmaXQiOjAuMTks…",
"hasMore": true,
"totalMatching": 2262
},
"coverage": {
"checkedAt": "2026-10-26T11:17:19.720Z",
"sourceCount": 32,
"healthySourceCount": 32,
"staleSourceCount": 0,
"evidenceStatus": "current",
"activePostingCount": 2477,
"matchingPostingCount": 2262,
"returnedPostingCount": 2
}
}In this example the employer's hiring system has no pay field, so compensation is empty, while facts.pay holds the pay stated in the description with the sentence it came from. Read pay from facts.pay.
Corrections
If a listing is closed, shows the wrong pay, place, or date, or isn't a real job, tell us. On any job page, choose Something wrong?. No account is needed. Reports are stored with a keyed hash for abuse control, never your IP address.
POST https://buffaloprojects.com/api/jobs/reports
Content-Type: application/json
{
"jobId": "the slug from the job page, /jobs/{slug}",
"reason": "wrong-pay",
"note": "The posting now says $22 an hour."
}reason is one of closed, wrong-pay, wrong-location, wrong-date, not-real, other. note is optional, up to 500 characters. The endpoint accepts requests from buffaloprojects.com pages and allows 20 reports an hour per person.
Organizations
The JSON form of an Organization page: its identity and the published jobs and events credited to it. Use the slug from the page URL, as in /api/organizations/43north. An Organization page is compiled from public sources. It does not mean the organization works with Buffalo Projects.
Data quality and limits
coverage.sourceCount says how many sources today's data came from. Which jobs we cover lists the rules and every change to them.facts.pay is set only when the posting or hiring system states a figure, and it quotes the text. Filter with payDisclosed=true to keep only those openings.unknown or null. A null fact means the posting doesn't say, never no. In attributes, a basis of posting means the posting said it; classified means a model read it from the text and passed a confidence check, with that confidence.checkedAt is when we last confirmed the opening at its source. firstSeenAt is when it first appeared in our data, which is not the employer's posting date (postedAt, when the source gives one). An opening leaves the data when the employer takes it down. Things can change between checks, so send people to applicationUrl and treat the employer as the authority./api/jobs returns at most 50 jobs a page and is cached for up to a minute; follow cursors for more. The MCP servers are rate-limited per IP address and answer HTTP 429 when you go over. The data changes once a day, so polling faster gains nothing.Existing accounts
Both adapters use the same Buffalo Projects API token for hosted owner operations. Local inspection can remain local; authenticated reads and writes stay within the existing builder account contract. None of the public data above needs them.
CLI
npm install --global buffalo-projects
buffalo --help
buffalo authThe installed binary owns the current command reference. Run buffalo --help instead of relying on a copied command catalog.
Account MCP
{
"mcpServers": {
"buffalo": {
"command": "npx",
"args": ["-y", "buffalo-projects-mcp@latest"],
"env": {
"BUFFALO_TOKEN": "bp_your_api_key"
}
}
}
}A connected MCP client receives the current tools, resources, prompts, and their write annotations directly from the running server. The protocol discovery result is the reference.
Contract
These adapters are ways to use the inherited Projects boundary. They do not create a separate developer product, transfer Project ownership, or turn private work into public data.
Hosted private records require the builder's token. A public record can be read only when its owner deliberately shared it.
Hosted changes require authenticated owner authority. Agents must still stop for explicit approval before recording or publishing consequential work.
Project records, work entries, portable Project Markdown, and the approved connections already exposed by each adapter.
Public projection
An inherited public builder record may expose an alternate JSON representation. Private Projects, raw work events, credentials, and account identifiers are not part of that projection.
GET /b/{handle}/json
Accept: application/json