# How to extract Quebec land registry index pages with the Fonciq API

Order an index, follow the job, read the rows as JSON or CSV. cURL, Python and Node examples.

An index of immovables is ordered, followed and read in three calls. Each filing in the index becomes a structured row (registration number, presentation date, nature, parties, cancellations as registered). The source PDF always comes back with the rows and is the authoritative record.

This page is an API guide. The overview of the Fonciq API, its operations and published limits is on [fonciq.ca](https://fonciq.ca/en/quebec-land-registry-api). The reference pages are in French.

## Before you start

- An API key: see [Premier appel](/plateforme/demarrage) (French). A `fq_test_` key bills nothing.
- Your organisation's own Registre foncier account, added in [Studio › Développeurs](https://fonciq.ca/studio/developpeurs). Without an account, no index can be ordered.

## 1. Order the index

`POST /v1/registre/requests` (scope `registre:write`) requires an `Idempotency-Key` header, kept for 24 hours. Retrying with the same key returns the same response.

```bash
curl -X POST https://api.fonciq.ca/v1/registre/requests \
  -H "Authorization: Bearer $FONCIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f1c0b2e-7a4d-4e1a-9c2b-0d5e6f7a8b9c" \
  -d '{"lot":"1234567","documents":["index"]}'
```

The response is `202`:

```json
{ "job_id": "job_01HZX", "account_id": "rfa_exemple" }
```

## 2. Follow the job

The lookup is asynchronous. `GET /v1/jobs/{id}` (scope `jobs:read`) returns the status: `queued`, `capacity_wait`, `running`, `succeeded` or `failed`. `capacity_wait` means the account's daily cap is still holding the request back. The account's capacity is in the same response.

```bash
curl https://api.fonciq.ca/v1/jobs/job_01HZX \
  -H "Authorization: Bearer $FONCIQ_API_KEY"
```

Your key is limited to 60 requests per minute, so poll at a reasonable interval.

## 3. Read the rows

When the job is `succeeded`, the result is on `result.index` (one block per lot for a batch). It carries `pdf_url`, `entries_url` and `entries`. The same rows can be read directly with `GET /v1/documents/{id}/entries`, as JSON by default or CSV with `format=csv`. The source PDF is `GET /v1/documents/{id}`.

```bash
curl "https://api.fonciq.ca/v1/documents/42/entries?format=csv" \
  -H "Authorization: Bearer $FONCIQ_API_KEY"
```

### Python

```python
import os, time, uuid, requests

BASE = "https://api.fonciq.ca"
HEADERS = {"Authorization": f"Bearer {os.environ['FONCIQ_API_KEY']}"}

job = requests.post(
    f"{BASE}/v1/registre/requests",
    headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
    json={"lot": "1234567", "documents": ["index"]},
).json()

while True:
    state = requests.get(f"{BASE}/v1/jobs/{job['job_id']}", headers=HEADERS).json()
    if state["status"] in ("succeeded", "failed"):
        break
    time.sleep(10)

if state["status"] == "succeeded":
    for entry in state["result"]["index"]["entries"]:
        print(entry["inscription"], entry["presentation_date"], entry["nature"])
```

### Node

```js
const BASE = 'https://api.fonciq.ca';
const headers = { Authorization: `Bearer ${process.env.FONCIQ_API_KEY}` };

const created = await fetch(`${BASE}/v1/registre/requests`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({ lot: '1234567', documents: ['index'] }),
}).then((r) => r.json());

let job;
do {
  await new Promise((resolve) => setTimeout(resolve, 10_000));
  job = await fetch(`${BASE}/v1/jobs/${created.job_id}`, { headers }).then((r) => r.json());
} while (!['succeeded', 'failed'].includes(job.status));

if (job.status === 'succeeded') {
  for (const entry of job.result.index.entries) console.log(entry.inscription, entry.nature);
}
```

## What the rows contain

| Field | Content |
| --- | --- |
| `inscription` | Registration number, spaces kept. |
| `presentation_date` | Presentation date, `YYYY-MM-DD`. |
| `nature` | Nature of the deed. Null when the column was not read. |
| `parties` | `{ "role", "name" }`, when the capacity is recognised. |
| `radiations` | Cancellation mentions as registered: `inscription`, `mention`, `raw_text`. |
| `confidence` | Always the literal `unscored`: the current parser does not produce a score. Leave it as `unscored`. |
| `source_document_id` | Identifier of the source PDF. |

The PDF is the authoritative record. An empty `entries` list is an empty read: no row is invented to fill it, and rows with no date or no registration number are set aside.

## Cost

Each index delivered costs CAD 0.99 excluding Registre fees. An index always consults the Registre, which charges CAD 1.50 to the account holder, so one consulted index costs CAD 2.49 in total. The Registre fee is not a Fonciq charge. A `fq_test_` key bills nothing.

## Common errors

| Code | Cause |
| --- | --- |
| `401` | Missing or revoked key, or missing the `registre:write` or `jobs:read` scope. |
| `400` | Invalid body, or a `format` other than `csv` on a rows read. |
| `409` | No Registre account: `rf_account_required`. |
| `404` | The document is not an index produced for your organisation. |
| `429` | More than 60 requests per minute (`rate_limited`): wait for `Retry-After`. |

## Next

- Many lots at once: [Bulk index search by CSV](/en/guides/bulk-index-search).
- Be told about a new filing: [Land registry filing webhooks](/en/guides/registry-webhooks).
