Which Official Chinese Court Endpoints Can You Actually Enumerate?
Yesterday we reported that China's national enterprise bankruptcy register advertises 47,949 pages of judgments and serves fifty of them. We described that ceiling as a property of the register. Today we paged two more of its list endpoints and found that it is not.
The cap is applied per chain. One of the three list endpoints on that host has no cap at all — it handed over every page it advertises, all 1,448 of them, without complaint. Another has the same fifty-page ceiling as the judgments list and, unlike the judgments list, cannot be talked out of it by filtering. Same domain, same afternoon, same request pattern, three different answers.
That distinction matters more than the individual numbers, because it is the thing a buyer cannot see from outside. “The data is published on an official site” is not a statement about obtainability. Obtainability is a property of a specific list endpoint on a specific day, and on this host it varies by a factor of roughly two thousand between the best case and the worst.
robots.txt was requested before any list endpoint. Nothing here is a legal conclusion about what the site permits: we did not log in, did not read terms of service, and did not deliberately probe for rate limits. Every figure is dated because this behaviour can change without notice, and none of it should be taken as a claim about what the operator intends.Three chains, three regimes
The register exposes three separate paginated lists, each reached by its own POST endpoint and each driven by the same pagination widget. That widget is initialised in the served page source with a literal total-page count, which is the only place on the entire site where any total appears at all. Those literals are the site's numbers, not ours, and there is no second interface anywhere on the host that would let us check them.
| List chain | Advertised pages | Pages served | Records reachable | Share reachable | Behaviour past the last served page |
|---|---|---|---|---|---|
| Judgments | 47,949 | 50 | 500 | 0.104% | HTTP 404, 1,148-byte body |
| Announcements | 90,746 | 50 | 500 | 0.055% | HTTP 404, 1,148-byte body, byte-identical to the above |
| Debtors | 1,448 | 1,448 | 14,479 | 100% | HTTP 200, 443-byte body, no rows |
The announcements chain is the new measurement and it behaves exactly like the judgments chain measured yesterday. Pages 1, 2, 49 and 50 each returned HTTP 200 carrying ten record identifiers. Pages 51, 55, 60, 100, 1,000 and 90,746 all returned HTTP 404 with a body whose checksum was identical on every one of them — and identical to the 404 bodies the judgments chain produced yesterday, which is what tells you the ceiling is enforced by shared infrastructure rather than by three separately written handlers.
Re-issuing page 1 immediately after that run of 404s returned HTTP 200 again. That is the test that separates a depth limit from rate limiting, and it comes out the same way it did yesterday: the server was not annoyed with us, it simply does not serve page 51. Sending pageSize, rows, limit or size set to 100 changed the response length slightly and returned ten rows in every case, so the window is 500 records regardless of how you ask.
The chain with no ceiling
The debtor list is the interesting one, and we went looking for its wall on the assumption that it had one. Pages 1, 2, 49, 50, 51, 55, 100 and 1,000 all returned ten rows. Page 1,448 — the last page the widget advertises — returned nine, which is the tail of a collection of 14,479 records rather than a truncation. Pages 1,449, 1,500, 2,000 and 5,000 returned HTTP 200 with a 443-byte body containing nothing.
To be sure the pagination was real rather than the same slice re-served under different page numbers, we compared the identifier sets from pages 1, 100, 1,000 and 1,448. All six pairwise intersections were empty and the union was thirty-nine distinct identifiers across thirty-nine rows. The list genuinely advances.
So on a single host, one registry chain is fully enumerable and two are not, and the difference tracks the size of the underlying collection almost perfectly: the smallest collection is open, and the two collections large enough to matter are capped at the same 500 rows.
Whether filtering gets you underneath the cap
A capped list is not necessarily an unreachable collection. If the interface offers a filter, you can partition the collection into slices small enough to fit inside the window and walk the slices instead of the list. Yesterday that worked on the judgments chain: a single-day publication-date filter returned that day's rows, and a day fits comfortably under 500. The obvious question was whether the same trick rescues the announcements chain.
Before testing that, we tested whether the filter filters at all. This is a step worth stealing regardless of what you are crawling: ask for a range that cannot possibly contain data and see whether the total moves. A range covering the whole of 1900 returned HTTP 200 with a 3,418-byte body and zero rows, so the date parameters are being honoured rather than silently discarded — a failure mode common enough on filtered endpoints that assuming otherwise has burned us before.
Then the slices:
| Slice on the announcements chain | Page 1 | Page 50 | Page 51 | Reading |
|---|---|---|---|---|
| 8 June 2025 (a Sunday) | 0 rows, HTTP 200 | 0 rows, HTTP 200 | HTTP 404 | Nothing published; the cap still fires on an empty set |
| 1 January 2025 (a holiday) | 10 rows | 0 rows, HTTP 200 | — | Fits inside the window; fully retrievable |
| 10 June 2025 (a Tuesday) | 10 rows, all dated 10 June | 10 rows, all dated 10 June | HTTP 404 | At least 500 that day; the window overflows |
| 10 June 2025, one case type | 10 rows | 10 rows, all dated 10 June | HTTP 404 | Overflows even at the finest cell the interface exposes |
The Sunday row is the cleanest evidence about the mechanism. That day has no announcements at all — page 1 returns an empty result — and page 51 still returns 404 with the same body checksum. The ceiling is enforced on the page number itself, before anything about the result set is consulted. It does not relax when the collection you asked for is small; it is not a limit on how much data you may have, it is a limit on how far down a list you may point.
The last row is the one that matters commercially. The interface exposes exactly two structured filters on this chain: a publication-date range and a case-type facet whose eight permitted values we took from the site's own dropdown rather than guessing. On 10 June 2025 we tried all eight. Four returned no rows at all that day. Three had shallow results that fit under the window. One — the general bankruptcy-case type — filled page 50 with ten rows all dated 10 June, and pages 51, 52 and 60 returned the familiar 404. One calendar day intersected with one case type is the finest cell this interface can address, and on at least one measured day that cell holds more than the interface will serve.
It is worth putting those three regimes side by side as request counts, because that is the form the question takes when it reaches an engineering plan rather than a blog post. The debtor chain costs 1,448 list requests to walk end to end — under ten minutes at a polite interval, and a job you can re-run nightly to detect additions. The judgments chain costs on the order of 48,000 list requests, because you must issue a fifty-page walk for every calendar day the archive covers whether or not that day has anything in it, and you have no way to ask in advance which days are empty. The announcements chain has no finite figure at all: you can spend the same 48,000 requests and still not know what you missed, because the days that overflow are precisely the days with the most in them. Three collections on one host, and the cost of completeness runs from ten minutes to undefined.
That is a stronger result than yesterday's, and it changes the recommendation rather than just extending it. On the judgments chain, exhaustive traversal is expensive: partition by day, expect roughly 48,000 requests before you have touched a detail page. On the announcements chain, exhaustive traversal through the structured interface is not available at any price, because there is no slice small enough to guarantee it fits.
Two ways to run off the end, and only one of them looks like an error
The same host produces two different end-of-data signatures, and an ingestion pipeline that handles one correctly can be silently wrong about the other.
The capped chains fail loudly: HTTP 404, a small HTML body, a checksum you can assert against. Any loop that stops on a non-2xx status stops in the right place, having quietly collected 0.1 per cent of the collection while believing it reached the end.
The uncapped chain fails quietly: HTTP 200 with a 443-byte body and no rows. A loop that stops on error status never stops. A loop that counts successful responses as a health metric will report thousands of successes carrying nothing, and a run-quality dashboard built on status codes and response counts will be green for the entire duration. We asked for page 5,000 of a 1,448-page collection and were told, in the technical sense, yes.
The practical consequence is that the termination condition has to be derived from content — row count, or identifier novelty against what you already hold — and never from status. That sounds obvious written down. It is also the single most common way we have seen a corpus arrive short without anyone noticing, because the failure produces no error to investigate and no gap in the logs.
The same lesson applies one layer up. Both robots.txt and sitemap.xml on this host returned HTTP 404 with a 1,148-byte text/html body — the same size, on inspection, as the pagination error page. The status code is honest, so a well-behaved crawler treats crawl policy as absent and proceeds under its own rules; the absence of a file grants nothing and we do not read it as permission. But a crawler that keys on status alone and stores whatever body arrives has filed a web page where a policy document belongs, and will do the identical thing at the pagination wall. One parser bug, two silent corruptions.
The endpoint that has stayed down
One carried-over item closes today. The Supreme People's Court gazette host returned 502 on all three attempts when we inventoried the national endpoints on 30 August. We retested it today from a different session: 502 again, after 8.7 seconds. Three days apart, same result. That moves it out of the “transient, retest later” column and into the column of endpoints that a coverage plan should not assume. We are not speculating about why, and a fourth observation from one exit address is still one exit address.
What a buyer should take from this
The reason we keep publishing these measurements is not that the official layer is inadequate — it is a public register doing a public register's job, and it is not obliged to be an API. The reason is that the gap between “published” and “obtainable” is invisible from a procurement document, and it is where data budgets go to die.
Three things are worth carrying into a vendor conversation:
- Ask about coverage per collection, not per source. “We source from the national register” is compatible with holding 100 per cent of one of its chains and 0.1 per cent of another. On the evidence here, those two claims live on the same hostname.
- Ask how the pipeline decides it has reached the end. If the answer involves status codes, you have found a place where a corpus can arrive short with a clean run log. Ask to see a run where the count came back lower than expected and what happened next.
- Ask what the vendor cannot get, and when they last checked. Every number on this page is dated because every one of them is a measurement of somebody else's server on a particular afternoon. A supplier who cannot tell you the date of their last coverage check is quoting you a memory.
We hold ourselves to the same three. Our own corpus is thin in recent years, carries case-cause fields that are empty far more often than we would like, and contains court-name variants we have not finished normalising — all of which we document before anything is signed rather than after. And a page arguing that obtainability is hard to verify is a page that happens to favour the party selling verified access, which is us. Re-run the probes.
How to reproduce this
Nothing here needs a credential or a special client. Every measurement was a form-encoded POST to a list endpoint with a page number and, where relevant, a date range and a facet value taken from the site's own dropdown; every response was checked for row count, identifier set and body checksum rather than for status alone. The whole sequence is roughly ninety requests and takes about eight minutes at a polite interval.
If you run it and get different numbers, that is the interesting outcome and we would like to hear it. Both of the falsifiable claims on this page — that the announcements chain overflows its finest addressable cell, and that the debtor chain has no ceiling — rest on a single day of measurement from a single exit address. A quieter week would make the first one fail. That is not a caveat added for form; it is the reason we date everything.
Frequently asked questions
No, and we said otherwise on 31 August. Measured on 1 September 2026, the cap is applied per list chain, not per host. The judgment list and the announcement list both stop at page 50 and return HTTP 404 with an identical 1,148-byte body from page 51 onwards. The debtor list on the same host, reached through a different endpoint, served every page we asked for up to and including page 1,448, its own advertised last page. Same domain, same session, same request pattern, two different regimes.
By the site's own page counts: the judgment chain advertises 47,949 pages and serves 50, which is 0.104 per cent; the announcement chain advertises 90,746 pages and serves 50, which is 0.055 per cent; the debtor chain advertises 1,448 pages and serves all of them, which is 100 per cent. Every chain returns exactly ten rows per page and ignores pageSize, rows, limit and size parameters, so the reachable totals are 500 records, 500 records and 14,479 records respectively.
Sometimes, and it depends on publication density rather than on the filter. The cap applies to the page number absolutely: on a day with no announcements at all, page 1 returned an empty result with HTTP 200 while page 51 still returned 404. For the judgment chain, a single calendar day fits comfortably inside the 500-row window. For the announcement chain it does not always: 10 June 2025 filled page 50 with rows all dated that day, and page 51 returned 404. Adding the case-type facet — the only other structured filter the interface exposes — did not rescue it, because one case type on that one day still filled page 50.
Two different things on the same host, and only one of them is an error. Past the capped depth on the judgment and announcement chains you get HTTP 404 with a 1,148-byte HTML body. Past the genuine end of the debtor register — pages 1,449, 1,500, 2,000 and 5,000 — you get HTTP 200 with a 443-byte body containing no rows. The second is the more dangerous of the two for an ingestion pipeline, because a loop that stops on error status never stops, and a loop that counts successful responses records thousands of successes that carried nothing.
It changes nothing about permission and it should change something about your parser. Read before probing on 1 September 2026, both robots.txt and sitemap.xml on this host returned HTTP 404 with a 1,148-byte text/html body — the same size as the pagination error page. The status code is correct, so a well-behaved crawler treats crawl policy as absent and proceeds under its own rules rather than treating absence as a grant. A crawler that keys on status alone and stores the body has filed an HTML error page where a policy document should be, and will do the same thing at the pagination wall.
Bring us the collection you cannot finish walking.
SinoVerdict licenses PRC judgment data to teams building legal AI — bulk delivery, a REST API and an MCP endpoint over a 160M+ record corpus, English-indexed, with case-number citations to the original judgments and the known gaps written down before anything is signed. If you have a coverage question, send us the case numbers or the slice you cannot reconcile and we will run the comparison with you. Write to chenjiaxin@wenshucha.com or use the form.
Request trial access