Independent community project · Not an official PSA website Visit PSA OpenSTAT ↗
Philippine
Statistics Explorer
PUBLIC PREVIEW
FOR DEVELOPERS & AI TOOLS

Public data, ready to connect.

Search Philippine statistics, discover source codes and read exact selections through a REST API or your AI assistant.

No API key required. Both interfaces use the same verified snapshots as the explorer. Availability and freshness come from the saved inventory.

Start with the catalog

The base path is /api/v1. JSON responses put results in data. Search and dimension values include pagination with a total, limit, offset and nextOffset. A null nextOffset means there are no more results.

# Use the origin where this site is running.
export API_ORIGIN=https://statistics.bettergov.ph

curl "$API_ORIGIN/api/v1/datasets?q=palay&ready=true&limit=5"
curl "$API_ORIGIN/api/v1/topics"

Try a palay dataset search ↗ · View current inventory coverage ↗

REST endpoints

All paths below are relative to /api/v1. Dataset IDs come from search results.

Method & pathWhat you get
GET /coverageInventory dates, mirror counts and query limits.
GET /topicsTopic names and dataset counts.
GET /datasetsSearch by q, exact topic, ready=true or false, sort, limit and offset.
GET /datasets/{id}Metadata, provenance, dimension summaries and a sampleQuery.
GET /datasets/{id}/valuesCodes and labels. Requires dimension and release; accepts q, limit and offset.
POST /datasets/{id}/queryExact observations. Send release and selection as JSON. Add ?format=csv for CSV.

Lists default to 20 entries and accept 1–200. Offsets start at zero. Search matches all words against titles, source paths and topics. Sorting accepts recommended, title-asc, title-desc, size-asc, size-desc, year-asc, year-desc, updated-asc and updated-desc.

Make an exact selection

Fetch a dataset first, then send its release and a nonempty array of source codes for every dimension. Labels are for display; they are not query codes. This example selects a single coordinate using the metadata’s sampleQuery.

// Run in this site's browser console, or set origin to your server URL.
const origin = window.location.origin;
async function read(path, options) {
  const response = await fetch(origin + '/api/v1' + path, options);
  const body = await response.json();
  if (!response.ok) throw new Error(body.error.message);
  return body;
}

const matches = await read('/datasets?q=palay&ready=true&limit=1');
if (!matches.data.length) throw new Error('No matching snapshot.');
const { data: dataset } = await read('/datasets/' + matches.data[0].id);

// sampleQuery contains one source code for every dimension and the release.
const result = await read('/datasets/' + dataset.id + '/query', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(dataset.sampleQuery)
});
console.table(result.data.rows);

To change a selection, look up accepted values for a dimension. Use URLSearchParams so dimension codes containing spaces or slashes are encoded correctly. Continue with pagination.nextOffset to read more values.

const dimension = dataset.dimensions[0].code;
const params = new URLSearchParams({
  dimension,
  release: dataset.release,
  q: '',
  limit: '20',
  offset: '0'
});
const values = await read('/datasets/' + dataset.id + '/values?' + params);
console.table(values.data); // Each entry has a code and a label.

Query responses include rows with codes, labels, value and source status in the returned dimension order. The source URL, unit, retrieval date and release accompany the data. CSV exports contain exactly the selected observations plus provenance.

Limits, freshness & errors

  • Up to 5,000 points per selection, 64 matching chunks and 32 MiB of decompressed source data. Even a small selection can span too much source data; narrow it if necessary.
  • Two observation queries can run concurrently per server process. A busy server returns HTTP 503 with Retry-After: 2.
  • JSON request bodies are limited to 64 KiB. Unknown parameters, duplicate codes and invalid dimension values are rejected.
  • Only complete snapshots can be queried. A changed release returns HTTP 409; fetch the metadata again and review your selection before retrying.
  • Missing observations remain null in JSON and empty in CSV. Zero remains zero. Check the official source for statistical definitions, units and status codes.

Errors use {"error":{"code":"invalid_selection","message":"…"}}. HTTP 400 means invalid input, 404 means an unknown dataset or dimension, 405 means an unsupported method, 409 means an unavailable release or incomplete snapshot, 413 means an oversized body, 415 means the wrong content type, and 503 means a busy or unreadable snapshot. REST supports cross-origin requests without credentials.

