BetterGov.ph | Budget Data API NEP 2027 GAA 2020–2026 API docs OpenAPI MCP

Philippine Budget Data API

A free, public, read-only API over Philippine national budget data — the enacted General Appropriations Act (FY2020–2026), the FY2027 National Expenditure Program, budget-cycle execution data, and the House budget hearings (what was actually said, topic by topic, with timestamps). Also available as an MCP server for AI agents.

Base URL https://budget.bettergov.ph/api/v1 No auth required CORS: open (*) All amounts in exact ₱ pesos
curl https://budget.bettergov.ph/api/v1/gaa/departments
curl "https://budget.bettergov.ph/api/v1/gaa/search?q=school%20building"
curl https://budget.bettergov.ph/api/v1/nep/2027

The API is free for public use under fair-use conditions. Responses are cached at the edge (Cache-Control: public, max-age=300, s-maxage=3600); data updates at most a few times a year, so aggressive client-side caching is encouraged. Please identify heavy automated use with a descriptive User-Agent.

Datasets & units

DatasetCoverageWhat it is
gaaFY2020–2026, 38 departments The enacted General Appropriations Act, down to UACS object-level line items (~2M rows). Hierarchy: department → agency → program (P/A/P) → operating unit → fund → expense class → object.
nep/2027FY2027, 39 departments The Executive's proposed ₱7.20 T National Expenditure Program. Every figure carries amount (FY2027 proposal), base_amount (FY2026 GAA baseline), delta, and pct.
budget-cycleFY2018–2026, selected departments How appropriations moved through the cycle: NEP → GAA → authorized/adjusted appropriation → adjusted allotment → obligations → disbursements, per program × year × stage × expense class.
hearingsFY2027 season, House Committee on Appropriations The budget deliberations: for each hearing (2–9 hours of video), a per-topic record of what transpired — the question at issue, who said what and when, the agency's answer, figures as spoken, commitments, and where it landed — plus a section-by-section timeline and the transcript. Every item is timestamped and deep-linked to the video.

Units. Every amount anywhere in this API is an exact Philippine peso figure. Each response's meta restates this ("currency": "PHP", "scale": "pesos"). The upstream source publishes GAA figures in thousands; this API rescales them so you never have to.

Department ids. GAA ids are two digits (07 = Department of Education, 18 = Public Works). The NEP FY2027 dataset uses the same two-digit ids plus two synthetic departments carved out for correctness: SPF (special purpose funds) and AUTO (automatic appropriations).

Caveats worth knowing. (1) AUTO is a synthetic NEP department — it is not GAA department 04. (2) Budget-cycle figures cover Current New Appropriations only, a narrower scope than the main GAA series — never compare the two 1:1. (3) GAA and NEP are different stages of the budget process: NEP is the proposal, GAA is the law. (4) In NEP rollups, __unassigned__ buckets rows the source didn't tag with that dimension, and __other__ is an explicit remainder row on capped lists — both exist so lists always sum to the true total.

Conventions

Every endpoint is GET and returns JSON in a common envelope:

{
  "meta": { "dataset": "gaa", "currency": "PHP", "scale": "pesos", ... },
  "data": [ ... ] | { ... },
  "next_cursor": "..." | null        // paginated lists only
}

GAA rows carry per-year figures as a nested map — every year key is always present (zeroed if the source has no row for that year):

"years": { "2020": { "count": 12, "amount": 4491000 }, ..., "2026": { "count": 15, "amount": 5120000 } }

Filtered list endpoints also report whole-result-set totals in meta.matched / meta.matched_amount, so a single request answers "how many and how much in total?" without fetching every page.

Pagination

