# JORT API Documentation

Complete mirror and full-text search for the Tunisian Official Gazette
(Journal Officiel de la République Tunisienne) from 1957 to 2026.

## Domains

- `jort.tn` — Search UI
- `api.jort.tn` — Search + OCR API
- `lake.jort.tn` — PDF data lake (22,395 files)
- `ocr.jort.tn` — OCR'd markdown (French only so far)
- `index.jort.tn` — Catalog/index JSON
- `docs.jort.tn` — This file

## Collections

Three types of documents, each identified by a collection id:

| Collection ID | Languages | Years | Description |
|---|---|---|---|
| `journal-officiel` | `ar`, `fr` | 1957–2026 | Laws, presidential decrees, government decisions |
| `tribunal-immobilier` | `ar` | 2000–2026 | Real estate tribunal announcements |
| `annonces-legales` | `ar`, `fr` | 2004–2026 | Legal, sharia and judicial announcements |

## URL patterns

All files use 3-digit zero-padded issue numbers (e.g. `001`, `035`, `156`).

### PDF (original)
```
https://lake.jort.tn/{collection}/{language}/{year}/{issue}.pdf
```

### OCR'd markdown
Replace `lake.jort.tn` with `ocr.jort.tn` and `.pdf` with `.md`:
```
https://ocr.jort.tn/{collection}/{language}/{year}/{issue}.md
```

The markdown contains page markers: `<!-- page:N -->` separates each page of
the original PDF. Pages are in original document order.

### Examples
```
https://lake.jort.tn/journal-officiel/fr/2026/001.pdf
https://ocr.jort.tn/journal-officiel/fr/2026/001.md
https://lake.jort.tn/annonces-legales/ar/2020/050.pdf
```

## Search API

### Endpoint
`GET https://api.jort.tn/search`

### Query parameters
| Param | Required | Description |
|---|---|---|
| `q` | yes | Search query. Wrap in `"..."` (URL-encoded as `%22`) for exact phrase match. |
| `collection` | no | One of `journal-officiel`, `tribunal-immobilier`, `annonces-legales` |
| `lang` | no | `fr` or `ar` |
| `year` | no | Exact year filter |
| `year_from` | no | Range start (inclusive) |
| `year_to` | no | Range end (inclusive) |
| `sort` | no | `relevance` (default, BM25), `newest`, or `oldest` |
| `limit` | no | Max results per page. Default 20, max 100. |
| `offset` | no | Pagination offset |

### Response
```json
{
  "query": "Haddad",
  "filters": { "collection": null, "language": null, "year": null, "yearFrom": null, "yearTo": null },
  "total": 6789,
  "limit": 20,
  "offset": 0,
  "results": [
    {
      "collection": "journal-officiel",
      "language": "fr",
      "year": 1982,
      "issue": "077",
      "page": 25,
      "pdf_url": "https://lake.jort.tn/journal-officiel/fr/1982/077.pdf",
      "md_url": "https://ocr.jort.tn/journal-officiel/fr/1982/077.md",
      "snippet": "...Monsieur <mark>Adel</mark> <mark>Haddad</mark> est nommé gér..."
    }
  ]
}
```

### Notes
- Snippets contain `<mark>...</mark>` tags around matches.
- The search uses FTS5 with the **trigram tokenizer**, meaning it matches 3-character
  sequences regardless of word boundaries. Queries match substrings.
- For name searches, always use quoted phrase search: `q="Adel Haddad"`.
- Currently only French documents are indexed (Arabic OCR pending).

### Examples
```
# Simple keyword
https://api.jort.tn/search?q=loi

# Exact phrase
https://api.jort.tn/search?q=%22Adel+Haddad%22

# Year range + collection
https://api.jort.tn/search?q=Haddad&collection=annonces-legales&year_from=2015&year_to=2020

# Newest first
https://api.jort.tn/search?q=VEPA1&sort=newest&limit=50
```

## Facets (aggregation counts)

### Endpoint
`GET https://api.jort.tn/search/facets`

Takes the same filter params as `/search` plus `by`:

| Param | Description |
|---|---|
| `by` | One or more of `year`, `collection`, `language` — comma-separated |

### Response
```json
{
  "query": "Haddad",
  "total": 6789,
  "by_year": { "1957": 13, "1958": 37, ..., "2026": 22 },
  "by_collection": { "journal-officiel": 4521, "annonces-legales": 3311 }
}
```

### Performance note
Facets can be significantly slower than `/search` for queries with many
matches (~2-15× depending on match count). The UI streams them separately.

## Deep-linking into PDFs

To link a user directly to the page/match, use the self-hosted PDF.js viewer:

```
https://jort.tn/pdfjs/web/viewer?file={ENCODED_PDF_URL}#page={N}&search={TERM}[&phrase=true]
```

- `file` — URL-encoded PDF URL (must be `lake.jort.tn`).
- `page` — page number to jump to.
- `search` — term to highlight.
- `phrase=true` — treat search as exact phrase.

Example:
```
https://jort.tn/pdfjs/web/viewer?file=https%3A%2F%2Flake.jort.tn%2Fjournal-officiel%2Ffr%2F1982%2F077.pdf#page=25&search=Adel%20Haddad&phrase=true
```

## Catalog / Index

### Full index
`GET https://index.jort.tn/` — lightweight: per-collection URL patterns +
available issues counts per year.

### Full catalog
`GET https://index.jort.tn/catalog` — heavy: every PDF URL + file size.

## Update schedule

PDFs are re-fetched from iort.tn every 20 minutes via a Cloudflare cron worker.
New French PDFs are automatically OCR'd and indexed for search within a few
minutes of publication.

## Contact

Source: <https://www.iort.tn>
