Gov APIdata.stats.gov.cn ↗
Access China NBS economic and social statistics: GDP, money supply, price indices, population, and provincial data via 7 structured endpoints.
What is the Gov API?
This API exposes 7 endpoints for querying official statistical data published by China's National Bureau of Statistics (NBS), covering national and provincial economic indicators, demographic data, and time-series figures. The get_indicator_tree endpoint lets you navigate the full hierarchy of indicator codes before running targeted queries, while get_money_supply surfaces M0, M1, and M2 figures directly with a configurable month limit.
curl -X GET 'https://api.parse.bot/scraper/c388b348-261a-4275-a2a9-53ecd8ac542b/get_indicator_tree?dbcode=hgyd&wdcode=zb' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace data-stats-gov-cn-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
NBS China Data Portal API Client
Get your API key from: https://parse.bot/settings
"""
import os
import json
import requests
from typing import Optional, Any, Dict, List
class ParseClient:
"""Client for the NBS China Data Portal API via Parse.bot"""
def __init__(self, api_key: Optional[str] = None):
self.base_url = "https://api.parse.bot"
self.scraper_id = "ccd066ce-bff4-4b66-ba20-af1a8c09ae1c"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key required. Set PARSE_API_KEY env var or pass api_key parameter.")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""
Make an API call to the Parse.bot scraper.
Args:
endpoint: The API endpoint name
method: HTTP method (GET or POST)
**params: Query/body parameters
Returns:
Parsed JSON response
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method == "GET":
response = requests.get(url, headers=headers, params=params)
elif method == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported method: {method}")
response.raise_for_status()
return response.json()
def get_indicator_tree(
self,
dbcode: str,
wdcode: str = "zb",
parent_id: str = ""
) -> List[Dict[str, Any]]:
"""
Browse the hierarchical tree of indicators, regions, or time periods.
Args:
dbcode: Database code (e.g., 'hgyd' for national monthly, 'fsnd' for provincial annual)
wdcode: Dimension code ('zb' for indicators, 'reg' for regions, 'sj' for time)
parent_id: Parent ID to drill down into (empty for root)
Returns:
List of tree nodes with id, name, isParent, etc.
"""
return self._call(
"get_indicator_tree",
method="GET",
dbcode=dbcode,
wdcode=wdcode,
parent_id=parent_id
)
def query_data(
self,
dbcode: str,
wds: Optional[str] = None,
dfwds: Optional[str] = None,
rowcode: str = "zb",
colcode: str = "sj"
) -> Dict[str, Any]:
"""
Execute a custom data query against the NBS database.
Args:
dbcode: Database code
wds: Fixed dimension filters as JSON string
dfwds: Display filters as JSON string
rowcode: Dimension for rows
colcode: Dimension for columns
Returns:
Query result with datanodes and wdnodes
"""
return self._call(
"query_data",
method="GET",
dbcode=dbcode,
wds=wds,
dfwds=dfwds,
rowcode=rowcode,
colcode=colcode
)
def search_indicators(self, query: str, page: int = 0) -> Dict[str, Any]:
"""
Search for statistical indicators by keyword.
Args:
query: Keyword to search (Chinese or English)
page: Page number (default 0)
Returns:
Search results with pagecount and result array
"""
return self._call(
"search_indicators",
method="GET",
query=query,
page=page
)
def get_provincial_data(
self,
indicator_code: str,
limit: int = 10
) -> Dict[str, Any]:
"""
Retrieve annual data for all 31 Chinese provinces for a specific indicator.
Args:
indicator_code: Indicator code (e.g., 'A020101' for Regional GDP)
limit: Number of recent years to retrieve
Returns:
Provincial data with datanodes and wdnodes
"""
return self._call(
"get_provincial_data",
method="GET",
indicator_code=indicator_code,
limit=limit
)
def get_money_supply(self, limit: int = 13) -> Dict[str, Any]:
"""
Retrieve the most recent monthly Money Supply (M0, M1, M2) data.
Args:
limit: Number of recent months to retrieve
Returns:
Money supply data with datanodes and wdnodes
"""
return self._call(
"get_money_supply",
method="GET",
limit=limit
)
def get_regions(
self,
dbcode: str,
parent_id: str = ""
) -> List[Dict[str, str]]:
"""
Get list of region codes (provinces, cities) for a database.
Args:
dbcode: Database code
parent_id: Parent region ID (empty for root)
Returns:
List of regions with id and name
"""
return self._call(
"get_regions",
method="GET",
dbcode=dbcode,
parent_id=parent_id
)
def get_time_periods(
self,
dbcode: str,
parent_id: str = ""
) -> List[Dict[str, str]]:
"""
Get available time period codes for a database.
Args:
dbcode: Database code
parent_id: Parent time ID (empty for root)
Returns:
List of time periods with id and name
"""
return self._call(
"get_time_periods",
method="GET",
dbcode=dbcode,
parent_id=parent_id
)
def main():
"""Practical workflow example: Search for GDP data and retrieve provincial statistics."""
# Initialize client
client = ParseClient()
print("=" * 70)
print("NBS China Data Portal API - Practical Workflow Example")
print("=" * 70)
# Step 1: Search for GDP indicator
print("\n[Step 1] Searching for 'GDP' indicator...")
search_results = client.search_indicators(query="GDP", page=0)
if search_results.get("result"):
print(f"Found {len(search_results['result'])} results (page count: {search_results.get('pagecount', 'N/A')})")
for i, result in enumerate(search_results["result"][:3], 1):
db_name = result.get("db", "Unknown")
indicator_name = result.get("zb", "Unknown")
print(f" {i}. [{db_name}] {indicator_name}")
# Step 2: Get available regions for provincial analysis
print("\n[Step 2] Fetching available regions for provincial database (fsnd)...")
regions = client.get_regions(dbcode="fsnd")
print(f"Available regions: {len(regions)}")
for region in regions[:5]:
print(f" - {region.get('name')} (ID: {region.get('id')})")
if len(regions) > 5:
print(f" ... and {len(regions) - 5} more regions")
# Step 3: Get time periods available for provincial annual data
print("\n[Step 3] Fetching available time periods for provincial annual data...")
time_periods = client.get_time_periods(dbcode="fsnd")
print(f"Available time periods: {len(time_periods)}")
for period in time_periods[-5:]: # Show last 5 (most recent)
print(f" - {period.get('name')} (ID: {period.get('id')})")
# Step 4: Get provincial GDP data
print("\n[Step 4] Retrieving provincial GDP data (Regional GDP indicator A020101)...")
provincial_data = client.get_provincial_data(
indicator_code="A020101",
limit=3
)
if provincial_data.get("datanodes"):
print(f"Retrieved {len(provincial_data['datanodes'])} data points")
# Parse and display sample data
sample_points = provincial_data["datanodes"][:5]
for point in sample_points:
code = point.get("code", "Unknown")
data_value = point.get("data", {}).get("data")
if data_value:
# Extract region info from code
print(f" Code: {code} | Value: {data_value:,.2f}")
# Step 5: Get money supply data
print("\n[Step 5] Retrieving recent Money Supply (M0, M1, M2) data...")
money_supply = client.get_money_supply(limit=3)
if money_supply.get("datanodes"):
print(f"Retrieved {len(money_supply['datanodes'])} money supply data points")
for point in money_supply["datanodes"][:3]:
code = point.get("code", "Unknown")
data_value = point.get("data", {}).get("data")
if data_value:
print(f" {code}: {data_value:,.2f}")
# Step 6: Explore indicator hierarchy
print("\n[Step 6] Exploring indicator hierarchy (national monthly database)...")
indicators = client.get_indicator_tree(
dbcode="hgyd",
wdcode="zb",
parent_id=""
)
print(f"Top-level indicator categories: {len(indicators)}")
for indicator in indicators[:5]:
is_parent = indicator.get("isParent", False)
parent_marker = " [has children]" if is_parent else ""
print(f" - {indicator.get('name')}{parent_marker} (ID: {indicator.get('id')})")
print("\n" + "=" * 70)
print("Workflow completed successfully!")
print("=" * 70)
if __name__ == "__main__":
main()Browse the hierarchical tree of indicators, regions, or time periods. Use this to discover IDs for querying data.
| Param | Type | Description |
|---|---|---|
| dbcoderequired | string | Database code (e.g., 'hgyd' for national monthly, 'fsnd' for provincial annual) |
| wdcode | string | Dimension code: 'zb' (indicators), 'reg' (regions), 'sj' (time) |
| parent_id | string | Parent ID to drill down into (empty for root) |
{
"type": "array",
"fields": {
"id": "string",
"pid": "string",
"name": "string",
"dbcode": "string",
"wdcode": "string",
"isParent": "boolean"
},
"sample": {
"data": [
{
"id": "A01",
"pid": "",
"name": "价格指数",
"dbcode": "hgyd",
"wdcode": "zb",
"isParent": true
},
{
"id": "A02",
"pid": "",
"name": "工业",
"dbcode": "hgyd",
"wdcode": "zb",
"isParent": true
},
{
"id": "A03",
"pid": "",
"name": "能源",
"dbcode": "hgyd",
"wdcode": "zb",
"isParent": true
}
],
"status": "success"
}
}About the Gov API
What the API Covers
The API wraps the NBS public data platform at data.stats.gov.cn, giving structured access to China's official macroeconomic and social statistics. Indicators span GDP, money supply (M0/M1/M2), price indices, population figures, and education statistics. Data is organized across multiple databases — for example, hgyd for national monthly data and fsnd for provincial annual data — and the API exposes both national-level and all-31-province breakdowns.
Navigating Indicators and Regions
The get_indicator_tree endpoint accepts a dbcode, an optional wdcode dimension (zb for indicators, reg for regions, sj for time), and an optional parent_id to drill into sub-nodes. Each node in the response includes id, pid, name, dbcode, wdcode, and isParent, making it straightforward to traverse from root categories down to specific leaf indicator codes. Pair this with get_regions (returns id and name for provinces or cities) and get_time_periods (available period codes for a given database) to assemble the exact filter parameters needed for a data query.
Querying Data
The query_data endpoint accepts wds (fixed dimension filters as a JSON list, e.g. locking an indicator code) and dfwds (display filters, e.g. LAST10 for the ten most recent periods), along with rowcode and colcode to control result orientation. Responses contain wdnodes (dimension metadata) and datanodes (the actual values). The get_provincial_data endpoint simplifies a common pattern: supply an indicator_code like A020101 for Regional GDP and an optional limit for recent years, and it returns region-by-year data for all provinces in the same wdnodes/datanodes structure. search_indicators supports keyword lookup in Chinese or English and returns paginated results with a pagecount field.
Indicator Discovery
Because the NBS data catalog uses opaque numeric and alphanumeric codes, the discovery flow matters: use search_indicators to find candidate indicator codes by keyword, confirm the right dbcode via get_indicator_tree, then pass those codes into query_data or get_provincial_data. The get_time_periods endpoint is useful when you need to confirm which periods are actually populated for a given database before constructing a time-series query.
The Gov API is a managed, monitored endpoint for data.stats.gov.cn — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when data.stats.gov.cn changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official data.stats.gov.cn API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Track quarterly GDP growth trends by querying national and provincial indicator codes over multi-year time series.
- Monitor M0, M1, and M2 money supply changes month-over-month using
get_money_supplywith a rolling window. - Build cross-province economic comparison dashboards using
get_provincial_datafor indicators like Regional GDP. - Discover and map the full NBS indicator hierarchy programmatically with
get_indicator_treefor automated catalog indexing. - Search NBS datasets by keyword (e.g. '居民消费价格' or 'CPI') via
search_indicatorsto identify relevant indicator codes. - Retrieve province and city-level region codes via
get_regionsto filter multi-dimensional queries by geography. - Automate ingestion of official Chinese economic releases into data pipelines using structured
datanodesresponses.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does data.stats.gov.cn have an official developer API?+
What does `query_data` return and how do I filter it?+
wdnodes, which contains dimension metadata describing the axes of the result, and datanodes, which holds the actual statistical values. You filter using wds (fixed dimensions, e.g. locking a specific indicator code) and dfwds (display filters, e.g. LAST10 to retrieve the ten most recent periods). Set rowcode to zb for indicator rows or reg for region rows, and colcode to sj for time columns.Does the API cover city-level or county-level data below the provincial level?+
get_regions with a parent_id parameter. Sub-provincial city or county breakdowns are not currently returned by these endpoints. You can fork the API on Parse and revise it to add queries targeting finer administrative divisions if the underlying NBS database supports them for a given indicator.How current is the data, and how often is it updated?+
Can I retrieve historical data beyond what `LAST10` covers, or get full time-series going back decades?+
dfwds filter supports range and recency codes including LAST10 for the ten most recent periods. Older historical depth depends on what each specific dbcode database holds; not all indicators have decades of data available. For indicators with limited depth in the current databases, you can fork the API on Parse and revise it to target additional NBS database codes or alternative historical tables.