Classification API

Look up PSA geographic, industry, occupation, consumption, product, commodity, education, crime and tourism classifications. The base path is /api/classification. No API key is required.

Browse the nine classification systems ↗ · Open the API directory ↗ · Snapshot coverage ↗

curl 'https://statistics.bettergov.ph/api/classification/psgc/Q2_2024/regions'
curl 'https://statistics.bettergov.ph/api/classification/psgc/Q2_2024/barangays?reg=1&prv=28&page_size=20'
curl 'https://statistics.bettergov.ph/api/classification/psic/2019/classes?group=011&page_size=20'
GET pathWhat you get
/api/classificationSystem directory and snapshot date.
/api/classification/manifest.jsonAvailable endpoints, record counts, source snapshot identifiers and retrieval dates.
/api/classification/{system}Available version and level endpoints for a classification.
/api/classification/{system}/{version}/{level}Classification records. For example, psgc/Q2_2024/barangays or psic/2019/classes.
/api/classification/ptscs/products
/api/classification/ptscs/industry
Tourism records; these endpoints have no version segment.

Filters and pagination

Classification responses use count, next, previous and results. Follow the relative next link until it is null. Set page (starting at 1) and page_size (1–1000; default 1000). Only JSON is supported.

For PSGC, use reg, prv, mun and bgy. Other systems use their hierarchy fields, such as PSIC section, division, group, class_code and subclasscode. Corresponding record-field aliases, such as groupcode and sectioncode, are accepted. Multiple filters narrow the same set of records. Preserve leading zeros in text codes.

Versions, source differences and errors

These are saved releases, not a live PSA feed. The directory lists the available versions; it does not guarantee the latest release. Source fields, nested population data and historical codes are preserved. The 2021 Manila district and city share a code, so do not assume that a geographic code identifies exactly one record.

PSGC all is unavailable; use individual geographic levels. The mirror applies documented filters that PSA sometimes ignores and consistently paginates tourism records. It accepts an optional legacy token parameter without requiring or returning it.

Classification errors return {"detail":"…"}: HTTP 400 for invalid parameters, 404 for unavailable paths, versions or pages, 405 for unsupported methods, 413 when a page requires too much data (reduce page_size), and 503 for an unavailable snapshot. GET, HEAD, OPTIONS and cross-origin requests are supported. Classification lookup is a REST interface; it is not included in the six MCP observation tools below.

Connect your AI assistant

The MCP endpoint is /mcp, using stateless Streamable HTTP with JSON responses. Add the server URL to a compatible client. A local URL works only for clients that can reach your computer; hosted clients need a reachable deployment.

Server URL: https://statistics.bettergov.ph/mcp

Choose a remote MCP server with Streamable HTTP transport in your client. No API key is needed. For clients that accept a mcpServers configuration:

{
  "mcpServers": {
    "philippine-data": {
      "type": "http",
      "url": "https://statistics.bettergov.ph/mcp"
    }
  }
}

Browser MCP clients must use the same origin, or their origin must be listed in the server’s PSA_MCP_ALLOWED_ORIGINS environment variable. Native clients without an Origin header can connect directly.

Available tools

get_coverage
Read saved inventory coverage, freshness and query limits.
list_topics
List statistical topics with dataset and explorable snapshot counts.
search_datasets
Search PSA datasets by words, exact topic and snapshot availability. Results are paginated; use IDs from these results for other tools.
get_dataset
Get dataset metadata, dimension summaries, provenance and a one-point sampleQuery. Source-only datasets have no sampleQuery. Sample selections may still exceed the source-read budget.
list_dimension_values
Search and paginate accepted codes and labels for one dimension of a pinned release. Pass the exact dimension code, including punctuation. Use returned codes, not labels, in queries.
query_dataset
Read an exact selection from a complete mirrored snapshot. Supply its release and a nonempty code array for every dimension. Maximum 5,000 points, 64 chunks and 32 MiB source data. Null means missing, not zero. Cite source and release; verify units and definitions. No aggregation or live PSA requests.

Try: “Find the palay production dataset, inspect its dimensions, then show one observation. Include the source, unit and snapshot release.”

Read the coverage and methodology before interpreting results. This is an independent community project using PSA OpenSTAT data.