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.

Base URL https://qu-stat-system.vercel.app
local: http://127.0.0.1:5000
AuthPublic endpoints need none; admin endpoints need X-Admin-Token or ?token=
FormatJSON / CSV / Markdown (per endpoint)
CachingAPI and HTML are no-store; static assets are immutable for 1 year (URLs carry a content hash)
On this page

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.

ParameterTypeRequiredDescription
langen | zhnoControls <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.

ParameterTypeRequiredDescription
langen | zhnoSource 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.

ParameterTypeRequiredDescription
langen | zhnoDocumentation 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.

ParameterTypeRequiredDescription
langen | zhnoStatement 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.

ParameterTypeRequiredDescription
yearintnoYear, defaults to 2024.
dimensionstrnoEconomy: accepts the canonical key (中国), the English label (China) or the slug (china). Defaults to the 亚太 aggregate.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
yearintnoYear, defaults to 2024. Insights compare it with the prior year.
dimensionstrnoEconomy: canonical key, English label or slug; defaults to the 亚太 aggregate.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
yearintnoOmit for every year.
dimensionstrnoEconomy (key / label / slug).
categorystrnoProfession / category.
indicatorstrnoIndicator (key / label / slug).
qstrnoFree-text search across both the raw and the localized row, in either language.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
year · dimension · indicator · category · q · lang—noIdentical 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.

ParameterTypeRequiredDescription
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
namestrnoAnalysis name; omit to list all.
yearintnoEvaluation year, defaults to 2024.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
yearintnoDefaults to 2024.
dimensionstrnoEconomy, defaults to 亚太.
cloud0 | 1noWhether to request the cloud AI interpretation.
formatjsonnoPresent → JSON, otherwise plain text.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
textstryesThe question, e.g. 2024 GDP or retail sales.
dimensionstrnoRestrict to one economy.
cloud0 | 1noCall the cloud AI interpretation or not.
langen | zhnoLocalization 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.

ParameterTypeRequiredDescription
dbstrnoComma-separated database ids to verify.
ragstrnoComma-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.

ParameterTypeRequiredDescription
qstrnoKeyword search; omit to list all.
categorystrnoFilter 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.

ParameterTypeRequiredDescription
idintyesEntry 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.

Asia-Pacific Statistical Analysis System · official public data only Privacy & data · GET /docs?lang=zh · https://qu-stat-system.vercel.app/sitemap.xml