Large lists use opaque keyset cursors. Take next_cursor from a response and pass it back as cursor; when it is null, the list is exhausted. Cursors are tied to the exact filter combination that produced them — changing any filter invalidates the cursor (you'll get 400 bad_cursor or wrong pages). There is no page-number or offset pagination; cursor performance does not degrade with depth.

Errors

Errors are JSON with a machine-readable slug and a human-readable message:

{ "error": "not_found", "message": "No GAA department 99" }
StatusSlugMeaning
400bad_requestInvalid parameter (year out of range, malformed id, …)
400bad_cursorThe cursor was not issued by this API (or filters changed)
404not_foundUnknown department / endpoint
405method_not_allowedThe API is read-only — use GET
503not_loadedThe dataset is not loaded on this deployment
500query_failedUnexpected database error

GAA — General Appropriations Act, FY2020–2026

GET/api/v1/gaa

National totals per fiscal year, summed across all departments.

curl https://budget.bettergov.ph/api/v1/gaa
{
  "meta": { "dataset": "gaa", "years": [2020, ..., 2026], "currency": "PHP", "scale": "pesos", "departments": 38 },
  "data": [
    { "year": 2020, "line_items": 725178, "amount": 4100000000000 },
    ...
    { "year": 2026, "line_items": 810081, "amount": 6326000000000 }
  ]
}
GET/api/v1/gaa/departments

All 38 departments with per-year appropriations and line-item counts, largest FY2026 budget first. This is where you discover valid {id} values.

curl https://budget.bettergov.ph/api/v1/gaa/departments
GET/api/v1/gaa/departments/{id}

One department in full: yearly totals, its agencies/bureaus (whose ids work as agency_id filters elsewhere), and the expense-class breakdown.

curl https://budget.bettergov.ph/api/v1/gaa/departments/07
GET/api/v1/gaa/departments/{id}/expense-classes

Personnel Services / MOOE / Financial Expenses / Capital Outlays totals per year.

CodeClass
1Personnel Services (PS)
2Maintenance and Other Operating Expenses (MOOE)
3Financial Expenses (FinEx)
6Capital Outlays (CO)
GET/api/v1/gaa/departments/{id}/programs

Program/activity/project (P/A/P) families — raw P/A/P rows deduplicated by normalized name within an agency — ranked by appropriation in the chosen year. Paginated.

ParamTypeDescription
year2020–2026Ranking year (default 2026)
qstringSubstring filter on program name
agency_idstringScope to one agency/bureau
limit1–500Page size (default 100)
cursorstringFrom a previous next_cursor
curl "https://budget.bettergov.ph/api/v1/gaa/departments/07/programs?year=2026&q=computerization&limit=5"
GET/api/v1/gaa/departments/{id}/objects

UACS object-level line items — the finest granularity in the GAA (DepEd alone has about a million rows). Filter tightly and paginate. Rows with a zero amount in the chosen year are hidden unless include_zero=1.

ParamTypeDescription
year2020–2026Filter/ranking year (default 2026)
qstringSubstring filter on description or UACS code
agency_idstringScope to one agency/bureau
expense_class1, 2, 3, 61=PS, 2=MOOE, 3=FinEx, 6=CO
sortamount | description | code | totaltotal ranks by the 7-year sum (implies include_zero)
dirasc | descDefaults: desc for amounts, asc otherwise
limit, cursorPage size 1–500 (default 100); keyset cursor
curl "https://budget.bettergov.ph/api/v1/gaa/departments/07/objects?year=2026&q=textbooks&limit=10"

Note that free-text search here matches the object-level description (the UACS expense object, e.g. "Textbooks and Instructional Materials"), not program names — search programs with /gaa/search instead.

Search program names across all departments at once — the quickest route from a topic ("school building", "flood control", "irrigation") to who funds it and with how much.

ParamTypeDescription
qstring, requiredSearch text (min 2 chars)
department_idstringOptional two-digit scope
year2020–2026Ranking year (default 2026)
limit1–100Max results (default 25)
curl "https://budget.bettergov.ph/api/v1/gaa/search?q=flood%20control&year=2026&limit=10"
GET/api/v1/gaa/years/{year}

One fiscal year in isolation: the national total plus every department's appropriation, line-item count, and share of that year's budget, largest first. The per-year counterpart of /gaa/departments, which returns all seven years per row. This is the API face of the per-year budget browser.

curl https://budget.bettergov.ph/api/v1/gaa/years/2022
{
  "meta": { "dataset": "gaa", "year": 2022, "departments": 37, ... },
  "data": {
    "total": { "year": 2022, "line_items": 628666, "amount": 5023600000000 },
    "departments": [
      { "id": "07", "slug": "department-of-education-deped", "description": "Department of Education (DepEd)",
        "year": 2022, "line_items": 308586, "amount": 633323678000, "share": 0.12607 },
      ...
    ]
  }
}
GET/api/v1/gaa/years/{year}/departments/{id}/children

Walk one department's hierarchy a level at a time, scoped to a single fiscal year — the same drill the per-year browser does: department → agency (bureau) → program (FPAP) → operating unit → fund → expense class. Rows carry that year's figures only, ranked largest first, and keep their parent-id columns so each row's id feeds the next level's parent. Rows with a zero amount in that year are hidden unless include_zero=1.

ParamTypeDescription
levelagencies | fpaps | operating_units | fund_subcategories | expensesLevel to list (default agencies)
parentstringParent entity id — required below agencies
include_zero1Keep rows the year doesn't fund
limit, cursorPage size 1–500 (default 100); keyset cursor
# DepEd's bureaus in FY2022
curl "https://budget.bettergov.ph/api/v1/gaa/years/2022/departments/07/children"

# then programs of one bureau...
curl "https://budget.bettergov.ph/api/v1/gaa/years/2022/departments/07/children?level=fpaps&parent=07-001"

# ...down to the expense classes of one fund
curl "https://budget.bettergov.ph/api/v1/gaa/years/2022/departments/07/children?level=expenses&parent=07-001-310400100002000-0807002-01101101"

NEP FY2027 — National Expenditure Program

The Executive's ₱7.20 T proposal for FY2027, measured line by line against the FY2026 GAA. Every row carries amount, base_amount, delta, and pct (null when the FY2026 baseline is zero, i.e. a new item).

GET/api/v1/nep/2027

National overview: every department with its delta, expense classes, regions, top funds and programs, and the biggest movers up/down.

curl https://budget.bettergov.ph/api/v1/nep/2027
GET/api/v1/nep/2027/departments

All 39 departments (including synthetic SPF and AUTO) with amounts, baselines, and per-dimension counts.

GET/api/v1/nep/2027/departments/{id}

Full department view: agencies, programs, expense classes, funds, and regions in full; objects, operating units, and divisions capped at the top 50 with an explicit __other__ remainder row so lists still sum to the department total.

curl https://budget.bettergov.ph/api/v1/nep/2027/departments/07
curl https://budget.bettergov.ph/api/v1/nep/2027/departments/SPF
GET/api/v1/nep/2027/departments/{id}/rollups/{dimension}

The complete, untruncated list for one dimension within one department, paginated. Dimensions: agency, program, expense_class, fund, region, object, operating_unit, division.

curl "https://budget.bettergov.ph/api/v1/nep/2027/departments/07/rollups/operating_unit?limit=500"
GET/api/v1/nep/2027/rollups/{dimension}

National rollups. Two modes:

curl https://budget.bettergov.ph/api/v1/nep/2027/rollups/expense_class
curl "https://budget.bettergov.ph/api/v1/nep/2027/rollups/region?code=13&by=department"

Budget cycle — NEP → GAA → execution

GET/api/v1/budget-cycle

Dataset manifest and coverage: which departments are included, fiscal years (2018–2026), stages, and expense classes. Scope is Current New Appropriations only — narrower than the main GAA series; do not compare the two directly.

curl https://budget.bettergov.ph/api/v1/budget-cycle
GET/api/v1/budget-cycle/departments/{id}

For one covered department: its programs (with the source→portal crosswalk and match confidence) and facts — one row per program × fiscal year × stage × expense class.

FieldValues
stagenep, gaa, authorized_appropriation, adjusted_appropriation, adjusted_allotment, obligations, disbursements
expense_classps, mooe, finex, co, total
curl https://budget.bettergov.ph/api/v1/budget-cycle/departments/14

Budget hearings — what was said, topic by topic

The House Committee on Appropriations deliberates each agency's proposed budget in hearings that run 2–9 hours. We transcribe the streams (speaker-labelled) and build a deliberation record: not a summary of the hearing, but an account of what transpired on each topic. Machine transcripts are imperfect — names and figures are as heard — so every item carries a timestamp and a url that opens the hearing page with the video at that moment. Treat the records as an index into the video, and verify before you quote.

GET/api/v1/hearings

All hearings, newest first. Filters: fiscal_year, agency (acronym as in the title, e.g. DOH), q (title keyword), limit, offset. has_record says whether the topic record exists; transcript_source says how the transcript was made.

curl "https://budget.bettergov.ph/api/v1/hearings?agency=DOH"

Keyword search over every topic record across all hearings — the topic, its summary, question, outcome, and every spoken moment. All terms must match. Each hit returns the topic and the specific matching_moments (timestamp, speaker, what was said, url). Optional fiscal_year, agency, status (resolved | committed | parked | unresolved | informational).

curl "https://budget.bettergov.ph/api/v1/hearings/search?q=philhealth%20subsidy"
curl "https://budget.bettergov.ph/api/v1/hearings/search?q=flood%20control&status=committed"
GET/api/v1/hearings/{video_id}

One hearing's metadata and its topic index (topic, status, first timestamp, summary, outcome).

GET/api/v1/hearings/{video_id}/topics  ·  /topics/{index}

The full deliberation record. For each topic: question (what the committee was trying to establish), thread (chronological moments: timestamp, speaker, side, kind, said, url), agency_position, positions, figures (as spoken; amount is a best-effort peso parse), actions (document requests, commitments, motions, rulings), outcome and status. ?thread=0 omits the moment-by-moment thread.

curl https://budget.bettergov.ph/api/v1/hearings/7U4YvhlnbZY/topics
curl "https://budget.bettergov.ph/api/v1/hearings/7U4YvhlnbZY/topics?thread=0"
GET/api/v1/hearings/{video_id}/timeline

The proceedings in order — roll call, presentation, each interpellation, motions, suspensions — with per-section summaries, exchanges, figures and actions.

GET/api/v1/hearings/{video_id}/transcript?from=&to=

Raw transcript segments for a time window (seconds; default 20 minutes from from, at most 1000 segments), with the inferred speaker name when known.

curl "https://budget.bettergov.ph/api/v1/hearings/7U4YvhlnbZY/transcript?from=3600&to=3900"

OpenAPI specification

A machine-readable OpenAPI 3.1 description of every endpoint, parameter, and schema:

https://budget.bettergov.ph/api/v1/openapi.json

Import it into Postman, Insomnia, or any OpenAPI-aware code generator.

MCP server

The same data is exposed as an MCP server for AI agents — Claude, Cursor, and any other MCP-capable client. The transport is stateless Streamable HTTP: no auth, no sessions, JSON-RPC over POST.

Endpoint https://budget.bettergov.ph/mcpTransport: Streamable HTTPRead-only tools

Install in your AI client

Claude Code (CLI)

claude mcp add --transport http ph-budget https://budget.bettergov.ph/mcp

Then just ask: "Using ph-budget, which departments gain the most in the FY2027 proposal?"

Claude Desktop & claude.ai

Settings → Connectors → Add custom connector → paste https://budget.bettergov.ph/mcp. No authentication is needed; the server is read-only.

Cursor

Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json in a project:

{
  "mcpServers": {
    "ph-budget": { "url": "https://budget.bettergov.ph/mcp" }
  }
}

Windsurf

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "ph-budget": { "serverUrl": "https://budget.bettergov.ph/mcp" }
  }
}

