Postal Codes Dataset for China, CN

199
Updated:
Files:1
Size:222 kB
Formats:csv
License:CC-BY-4.0

Postal Codes Dataset for China, CN including name of the city, town, or place, various administrative divisions and alternative city names.

Part of a Data Solution

  • Postal Codes Solution

    Every worldwide postal-code dataset in one bundle — the ultimate global reference for precise postal codes.

    Explore →

Premium

You're viewing a free sample

Get the complete dataset — all rows and files — delivered instantly after checkout, with lifetime access to the latest version.

  • Secure checkout via Stripe
  • Instant download after payment
  • Lifetime access to the latest version
  • Creative Commons Attribution 4.0 International license
20% off
$49.90$39.92
one-time payment

API Access

Access dataset files directly from scripts, code, or AI agents.

Browse dataset files
Dataset Files

Each file has a stable URL (r-link) that you can use directly in scripts, apps, or AI agents. These URLs are permanent and safe to hardcode.

/logistics/postal-codes-cn/
https://datahub.io/logistics/postal-codes-cn/_r/-/.licensing-notes.md
https://datahub.io/logistics/postal-codes-cn/_r/-/README.md
https://datahub.io/logistics/postal-codes-cn/_r/-/datapackage.yml
Key Files

Start with these files — they give you everything you need to understand and access the dataset.

datapackage.ymlmetadata & schema
https://datahub.io/logistics/postal-codes-cn/_r/-/datapackage.yml
README.mddocumentation
https://datahub.io/logistics/postal-codes-cn/_r/-/README.md
Typical Usage
  1. 1. Fetch datapackage.yml to inspect schema and resources
  2. 2. Download data resources listed in datapackage.yml
  3. 3. Read README.md for full context

Data Files

Postal Codes Data Resource for China, CN

About

Last updated
17 September 2026
Total rows
...
Format
CSV
File size
222 kB

About this dataset

China (CN) — internal licensing notes

Source research, 2026-09-17 — first proper audit of this country

