fonciqAPI
EnGuides

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.

Ouvrir

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. The reference pages are in French.

Before you start

  • An API key: see Premier appel (French). A fq_test_ key bills nothing.
  • Your organisation's own Registre foncier account, added in Studio › Développeurs. 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.

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:

{ "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.

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}.

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

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

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

FieldContent
inscriptionRegistration number, spaces kept.
presentation_datePresentation date, YYYY-MM-DD.
natureNature of the deed. Null when the column was not read.
parties{ "role", "name" }, when the capacity is recognised.
radiationsCancellation mentions as registered: inscription, mention, raw_text.
confidenceAlways the literal unscored: the current parser does not produce a score. Leave it as unscored.
source_document_idIdentifier 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

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

Next

Sur cette page