Metadata-Version: 2.4
Name: inpi-patents
Version: 0.1.0
Summary: Fetch, parse and analyse French patent data from INPI's FTP server and API PI Diffusion.
Author: Muhammad Aliyu
License-Expression: MIT
Project-URL: Documentation, https://maliyuam.com/inpi-patent-harvester/
Project-URL: Source, https://github.com/maliyuam/inpi-patent-harvester
Project-URL: Issues, https://github.com/maliyuam/inpi-patent-harvester/issues
Keywords: INPI,patents,ST36,FTP,France,inventors,open data
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: lxml>=5.0
Requires-Dist: pandas>=2.2
Requires-Dist: pyarrow>=15
Requires-Dist: duckdb>=1.0
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13
Provides-Extra: ui
Requires-Dist: fastapi>=0.110; extra == "ui"
Requires-Dist: uvicorn>=0.29; extra == "ui"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: httpx2>=0.1; extra == "dev"
Requires-Dist: fastapi>=0.110; extra == "dev"
Requires-Dist: uvicorn>=0.29; extra == "dev"
Requires-Dist: mkdocs-material>=9.5; extra == "dev"
Dynamic: license-file

# inpi-patents

**French patent data, from INPI's servers to analysis-ready tables.**

[![CI](https://github.com/maliyuam/inpi-patent-harvester/actions/workflows/ci.yml/badge.svg)](https://github.com/maliyuam/inpi-patent-harvester/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/badge/docs-maliyuam.com-blue)](https://maliyuam.com/inpi-patent-harvester/)
[![License: MIT](https://img.shields.io/badge/code-MIT-green.svg)](https://github.com/maliyuam/inpi-patent-harvester/blob/main/LICENSE)
![Python](https://img.shields.io/badge/python-3.11%2B-blue)

INPI, the French patent office, publishes the full French patent register
as WIPO ST36 XML: bibliography, legal status, and the parties on each
document, **including inventors with city and postcode**. Getting from
"the data exists" to "I have a table" involves a licence form, an FTP
server with no English documentation, a cookie-authenticated API with a
small quota, and an XML dialect with a few silent traps.

This package handles the whole path:

- **FTP route** (bulk): map the server, download the full-register
  snapshot (~3.5 GB, ~2.5 million documents) and the weekly update files.
  Resumable, size-verified, restartable.
- **API route** (targeted): log in to API PI Diffusion, fetch notices, run
  searches, harvest small sets without fighting the quota by hand.
- **One parser** for both: every element of the notice, with the known
  source defects repaired and flagged rather than silently propagated.
- **Geography built in**: postcode to département to NUTS-3, Corsica and
  overseas handled, no geocoder needed.
- **A CLI and a local dashboard** that drive the same steps.

**Documentation: https://maliyuam.com/inpi-patent-harvester/** — from
requesting access to using the tables. The same pages live in [`docs/`](https://github.com/maliyuam/inpi-patent-harvester/blob/main/docs/index.md);
`mkdocs serve` browses them locally.

## Sixty seconds, no credentials

```bash
pip install inpi-patents
inpi parse file tests/fixtures/FR_SYNTHETIC_MODERN.xml
```

The synthetic record exercises every trap the parser handles: inverted
city/postcode, duplicated party renderings, the filing date hidden behind
INPI-specific element names, priorities that move the earliest filing
date, a foreign inventor whose five-digit code is not a French postcode.

## The real thing

```bash
inpi env init                                 # write .env, fill in the codes INPI emailed you
inpi ftp probe --cred NOTICES                 # what your licence can see
inpi ftp fetch /Backlog --cred NOTICES        # the snapshot, resumable
inpi parse zips data/raw/ftp/Backlog          # archives -> parquet parts, parallel
inpi merge                                    # parts -> one parquet per table, QA numbers
inpi geo                                      # inventors_geo.parquet with département + NUTS-3
inpi ui                                       # or do all of the above from a browser
```

```python
import duckdb
duckdb.sql("""
    SELECT app_year, dept, count(*) AS inventors
    FROM 'data/export/inventors_geo.parquet'
    WHERE app_year BETWEEN '2000' AND '2020' AND dept IS NOT NULL
    GROUP BY 1, 2 ORDER BY 1, 3 DESC
""").show()
```

## What you get

| Table | One row per |
|---|---|
| `documents` | publication: numbers, dates, kind, title, abstract, family id, earliest filing date |
| `parties` | document × party × rendering: inventors, applicants, owners, agents, licensees, with addresses |
| `priorities`, `classifications`, `related`, `legal_events` | document × item |
| `inventors_geo` | document × inventor, de-duplicated, swap-repaired, with `dept` and `nuts3` |

On a mid-2025 snapshot: 2.52 million documents, zero parse errors,
~316,000 filings in 2000-2020 of which 99.5% carry an inventor and 84% of
inventor slots resolve to a département (99.6% of French-resident ones).

## Getting access

The FTP route needs a free licence from INPI: fill in the database
selection form on INPI's data portal and email it to their licence desk;
access codes come back by email, one login per database. The API route
needs a technical account on data.inpi.fr with *APIs PI Access* activated.
The docs walk through both, and include a bilingual request letter
template: [Getting access](https://maliyuam.com/inpi-patent-harvester/guide/getting-access/).

## Code is MIT. Data is INPI's.

This repository contains code, documentation and **synthetic** test
fixtures only. Data obtained from INPI is covered by INPI's reuse licence:
access codes are personal, raw data is not redistributable, and INPI must
be cited. `.gitignore` keeps `data/`, archives, XML, parquet and `.env` out
of the repository. See [Licence and redistribution](https://maliyuam.com/inpi-patent-harvester/reference/licence/).

## Development

```bash
git clone https://github.com/maliyuam/inpi-patent-harvester.git && cd inpi-patent-harvester
pip install -e ".[dev,ui]"
pytest                    # no network, synthetic fixtures only
ruff check src tests
mkdocs serve              # docs at http://127.0.0.1:8000
```

See [CONTRIBUTING.md](https://github.com/maliyuam/inpi-patent-harvester/blob/main/CONTRIBUTING.md). Not affiliated with INPI.