VS Code (Copilot Chat)

Run MCP: Add Server… from the command palette (choose HTTP), or add to .vscode/mcp.json:

{
  "servers": {
    "ph-budget": { "type": "http", "url": "https://budget.bettergov.ph/mcp" }
  }
}

Gemini CLI

Add to ~/.gemini/settings.json:

{
  "mcpServers": {
    "ph-budget": { "httpUrl": "https://budget.bettergov.ph/mcp" }
  }
}

Anything else

Any client that speaks MCP over Streamable HTTP works: point it at https://budget.bettergov.ph/mcp, no auth, no session setup. Most clients accept the generic {"mcpServers": {"ph-budget": {"url": "https://budget.bettergov.ph/mcp"}}} shape.

Tools

ToolWhat it answers
list_gaa_departmentsWhich departments exist, with per-year budgets (start here)
get_gaa_national_trendsNational GAA totals per year, 2020–2026
get_gaa_departmentOne department: yearly totals, agencies, expense classes
search_gaa_programsFind programs by name across all departments
get_gaa_department_programsAll programs of one department, paginated
search_gaa_line_itemsObject-level line items with text/agency/expense-class filters
get_nep_2027_overviewThe FY2027 proposal at national level, vs FY2026
list_nep_2027_departmentsAll 39 NEP departments with deltas
get_nep_2027_departmentOne department's FY2027 proposal in full
get_nep_2027_rollupSlice FY2027 by agency/program/region/object/… nationally, per department, or cross-department
get_budget_cycleNEP→GAA→execution stages for covered departments

