API Reference
REST interface of the Asia-Pacific Statistical Analysis System. Every figure comes from official public sources (World Bank Open Data · China NBS · China Customs); identifiers are localized server-side per request.
Conventions
The lang parameter
?lang=en|zh wins; otherwise Accept-Language is read, then en (the product default). The page's <html lang> and the share card derive from the same value.
Three keys per identifier
The data tables store canonical Chinese keys; localization happens in exactly one place, server-side. Every record exposes field (display value in the requested language), field_key (canonical key) and field_slug (stable ASCII key). Request parameters accept any of the three, so English consumers need no Chinese glossary.
Unknown terms pass through
Terms missing from the label pack are returned verbatim rather than raising — new data stays forward-compatible. Likewise an unresolvable dimension such as ?dimension=Atlantis is passed through and simply matches nothing; it is never silently swapped for a default.
Errors
Validation failures return {"ok": false, "error": "…"} with 400; 403 for auth, 404 when missing, 502 when an upstream source fails. Error text is localized per lang.
Security headers
Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin, a restrictive Permissions-Policy and a CSP locked to 'self'; HSTS is added over HTTPS. The project has zero external CDN dependencies (fonts use the system stack).
Sources & license
World Bank Open Data (CC BY 4.0), plus public releases from China NBS and China Customs. This project uses only public data — no internal or restricted material.
Endpoints
Pages & static assets
GET/Public
Landing page
Product overview, capabilities and data-source statement. The headline counters are filled at runtime from /api/stats.
| Parameter | Type | Required | Description |
|---|
| lang | en | zh | no | Controls <html lang> and the share card. Falls back to Accept-Language, then en. |
Example
curl -s "$BASE/?lang=en"
GET/appPublic
Data dashboard
Dashboard: AI query, indicator table, custom analysis, statistical bulletin and charts.
| Parameter | Type | Required | Description |
|---|
| lang | en | zh | no | Source of the en placeholder. |
Example
curl -s "$BASE/app?lang=zh"
GET/docsPublic
This page
The API reference. Rendered server-side per ?lang=, no JavaScript required.
| Parameter | Type | Required | Description |
|---|
| lang | en | zh | no | Documentation language. |
Example
curl -s "$BASE/docs?lang=en" | less
GET/privacyPublic
Privacy & data statement
The standing answer to the three questions asked most often after going public: what is collected, where the data comes from, and whether the AI sends your question anywhere. Also server-rendered, zero JS.
| Parameter | Type | Required | Description |
|---|
| lang | en | zh | no | Statement language. |
Example
curl -s "$BASE/privacy?lang=en"
GET/theme.css · /style.css · /landing.css · /app.js · /app.charts.js · /app.palette.js · /app.indicators.js · /app.custom.js · /app.bulletin.js · /i18n.js · /i18n-dict.js · /i18n-dict-landing.jsPublic
Frontend assets
Twelve frontend assets: theme tokens / dashboard styles / landing styles / dashboard logic / charts chunk / command-palette chunk / indicator-catalog chunk / custom-analysis chunk / bulletin chunk / bilingual runtime / full dictionary / landing dictionary. Each asset carries its own content-hash version, ?v=<sha256[:12]>, which makes the long cache safe: changing one asset only invalidates that one URL. /app.charts.js, /app.palette.js, /app.indicators.js, /app.custom.js and /app.bulletin.js are lazily loaded chunks: the core (/app.js) fetches them on first use, so they are not part of the first-visit payload.
No parameters.
Example
curl -sI "$BASE/style.css" | grep -i cache-control
GET/og-image.png · /favicon.svg · /favicon.icoPublic
Icon & share card
og-image.png is the 1200×630 social share card, generated by scripts/make_og_image.py and embedded as base64 in src/pages.py. /favicon.ico 301-redirects to /favicon.svg: some browsers and crawlers request it regardless of the <link rel=icon> hint, and only an SVG ships, so without the redirect every first visit eats a 404.
No parameters.
Example
curl -sI "$BASE/og-image.png" | grep -i content-type
GET/robots.txt · /sitemap.xmlPublic
SEO resources
Crawler policy and sitemap, cached for one hour.
No parameters.
Example
curl -s "$BASE/robots.txt"
Data endpoints
GET/api/statsPublic
Dataset size
Public read-only aggregate counts (rows, knowledge entries, covered economies and years) for the landing page and dashboard sidebar. Contains no environment details such as DB paths — those live in /api/db.
No parameters.
Example
curl -s "$BASE/api/stats"
GET/api/overviewPublic
Overview cards
Returns the headline cards for a year + dimension. Each card carries the localized display value plus *_key (canonical Chinese key) and *_slug (stable ASCII key). Dropdown options are included.
| Parameter | Type | Required | Description |
|---|
| year | int | no | Year, defaults to 2024. |
| dimension | str | no | Economy: accepts the canonical key (中国), the English label (China) or the slug (china). Defaults to the 亚太 aggregate. |
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/overview?year=2024&dimension=china&lang=en"
GET/api/insightsPublic
Data insights
Recomputable year-over-year movements computed from local public data: the biggest movers for the dimension, the economies with the largest rank changes on the widest-coverage indicator, and the real coverage counts. Every item carries both year values so it can be checked against /api/indicators; indicators without a prior-year value are omitted rather than given a misleading percentage.
| Parameter | Type | Required | Description |
|---|
| year | int | no | Year, defaults to 2024. Insights compare it with the prior year. |
| dimension | str | no | Economy: canonical key, English label or slug; defaults to the 亚太 aggregate. |
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/insights?year=2024&dimension=china&lang=en"
GET/api/indicatorsPublic
Indicator wide table
The primary machine-readable endpoint. category / indicator / dimension / unit are localized per lang, each accompanied by *_key and *_slug, so English consumers need no Chinese glossary.
| Parameter | Type | Required | Description |
|---|
| year | int | no | Omit for every year. |
| dimension | str | no | Economy (key / label / slug). |
| category | str | no | Profession / category. |
| indicator | str | no | Indicator (key / label / slug). |
| q | str | no | Free-text search across both the raw and the localized row, in either language. |
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/indicators?indicator=GDP&year=2024&lang=en"
# or search by keyword (either language works)
curl -s "$BASE/api/indicators?q=retail" | head -40
GET/api/export.csvPublic
CSV export
Exports the current selection as CSV with the fixed columns year,category,indicator,dimension,value,unit,note. Content is BOM-prefixed (utf-8-sig) so Excel opens it directly; the filename encodes year, dimension slug and language.
| Parameter | Type | Required | Description |
|---|
| year · dimension · indicator · category · q · lang | — | no | Identical to /api/indicators. |
Example
curl -s "$BASE/api/export.csv?year=2024&dimension=china&lang=en" \
-o indicators_2024_china_en.csv
GET/api/indicator_keysPublic
Bindable indicator keys
Returns every bindable (category, indicator, dimension) triple for the custom-analysis variable pickers.
| Parameter | Type | Required | Description |
|---|
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/indicator_keys?lang=en"
GET/api/customPublic
Custom analyses (read)
Without name it lists the analyses (with both language names). With name it evaluates one and returns the result; compare analyses also carry a yoy value. The name matches in either language.
| Parameter | Type | Required | Description |
|---|
| name | str | no | Analysis name; omit to list all. |
| year | int | no | Evaluation year, defaults to 2024. |
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/custom?name=GDP&lang=en"
POST/api/customPublic
Custom analyses (create)
Appends an analysis to the config file. The expression is pre-validated against placeholder values and duplicate names are rejected. A variable that cannot resolve yields an error field rather than an exception.
No parameters.
Request body
JSON body:
{
"name": "Tertiary share", // required, canonical name
"name_en": "Tertiary share", // optional display name
"unit": "%",
"description": "…",
"description_en": "…",
"variables": { // required: var -> [category, indicator, dimension?]
"x": ["国民经济", "第三产业增加值", "中国"],
"y": ["国民经济", "GDP", "中国"]
},
"expr": "x / y * 100", // required, safely evaluated
"compare": true // optional, compute YoY
}Example
curl -s -X POST "$BASE/api/custom" \
-H 'Content-Type: application/json' \
-d '{"name":"Tertiary share","variables":{"x":["国民经济","第三产业增加值","中国"]},"expr":"x"}'GET/api/reportPublic
Statistical bulletin
Generates a bulletin for a year + dimension. format=json returns structured data (identifiers already localized server-side) for the frontend to render; the default is plain-text Markdown. cloud=1 adds a cloud AI interpretation.
| Parameter | Type | Required | Description |
|---|
| year | int | no | Defaults to 2024. |
| dimension | str | no | Economy, defaults to 亚太. |
| cloud | 0 | 1 | no | Whether to request the cloud AI interpretation. |
| format | json | no | Present → JSON, otherwise plain text. |
| lang | en | zh | no | Localization language. |
Example
curl -s "$BASE/api/report?year=2024&format=json&lang=en"
Interpretation
GET/api/askPublic
Natural-language query
Parses a natural-language question into indicator queries. With cloud=1 the local statistics are passed to the model as grounding facts (the prompt forbids inventing figures). If the cloud is unavailable it degrades to local statistics and explains that in the 注 field.
| Parameter | Type | Required | Description |
|---|
| text | str | yes | The question, e.g. 2024 GDP or retail sales. |
| dimension | str | no | Restrict to one economy. |
| cloud | 0 | 1 | no | Call the cloud AI interpretation or not. |
| lang | en | zh | no | Localization language; also selects the cloud prompt language. |
Example
curl -s "$BASE/api/ask?text=2024%20GDP&lang=en&cloud=0"
GET/api/infini_skillPublic
Integration self-check
Returns the recommended agent_infini workflow and a resource pre-flight result. Read-only; never triggers a cloud call, and degrades gracefully when the CLI is absent.
| Parameter | Type | Required | Description |
|---|
| db | str | no | Comma-separated database ids to verify. |
| rag | str | no | Comma-separated RAG knowledge-base ids to verify. |
Example
curl -s "$BASE/api/infini_skill"
GET/api/ai-cachePublic
Interpretation cache metrics
Runtime metrics for the interpretation cache: hit / miss counters, L1 capacity and whether Redis is available. Read-only and side-effect free — it proves a repeated question never re-hit the cloud.
No parameters.
Example
curl -s "$BASE/api/ai-cache"
Knowledge base
GET/api/knowledgePublic
Knowledge base (read)
Lists or searches knowledge entries; matches are attached to query answers as supplementary context.
| Parameter | Type | Required | Description |
|---|
| q | str | no | Keyword search; omit to list all. |
| category | str | no | Filter by category. |
Example
curl -s "$BASE/api/knowledge?q=GDP"
POST/api/knowledgePublic
Knowledge base (create)
Adds an entry; title and content are required.
No parameters.
Request body
JSON body: {"title": "…", "content": "…", "category": "通用", "tags": "gdp,trade", "source": "…"}Example
curl -s -X POST "$BASE/api/knowledge" \
-H 'Content-Type: application/json' \
-d '{"title":"GDP 口径","content":"…"}'DELETE/api/knowledgePublic
Knowledge base (delete)
Deletes an entry by id.
| Parameter | Type | Required | Description |
|---|
| id | int | yes | Entry id. |
Example
curl -s -X DELETE "$BASE/api/knowledge?id=1"
Admin endpoints (token required)
GET/api/dbAdmin token
Database status
Returns the DB path, engine, size and per-table row counts. It exposes environment details, hence admin-only.
No parameters.
Example
curl -s "$BASE/api/db" -H "X-Admin-Token: $ADMIN_TOKEN"
POST/api/reseedAdmin token
Re-seed
Truncates the indicators table and reloads the embedded public-data seed set, clearing the stale KV snapshot. Overwrites existing rows.
No parameters.
Example
curl -s -X POST "$BASE/api/reseed" -H "X-Admin-Token: $ADMIN_TOKEN"
GET/api/kv-statusAdmin token
KV status
Checks the Redis / Vercel KV connection: env-var presence (masked), write and read-back.
No parameters.
Example
curl -s "$BASE/api/kv-status" -H "X-Admin-Token: $ADMIN_TOKEN"
POST/api/collectAdmin token
Data collection
Fetches indicators from World Bank Open Data and stores them (public source, no auth). Requires a writable disk, so it is off in production.
No parameters.
Request body
JSON body: {"source": "worldbank" | "global", "year": 2024, "country": "CN", "indicators": "NY.GDP.MKTP.CD"}Example
curl -s -X POST "$BASE/api/collect" \
-H 'Content-Type: application/json' -H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"source":"worldbank","year":2024,"indicators":"NY.GDP.MKTP.CD"}'Operations
GET/healthzPublic
Health check
Liveness probe: always 200, with status telling healthy from degraded. Triggers no seeding and needs no auth, so platform probes and external monitors can call it as-is. version identifies the live build, while uptime_s surfaces a just-restarted process as a clue.
No parameters.
Example
curl -s "$BASE/healthz"
With no QU_STAT_ADMIN_TOKEN configured, admin endpoints are permitted in local development and return 403 on Vercel — there is no persistent disk, so re-seeding or collecting has nowhere to land.