# Devnors Data Python SDK: one API key for Chinese legal, company and index data

> The devnors-data package wraps a single POST /v1/data/query gateway behind typed Python helpers and an async client, aimed at AI agents and analysts who need Chinese legal, enterprise and content data without wiring up dozens of scrapers.

**DevnorsAI/devnors-data-python** — Devnors Data - Python SDK: 接口,数据API,裁判文书,Legal Case,法律法规,法条,Law Article,企业工商,Company Registry,企业年报,Annual Report,税号开票,Tax Invoice,失信核查,Dishonest Judgment Debtor,被执行人,Enforcement Debtor,关键词指数,Keyword Index,微信指数,WeChat Index,热搜榜,Hot Rank,微博热搜,抖音热搜,快递查询,Express Tracking,快递公司编号,统一查询,/v1/data/query,API Key,MCP,AI Agent

- Repository: https://github.com/DevnorsAI/devnors-data-python
- Stars: 521 · Forks: 3
- Language: Python
- License: MIT
- Published: 2026-09-11 · Updated: 2026-09-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/devnorsai-devnors-data-python

## What devnors-data actually wraps

The package is a client, not a dataset. Everything it returns comes from a hosted gateway at data.devnors.com, reached through one endpoint, POST /v1/data/query. The README frames the design goal plainly: call legal cases, laws, enterprise data, content indexes, academic research, express tracking and more through a single gateway, with official-source traceability where applicable.

The intended audience is narrow and identifiable. If you are building an AI agent or an internal tool that needs Chinese legal judgments, company registration records, dishonest judgment debtor checks, Weibo or Douyin hot-rank items, or academic paper and patent lookups, you would otherwise be stitching together several vendors with different auth schemes, response shapes and rate-limit semantics. This SDK collapses that into one key and one error model. If your data needs are Western or your queries are generic web search, the domain table offers little that is not better served elsewhere.

## The domain and type table is the real API surface

The most useful part of the README is the table mapping domain to type to status. Legal covers case, law_article and law_catalog. Content covers keyword_index, suggest_list, keyword_word, wechat_index_v2 and hot_rank. Enterprise is by far the largest group, spanning company_detail_v2, annual_report_list, annual_report_detail, account_open, company_tag, same_legal_company, key_person, shareholder, branch_org, industrial_commercial_change, taxpayer_basic, tax_credit_level, tax_illegal, tax_illegal_major, tax_illegal_major_detail, operation_except, admin_punishment, judgment_list, court_notice_list, court_trial_list, cases_info_list, termination_case_list, serious_illegal, exec_person, breach_of_trust, listed_company and listed_company_neeq. Cloud holds express, express_com, web_search and invoice_ocr. Research holds paper_search, patent_search, journal_search, paper_detail, patent_detail, journal_detail, scholar_search and scholar_detail.

Every row is marked live. That is worth noting because the table is the only place the README enumerates capability. There is no per-type schema documentation in the repository, so the shape of a company_detail_v2 hit versus a shareholder hit has to be discovered by calling the API. Treat the table as a coverage list, not a contract.

## Installing devnors-data and running a first legal query

The package requires Python 3.9 or newer and depends only on httpx. Install it from PyPI:

```bash
pip install devnors-data
```

Create an API key in the developer console at data.devnors.com/console. The key can be passed to the constructor or supplied through the DEVNORS_API_KEY environment variable. The README shows the constructor form with a live key prefix:

```python
from devnors_data import DevnorsData

client = DevnorsData(api_key="devnors_sk_live_xxx")  # or set DEVNORS_API_KEY

res = client.legal_cases("民间借贷 利息", top_k=10)
for hit in res["hits"]:
    print(hit.get("case_no"), hit.get("title"))
```

Each element in res["hits"] is a dictionary; the README reads case_no and title from it with .get(), which suggests fields are not guaranteed present on every hit. For law articles the helper is legal_laws, and for anything not covered by a named helper there is the unified entry point:

```python
res = client.query(domain="legal", type="case", query="劳动争议", top_k=5)
print(res["units"], "tokens used")
```

The query method takes domain, type, query and top_k, and the response carries units, the token count charged for the call. An async variant exists for concurrent workloads:

```python
import asyncio
from devnors_data import AsyncDevnorsData

async def main():
    client = AsyncDevnorsData(api_key="devnors_sk_live_xxx")
    res = await client.legal_cases("交通事故 责任认定")
    print(res["total_hits"])

asyncio.run(main())
```

If you run against a private or self-hosted deployment, point the client at it with DEVNORS_DATA_BASE_URL, which the README shows defaulting to https://data.devnors.com.

## Retries, pagination and the error contract

The SDK retries 429, 5xx and connection errors with bounded exponential backoff. It never retries 400, 401, 402 or 501. You can turn retries off entirely with DevnorsData(..., max_retries=0), which is the right move inside a request handler where you would rather fail fast than hold a worker for the length of a backoff schedule.

The pagination helper walks pages using total_hits from the response:

```python
for hit in client.paginate("legal", "case", "借贷纠纷", page_size=20, max_items=100):
    print(hit.get("case_no"))
```

That max_items ceiling matters. Pagination against a metered API is a spend decision, and the helper makes the ceiling explicit rather than leaving it to a loop you write yourself.