Example prompts

Once connected, ask your assistant things like:

How an agent typically chains the tools

A question like "how much of DepEd's 2026 budget is textbooks?" resolves in three calls:

  1. list_gaa_departments → find DepEd's id (07)
  2. get_gaa_department {"department_id": "07"} → totals and agencies for context
  3. search_gaa_line_items {"department_id": "07", "query": "textbooks", "year": 2026} → the matched line items, plus meta.matched_amount answering the question in one number

National "who spends on X?" questions go straight to search_gaa_programs (GAA) or get_nep_2027_rollup with by_department: true (FY2027 proposal).

Raw protocol examples (curl)

The transport is stateless JSON-RPC over POST — you can drive it with curl. Handshake:

curl -X POST https://budget.bettergov.ph/mcp -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": { "protocolVersion": "2025-06-18", "capabilities": {},
              "clientInfo": { "name": "curl", "version": "1.0" } }
}'

curl -X POST https://budget.bettergov.ph/mcp -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Search flood-control programs across all departments:

curl -X POST https://budget.bettergov.ph/mcp -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": { "name": "search_gaa_programs",
              "arguments": { "query": "flood control", "limit": 3 } }
}'

Break FY2027 spending in NCR (region code 13) down by department:

curl -X POST https://budget.bettergov.ph/mcp -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0", "id": 4, "method": "tools/call",
  "params": { "name": "get_nep_2027_rollup",
              "arguments": { "dimension": "region", "code": "13", "by_department": true, "limit": 5 } }
}'

Tool results arrive as JSON text inside result.content[0].text, in the same envelopes the REST API returns. Invalid inputs come back as a result with isError: true and a message explaining what to fix.

Data sources & attribution

Source data is derived from Department of Budget and Management publications, packaged as the BetterGov.PH GAA dataset. Figures are presented as published; always verify against official DBM documents before citing in formal work. If this API is useful to you, attribution to BetterGov.PH is appreciated.