No .licensing-notes.md existed for CN before this pass. datapackage.yml carries the old-style placeholder sources: [{title: "Multiple sources", path: https://datahub.io/collections/postal-codes-datasets}] — the same non-informative pattern already flagged as a defect elsewhere in this project (see feedback_datapackage_no_sources) — and no licenses: array at all. is_premium: true but nothing in the repo records what cn/cn.csv actually is. Same starting condition CH/PR/CO were in before their 2026-09-16 audits.

Also stale in datapackage.yml: the declared resource is bytes: 9241, modified: '2024-11-07'. The real R2 object is 230,741 bytes, modified 2026-07-30 — the metadata describes neither the size nor the date of the file customers actually get. See §4 for what the 9,241-byte figure actually corresponds to.

1. What the current R2 file (cn/cn.csv, 230,741 bytes, 2026-07-30) actually is — VERIFIED

Fetched directly from the main postal-codes R2 bucket (read-only, R2_ACCESS_KEY/R2_SECRET_KEY from this repo's own .env).

Row count: 2,352 data rows. Not a coincidence and not a subset build — this is essentially the complete GeoNames China postal file, because GeoNames' own China coverage is truncated at source (see below). 230 KB for "China postal codes" looks tiny only if you assume GeoNames covers China at real 6-digit/township granularity. It doesn't.

Confirmed via GeoNames' own readme.txt (https://download.geonames.org/export/zip/readme.txt):

For China we have only the first digits of the full postal codes ending
with 00

This is the exact "for copyright reasons"-style truncation caveat the task asked me to check for — China sits in the same readme paragraph as Chile, Ireland, Malta, Argentina and Brazil, all of which GeoNames explicitly ships in truncated form. Verified directly: every one of the 2,352 postal codes in both the R2 file and raw GeoNames CN.txt ends in 00 (2,349 distinct codes). Real Chinese postal codes are 6-digit and reach township/delivery-office granularity (China's actual system has on the order of tens of thousands of assigned codes); this file only ever carries the first 4 significant digits + 00, i.e. prefecture/county-level granularity at best, never a real township-level code. This is a GeoNames-side ceiling, not something introduced by this repo's pipeline — no rebuild against GeoNames can fix it; a real per-township product needs a different source entirely (see §3).

Fetched https://download.geonames.org/export/zip/CN.zip (CC BY 4.0) directly for comparison: CN.txt also has exactly 2,352 rows, and the R2 file is a straight tab-to-comma conversion of it, carrying forward several defects:

GeoNames:  CN  230000  Hefei  Anhui  01  Hefei Shi  3401          31.8639  117.2808  4
R2 file:   CN,230000,Hefei,Anhui,1,Hefei Shi,3401.0,nan,,31.8639,117.2808,4.0,Hefei

Defect A — literal string "nan" in admin_name3 (225 rows)

Wherever GeoNames' own admin3 code/name field is empty, the R2 file contains the literal 4-character string nan instead of an empty cell — a classic pandas NaN-to-str() bug. Confirmed on 225 of 2,352 rows.

This is not a stale-conversion artifact — it is reproduced in the pipeline's own live output. cn/geonames/latest/0.csv and cn/geonames/2026-09-01/0.csv (both already sitting in the same R2 bucket, produced by scripts/geonames/geonames_parse_to_R2.py's automated GeoNames refresh job, most recently run 2026-09-01) carry the identical 225-row "nan" defect and the identical float-accuracy defect below. The bug is in the shared ingestion script (clean_and_normalize/df.to_csv in geonames_parse_to_R2.py never .fillna('')s the postal-code frame the way it does for alternatenames), not something specific to the currently-shipped cn.csv snapshot. This audit is scoped to CN only, so I have not checked whether other countries' shipped files inherit the same bug from the same script — flagging it as a likely-systemic root cause, not asserting its blast radius.

Defect B — accuracy is float-formatted (612 rows 4.0, 1,726 rows 1.0, 1 row 3.0, 13 blank)

Same class of bug already fixed-forward in datasets/lk/scripts/publish_r2.py (if r["accuracy"].endswith(".0"): abort) and flagged in the CH audit. GeoNames' own file has plain integers (4, 1, 3); the R2 file has 4.0, 1.0, 3.0.

Defect C — admin_code1 is GeoNames' own internal ordinal, not any real China admin code

All 31 distinct admin_code1 values in the file are 133 (skipping 17 and 27) — a GeoNames-internal sequential enumeration in roughly the order GeoNames itself lists provinces, e.g. 1=Anhui, 2=Zhejiang, 22=Beijing, 31=Hainan, 33=Chongqing. This does not match either of China's two real numbering schemes:

  • ISO 3166-2:CN — two-digit codes derived from GB/T 2260 (e.g. CN-11 Beijing, CN-12 Tianjin, CN-34 Anhui, CN-46 Hainan, CN-50 Chongqing).
  • GB/T 2260 itself — the same two-digit province prefix used inside the 6-digit county code.

Under either real scheme Anhui is 34/CN-34, not GeoNames' 1; Beijing is 11/CN-11, not GeoNames' 22. This is the standard GeoNames admin-code "false friend" this project has flagged repeatedly elsewhere — China is not an exception the way Switzerland turned out to be.

Defect D — admin_code2 is a mix of a real (truncated) official code and, for 79 rows, a GeoNames internal ID with no administrative meaning at all

For the 2,271 rows that have a normal prefecture layer, admin_code2 (e.g. 3401 for Hefei-Shi under Anhui) lines up with the first four digits of the real GB/T 2260 prefecture code (Hefei's actual code is 340100) — genuinely informative, just truncated and float-suffixed (3401.0).

But for 79 rows — the four direct-administered municipalities (Beijing, Tianjin, Shanghai, Chongqing) and a set of county-level cities/districts that sit directly under their province with no intervening prefecture (e.g. Shennongjia, Xiantao, Tianmen, Qianjiang in Hubei; Wujiaqu in Xinjiang; Jiyuan in Henan) — GeoNames has no 4-digit prefecture code to put there, and falls back to its own internal 7–8 digit geonameId (e.g. Beijing → 11876380, Chongqing → 8739734, Shennongjia → 1795614). These numbers are meaningless as administrative codes — they are GeoNames' own gazetteer row IDs — but nothing in the file distinguishes them from the genuine truncated GB/T 2260 codes in the other 2,271 rows except length. A consumer treating admin_code2 as a uniform administrative code column would silently ingest 79 fake codes alongside 2,271 real (if truncated) ones.

Defect E — the free/sample distribution channel is cleaner than the paid file

https://postal.datahub.io/cn/cn.csv (the public sample referenced by README.md's "first 100 rows" claim, and matching datapackage.yml's bytes: 9241 figure closely — live fetch returned 9,199 bytes / 100 rows, last-modified: 2026-07-30, same day as the full R2 object) does not carry the literal "nan" string — those cells are genuinely empty in the sample. A row-for-row diff of the sample against the first 100 rows of the paid cn/cn.csv shows every other field identical; only the "nan" cells differ. The paying customer's file is measurably lower quality than the free sample the same customer evaluates before buying. This is worth fixing regardless of what source is used going forward.

Verdict on the current file: it is a legitimate-if-defective GeoNames CC BY 4.0 extract of what is itself a heavily-truncated GeoNames China dataset (prefecture/county granularity ending in 00, never real 6-digit township codes), carrying four packaging defects (nan strings, float-formatted accuracy, GeoNames-internal admin_code1, and 79 rows of GeoNames-internal admin_code2 masquerading as administrative codes) on top of a source-level granularity ceiling that no repackaging of GeoNames can lift.

2. Coverage note — no conflation of Hong Kong / Macau / Taiwan found (disclosure only, not a judgment call)

Checked explicitly, as requested. The current file contains zero rows for Hong Kong, Macau, or Taiwan — no admin_name1 value matches any of those names, and GeoNames itself carries them under separate country files (HK.txt, TW.txt; Macau has no ZIP entry in GeoNames' postal export at all). So the existing product does not conflate them with mainland administrative structure — it simply omits them entirely, which is consistent with how GeoNames scopes its own CN.txt (31 mainland province-level divisions only).

Stated factually, without taking a position on the underlying status question: these three are administered under postal/administrative systems that are procedurally distinct from mainland China's GB/T 2260 + China Post scheme, not variants of it:

  • Hong Kong has no postal code system in ordinary civilian use; Hongkong Post's own guidance is to leave the postcode field blank. Mainland China's own postal system reserves a placeholder code 999077 for mail addressed to Hong Kong from the mainland, but this is not a real Hong Kong postcode and Hongkong Post does not use it.
  • Macau likewise has no postal code system; Macao Post (CTT) routes by descriptive street/building/floor address only. A 999078 placeholder exists in the same mainland-originated scheme, similarly not used by Macau's own carrier.
  • Taiwan has its own real, actively-administered postal code system run by Chunghwa Post (3-digit from 1970, 3+2-digit from 1985, current 3+3-digit format since 2020-03-03), entirely independent of GB/T 2260. A code range in the mainland's own numbering (000000009999) was nominally reserved for "Taiwan" in the PRC scheme but is not populated or used; mail between the mainland and Taiwan is handled with Taiwan's own codes.

If a future build for CN is scoped to include any of these three, the schema/licensing/source questions would need to be worked out separately for each — they are not a drop-in extension of the mainland admin hierarchy this file uses.

3. Official tier-1/2 sources checked FIRST, per the standing GeoNames-is-fallback rule

China Post (11185.cn / chinapost.com.cn) — checked, no bulk channel found

  • 11185.cn (China Post's consumer site) and dey.11185.cn (its postcode lookup tool) expose single-address lookup only — type an address, get one postcode back. No bulk download, no public API, no open-data section found.
  • data.chinapost.com.cn — China Post Group's own "data center" domain, the most plausible candidate for an official bulk channel — is currently unreachable (DNS does not resolve). Cannot confirm it ever offered public downloads, only that nothing is there now.
  • No terms-of-use text could be retrieved from China Post's own domains in this pass (fetch failures on the JS-heavy pages), so no licence posture is confirmed for China Post as a source, on top of the fact that no bulk data was found there to license in the first place.

Conclusion: no confirmed official/tier-1 postal-code bulk source exists. This is a genuine gap, not a "we didn't look" gap — unlike CH, KOR, or PHL where an official channel existed and just hadn't been used yet, China Post does not appear to publish one at all.

National Bureau of Statistics (NBS, stats.gov.cn) — CONFIRMED, real official admin-code source, but ADMIN ONLY, not postal

  • https://www.stats.gov.cn/sj/tjbz/tjyqhdmhcxhfdm/2023/ — "2023年统计用区划代码和城乡划分代码" (2023 Statistical Administrative Division Codes and Urban-Rural Classification Codes), NBS's own annually-republished crosswalk.
  • Format: a 12-digit statistical division code (province/prefecture/ county/township/village) whose first 6 digits are the classic GB/T 2260 province+prefecture+county code, plus a 3-digit urban/rural classification suffix. Delivered as an HTML drill-down directory tree (province → prefecture → county → township pages), not a single downloadable file.
  • Scope: 31 mainland province-level divisions — explicitly excludes Taiwan, Hong Kong SAR, and Macao SAR from this statistical series (a separate, independent confirmation of §2, from the admin-code side rather than the postal side).
  • Licence/terms (from NBS's own terms-of-service page, stats.gov.cn/wzgl/202302/t20230217_1912857.html): content is copyrighted by NBS; reuse/reprinting is permitted if attributed back to stats.gov.cn, not distorted, and used for legitimate/good-faith informational purposes. This is a permissive-with-attribution posture, not a formal open licence — no SPDX identifier applies. Per this project's provenance-gate convention, any licence_status built on this source should use a custom: id (e.g. custom:nbs-stats-gov-cn-terms) and decision: "unresolved" until a human confirms that posture is acceptable for a resold commercial product — NBS's own language ("legitimate purposes," no explicit commercial-resale carve-out) is not self-evidently compatible with reselling the derived crosswalk at a $49–99 tier.
  • This would only ever solve the admin-code side. GB/T 2260 codes reach province/prefecture/county; NBS's data does not itself contain postal codes, so it cannot fix the GeoNames postal-granularity ceiling in §1 — it could only replace the fake admin_code1/partially-fake admin_code2 values with the real ones, joined against whatever postal layer is used.

GitHub community repos — evaluated, none are primary sources, licensing is mixed/absent

Checked via gh api for reliable licence metadata (README claims alone were not trusted):

RepoContainsLicenceNotes
tombcato/china-zipcode-dataPostal codes ↔ admin (adcode)MITMost current (pushed 2026-02-13); states it's revised to 2023 NBS admin codes, but itself credits Amap + other GitHub projects as sources — a re-aggregation, not primary
mumuy/data_post6-digit postal codes, 4-tier down to delivery officeMITPushed 2024-05-28, no stated primary source
khaeru/gb2260GB/T 2260 admin codes onlyGPLv3 (in-repo, not GitHub-detected)Sources from the same NBS page above; stale (2018)
grezbo/cn_zipcodePostal codes → adminnone declaredClaims 800k+ rows, explicitly "scraped from the web"; stale (2015)
phoenixgao/ChinaPostcodesPostcodesUnlicenseStale (2016), not verified in depth
CallMeNP/GB2260Admin codes, multi-year historicalnone declaredStale (2019)

None of these is a primary/official source. The best-licensed and most current one (tombcato, MIT) is itself downstream of Amap (a Chinese commercial mapping company) and unnamed "other GitHub projects" — using it would mean reselling a re-aggregation of an unlicensed upstream, which this project's provenance gate treats as unresolved at best. A for-sale commercial dataset ("170万条邮政编码信息" / ~1.7M postal-code records, Access database, jamesqi.com) also surfaced in search — not evaluated further, as it is a paid third-party product, not a research source to verify against.

4. What this means for the current product

  • The granularity ceiling is real and cannot be fixed by rebuilding against GeoNames. GeoNames' China export is capped at "first digits + 00" by GeoNames' own admission — no amount of re-processing produces real 6-digit township codes from it.
  • No official tier-1 bulk postal source was found to replace it with. China Post appears not to publish one; its most likely data-center domain is dead.
  • A real official tier-2 admin-code source exists (NBS GB/T 2260/ statistical codes) but only fixes the admin-code columns, not the postal-code granularity, and its own reuse terms are permissive- with-attribution rather than a clean open licence — would need explicit decision sign-off before use in a resold product, not an automatic "compatible."
  • Every community postal-code repo checked is either unlicensed or is itself a re-aggregation of unlicensed/commercial upstreams (Amap and others) — none is a clean substitute source.
  • Independent of any sourcing decision, defects A/B/C/D/E above are pure packaging bugs (nan-strings, float-accuracy, fake admin codes passed through unlabeled, a worse-quality paid file than the free sample) that should not survive any republish of this file regardless of which source is ultimately used.

Verdict

This is not a "GeoNames is good enough" case, and it is also not a straightforward "here is the official upgrade" case like CH. China differs from Switzerland in the opposite direction: GeoNames' own China coverage is source-capped at prefecture/county-level truncated codes, no confirmed official bulk postal source exists to replace it with, and the one confirmed official source that does exist (NBS) covers admin codes only, not postal codes, under attribution-only terms that still need a human licensing decision before resale. A rebuild would raise data correctness (fix defects A–D, drop or clearly label the 79 fake admin_code2 values, fix Defect E) without being able to raise data granularity — that would require either an unverified/unlicensed community aggregation or a commercial third-party purchase, both outside what this audit was scoped to resolve.

Not done in this pass, by design: no script written, no datapackage.yml change, no R2 write, no git commit. This file is the audit record only.

Build, 2026-09-17 — packaging fix (local only, not published)

Rebuilt per the plan above. datasets/cn/scripts/build_postal_codes_product.py downloads https://download.geonames.org/export/zip/CN.zip fresh (2,352 rows, matches the audit exactly) and writes datasets/cn/data/{cn.csv, datapackage.json, README.md, ATTRIBUTION.txt, cn.zip} directly with csv (no pandas), so none of the shared script's bugs can reappear.

Coverage: unchanged, 2,352 rows, same GeoNames source. This was a packaging fix, not a granularity upgrade, per the task scope — every postal_code still ends in 00 (source-level truncation, confirmed again: all 2,352 rows end 00).

Defect A ("nan" strings) — fixed, and not present in this build's own output pipeline. Reading GeoNames' raw tab file directly with csv never introduces a pandas NaN → str() conversion, so the 225 originally- blank admin_name3 rows stay genuinely empty. A belt-and-braces guard (if value == "nan": value = "") and a post-write assertion are in the script anyway, in case a future edit reintroduces pandas.

Defect B (float-formatted accuracy) — fixed, same reasoning. The raw GeoNames file already has plain integers (4, 1, 3, or blank); this build never coerces through a numeric dtype, so no .0 suffix appears. Guarded with the same belt-and-braces strip + assertion.

Defect C (admin_code1) — fixed via a verified NAME-based crosswalk, with one important correction to the audit's own framing. Fetched en.wikipedia.org/wiki/ISO_3166-2:CN directly on 2026-09-17, including its "Changes" history section. Finding: ISO 3166-2:CN's numeric codes (CN-11, CN-34, …, which do match GB/T 2260's province digits) were superseded on 2017-11-23 by the current alpha-2 codes (CN-BJ, CN-AH, …). The audit's illustrative example ("real Beijing=CN-11 … real Anhui=CN-34") was correct for the pre-2017 numeric standard but is not the code ISO 3166-2:CN uses today. Per the task's own instruction to verify against the live Wikipedia page rather than assume, and consistent with this project's standing rule to confirm current ISO registers rather than carry forward an assumed one (see the IDN registry row in CLAUDE.md), this build ships the current alpha form — e.g. CN-BJ (Beijing), CN-AH (Anhui) — as the real, verified ISO 3166-2:CN code. All 31 mainland province-level names present in the GeoNames source matched the crosswalk with zero unmapped names. GeoNames' own admin_code1 (an internal 1–33 ordinal) matches neither the current alpha codes nor the superseded numeric ones — confirmed false-friend, now fixed either way you read the audit's claim.

Defect D (admin_code2) — fixed by digit-count detection, not by NBS join. Confirmed 2,271 rows carry a genuine 4-digit truncated GB/T 2260 prefecture code (kept as-is) and exactly 79 rows carry a 7–8 digit GeoNames-internal geonameId instead (Beijing, Tianjin, Shanghai, Chongqing, plus Shennongjia/Xiantao/Tianmen/Qianjiang/Jiyuan/Wujiaqu/ Shihezi) — matches the audit's count exactly. These 79 are now blanked (admin_code2 = ""), disclosed by name in ATTRIBUTION.txt, rather than shipped as a fake-looking administrative code. 2 rows had no admin_code2 in the source to begin with and remain blank. No NBS data was fetched or joined — that source's licensing remains unresolved per §3 of the audit and was correctly out of scope for a packaging-only fix.

Defect E (paid file worse than free sample) — resolved as a side effect. This rebuild's output has none of defects A/B, so the paid file is no longer measurably worse than the free sample on those fields.

Coverage disclosure (§2 of the audit) — carried into ATTRIBUTION.txt and README.md verbatim as a factual coverage-boundary statement: zero HK/ Macau/Taiwan rows, GeoNames scopes them as separate files, no position taken on the underlying status question.

Validation: frictionless validate datapackage.json run from inside datasets/cn/data/ (the delivered artifact, not a working copy) → VALID. Post-write script assertions also confirm: 0 "nan" leaks, 0 float-formatted accuracy values, 0 non-4-digit admin_code2 values, 0 malformed admin_code1 values, 0 duplicate (postal_code, place_name) pairs, exactly 79 blanked admin_code2 rows.

Local only, as instructed: no R2 upload, no git commit, no changes to _meta/catalog.json or datapackage.yml. Only datasets/cn/scripts/build_postal_codes_product.py, datasets/cn/sources/postal_raw/{CN.txt,CN.zip,readme.txt}, datasets/cn/data/*, and this note were touched.