The error model is the strongest part of the design. Every DevnorsDataError carries code, retryable, next_action and request_id. Insufficient balance surfaces as code="insufficient_balance" with status 402; rate limiting surfaces as code="rate_limited" with 429. Because retryable is a field on the exception, your retry logic can read the SDK's own judgement instead of reimplementing a status-code table. Keep request_id in your logs; it is the only handle the README gives you for tracing a disputed charge back to a specific call.

## Where this is the wrong tool

Three constraints deserve attention before you build on it.

First, there is no offline or self-hosted data path in the README. DEVNORS_DATA_BASE_URL points the client at a different gateway, which is useful if you run your own proxy, but the underlying records still come from Devnors. If your compliance posture forbids sending queries to a third party, this SDK is the wrong layer entirely.

Second, billing is per unit and there is no documented free tier. A 402 is a hard stop, not a soft warning, and the README does not describe a sandbox or test mode. You cannot estimate cost from the repository alone; you have to call the API and read units from a real response.

Third, the response schemas are undocumented here. The README shows hits, total_hits and units, and demonstrates .get() access, but it does not describe the fields inside a shareholder record or a patent_detail result. Teams that need stable, typed models for downstream validation will be writing their own wrappers, and those wrappers will break silently when the gateway changes a field. The SDK does not appear to ship typed response objects.

## How this differs from scraping or a general search API

The obvious alternative is building your own collectors against the public sources: court announcement sites, the national enterprise credit system, hot-rank pages, parcel carrier endpoints. That approach gives you full control over fields and no per-call cost, and for a single narrow dataset it is often the cheaper path. The difference is maintenance. Chinese court and registry sites change markup, add verification steps and rate-limit aggressively, and every one of those changes lands on you. The SDK trades that ongoing work for a metered bill and a fixed domain table.

A second alternative is a general web search API. Those return ranked pages, not structured legal or registry records. If you need a case number and a title, a search API gives you a URL to parse; devnors-data returns case_no and title directly. If you need open-ended discovery rather than a specific record type, the search API is the better fit, and the cloud domain here does include web_search if you want both behind one key.

## Maintenance, licence and upgrade cost

The repository is MIT licensed, with the licence declared both in LICENSE and in pyproject.toml as license = { text = "MIT" }. That permits commercial use, modification and redistribution of the client code. It does not grant any rights to the data the gateway returns; the README does not state terms for the underlying records, and those terms live with the service, not the package. Read them separately before shipping a product that redistributes results.

The last push to main was on 2026-08-13. The repository is not archived. There are no retrieved releases, and the version is dynamic, read from devnors_data.__version__, so there is no changelog in the repository to consult before upgrading. The dependency surface is small, httpx>=0.24 with setuptools>=68 and wheel for the build, which keeps upgrade risk low on the packaging side. The real upgrade risk is server-side: because response schemas are not documented or typed, a gateway change can alter your output without any version bump in the package you installed. Pin the version you test against and keep a fixture of a known-good response to diff against.

## Conclusion

Adopt devnors-data if your workload is Chinese legal, enterprise registry, content index or academic lookups and you would rather pay per unit than maintain scrapers or sign per-dataset contracts. Do not adopt it if you need on-premise data, a free tier, or a dataset the table does not list as live. Before committing, verify three things: that your account balance covers your expected call volume, since a 402 raises DevnorsDataError with code insufficient_balance, that the specific domain and type pair you need appears in the README table, and that the units field returned by a trial query matches your budget model. The gateway is the product; the SDK is a thin, correct client for it, and the MIT licence means the client itself is never the thing holding you back.

## FAQ

### How do I install the Devnors Data Python SDK?

Install it from PyPI with pip install devnors-data. It requires Python 3.9 or newer, and the only runtime dependency declared in pyproject.toml is httpx>=0.24.

### How does the Devnors Data Python SDK authenticate requests?

You create an API key in the developer console at data.devnors.com/console, then either pass it as DevnorsData(api_key="devnors_sk_live_xxx") or set the DEVNORS_API_KEY environment variable. A private or self-hosted base URL can be set with DEVNORS_DATA_BASE_URL.

### Which data domains does the Devnors Data Python SDK cover?

The README lists four live domains: legal (case, law_article, law_catalog), content (keyword_index, suggest_list, keyword_word, wechat_index_v2, hot_rank), enterprise (company, annual report, shareholder, tax, enforcement and related types), cloud (express, express_com, web_search, invoice_ocr) and research (paper, patent, journal and scholar search and detail types).

### What happens when my Devnors Data balance runs out?

An insufficient balance raises DevnorsDataError with code="insufficient_balance" and status 402. The README states that 402 is not retryable, so the SDK will not retry the call automatically.

## Sources

- [DevnorsAI/devnors-data-python on GitHub](https://github.com/DevnorsAI/devnors-data-python)
- [Issues](https://github.com/DevnorsAI/devnors-data-python/issues)
- [License: MIT](https://github.com/DevnorsAI/devnors-data-python/blob/main/LICENSE)
- [README](https://github.com/DevnorsAI/devnors-data-python/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/devnorsai-devnors-data-python
