# Paginering med side og per_side

> Hent lange lister side for side med side og per_side, kend grænserne pr. endpoint, og skriv en løkke, der stopper ved den sidste side.
>
> https://danadresse.dk/guides/paginering · verified 2026-11-02
Lister som `/adresser`, `/vejnavne` og `/postnumre` returnerer resultatet i sider. Du vælger siden med `side` og størrelsen med `per_side`, præcis som i DAWA. Hver side er et API-kald, så færre og større sider er billigere end mange små.

## Parametrene

- `side` er sidenummeret. Den første side er `1`. `side=0` giver en valideringsfejl.
- `per_side` er antal rækker pr. side. Standarden er 100 på de fleste lister. Se tabellen for, hvad du får uden de to parametre.

```bash id=page-curl run=live expect="adressebetegnelse"
curl -s "https://api.danadresse.dk/adresser?postnr=8000&side=2&per_side=2" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"
```

Svaret er en almindelig JSON-liste. Der er ingen header eller felt med det samlede antal, og ingen "næste side"-link. Du ved, at du er på den sidste side, når den har færre rækker end `per_side`.

## Grænser pr. endpoint

| Endpoint | Uden `side` og `per_side` | `per_side` maks. | `side` |
|---|---|---|---|
| `/adresser`, `/adgangsadresser` | de første 100 | 1000 | ja |
| `/postnumre`, `/sogne`, `/supplerendebynavne`, `/supplerendebynavne2` | hele listen | 1000 | ja |
| `/vejnavne`, `/vejstykker` | hele listen med `kommunekode` eller `postnr`, ellers de første 100 | 1000 | ja |
| `/navngivneveje`, `/ejerlav`, `/jordstykker`, `/matrikelnumre`, `/steder`, `/stednavne` | de første 100 | 1000 | ja |
| `/kommuner` | hele listen | 500 | ja |
| `/autocomplete` | 20 forslag | 100 | nej |
| `/adresser/autocomplete`, `/adgangsadresser/autocomplete`, `/postnumre/autocomplete`, `/vejnavne/autocomplete`, `/vejstykker/autocomplete`, `/ejerlav/autocomplete` | 20 forslag | 1000 | nej |
| `/kommuner/autocomplete` | 20 forslag | 500 | nej |
| `/regioner`, `/retskredse`, `/politikredse`, `/opstillingskredse`, `/landsdele`, `/storkredse`, `/valglandsdele` | hele listen | hele listen | nej |

Som i DAWA kommer de små lister hele tilbage, når du ikke sender `side` eller `per_side`. `/postnumre` giver alle postnumre i ét kald, klar til en dropdown. Sender du `side` eller `per_side`, er standarden 100 rækker pr. side. På referencelisterne i den sidste række bliver `side` og `per_side` ignoreret.

Sætter du `per_side` højere end grænsen, får du status 400:

```json id=per-side-too-big run=no
{
  "type": "QueryParameterFormatError",
  "title": "Invalid query parameter",
  "details": [
    {
      "type": "less_than_equal",
      "loc": ["query", "per_side"],
      "msg": "Input should be less than or equal to 1000",
      "input": "5000",
      "ctx": {"le": 1000}
    }
  ],
  "code": "invalid_query_parameter"
}
```

## Løkken: hent indtil en kort side

Opskriften er den samme i alle sprog: start på side 1, hent, og stop når siden har færre rækker end `per_side`. Går antallet op i `per_side`, er den sidste side tom, og det koster ét ekstra kald.

En generator i Python, der giver dig rækkerne en ad gangen:

```python id=paginate-python run=live expect="postnumre"
import os
from collections.abc import Iterator

import requests

API = "https://api.danadresse.dk"
session = requests.Session()
session.headers["X-Api-Key"] = os.environ["DANADRESSE_API_KEY"]


def pages(path: str, params: dict | None = None, per_side: int = 1000) -> Iterator[dict]:
    """Giv alle rækker fra en DAWA-liste, side for side."""
    side = 1
    while True:
        r = session.get(
            API + path,
            params={**(params or {}), "side": side, "per_side": per_side},
            timeout=30,
        )
        r.raise_for_status()
        rows = r.json()
        yield from rows
        if len(rows) < per_side:
            return
        side += 1


alle = list(pages("/postnumre"))
print(f"Hentede {len(alle)} postnumre. Første: {alle[0]['nr']} {alle[0]['navn']}")
```

En async iterator i JavaScript til Node 18+. Eksemplet henter alle kommuner i sider på 50:

```js id=paginate-js run=live expect="kommuner"
const API = "https://api.danadresse.dk";

export async function* pages(path, params = {}, perSide = 1000) {
  for (let side = 1; ; side++) {
    const url = new URL(API + path);
    for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
    url.searchParams.set("side", String(side));
    url.searchParams.set("per_side", String(perSide));

    const res = await fetch(url, {
      headers: { "X-Api-Key": process.env.DANADRESSE_API_KEY },
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
    const rows = await res.json();
    yield* rows;
    if (rows.length < perSide) return;
  }
}

let antal = 0;
for await (const kommune of pages("/kommuner", {}, 50)) antal++;
console.log(`Hentede ${antal} kommuner`);
```

Vil du afbryde tidligt, så stop løkken. Generatoren henter kun den næste side, når du beder om flere rækker.

## Ting, der er værd at vide

- **Rækkefølge.** Lister uden `q` har en fast rækkefølge: `/adresser` og `/adgangsadresser` efter id, de andre lister efter kode eller navn. På `/adresser` og `/adgangsadresser` overlapper siderne derfor ikke og springer ingen rækker over. Adressedata bliver opdateret løbende, så til et komplet udtræk bruger du replikering.
- **Fritekst `q`.** Med `q` er siderne sorteret efter relevans. Dybe sider giver sjældent mening. Gør søgningen mere præcis i stedet.
- **Cirkel og polygon.** Med `cirkel=` eller `polygon=` bliver kandidaterne fundet først og derefter delt i sider, de nærmeste først i en cirkel. Dækker området mere end 50.000 adgangsadresser, får du `400` med koden `area_too_large` i stedet for et afskåret svar. Gør området mindre, eller tilføj `postnr` eller `kommunekode`.
- **Hver side er et kald.** 2.500 adresser med `per_side=100` er 25 kald. Med `per_side=1000` er det 3. Se [Grænser, kvoter og 429](/guides/graenser-og-429).

## Når du skal bruge alt: replikering

Paginering er beregnet til lister, som en bruger eller et job har brug for nu: adresserne i et postnummer, vejene i en kommune. Skal du bruge alle Danmarks adresser, så hent dem med `/replikering/udtraek?entitet=adresse`. Det er én lang JSON-liste i ét kald (eller NDJSON, én række pr. linje, med `&ndjson`), og bagefter kan du holde dine data opdateret med hændelser. Se [Replikering: det første udtræk](/guides/replikering-foerste-udtraek).

`format=ndjson` på `/adresser` giver også NDJSON, men det følger stadig `side` og `per_side`. Det er praktisk til at skrive en side direkte til en fil, men det henter ikke hele datasættet.

## Næste skridt

- [Formater: JSON, CSV, GeoJSON og NDJSON](/guides/formater-json-csv-geojson)
- [Replikering: det første udtræk](/guides/replikering-foerste-udtraek)
- [Ingen resultater?](/guides/ingen-resultater)
