# Batch og eksport

Slå op til 50 ejendomme, adresser eller områder op i ét kald, eller lad en asynkron eksport hente op til 5.000 og levere dem som én fil.

## Batch-endpoints

| Endpoint | Body | Plan |
| --- | --- | --- |
| [POST /v1/bbr/ejendom:batch](https://grundfast.dk/docs/api/bbr-ejendom-batch) | `{ "bfe": [5651067, …] }` | Pro |
| [POST /v1/matrikel/ejendom:batch](https://grundfast.dk/docs/api/matrikel-ejendom-batch) | `{ "bfe": [ … ] }` | Pro |
| [POST /v1/adresse:batch](https://grundfast.dk/docs/api/adresse-batch) | `{ "uuid": [ … ] }` | Pro |
| [POST /v1/dagi:batch](https://grundfast.dk/docs/api/dagi-batch) | `{ "tema": "kommune", "kode": ["0101", …] }` | Pro |
| [POST /v1/datavask/adgangsadresser:batch](https://grundfast.dk/docs/api/datavask-batch) | `{ "betegnelser": [ … ] }` | Pro |
| [POST /v1/dhm/kote:batch](https://grundfast.dk/docs/api/dhm-kote-batch) | `{ "punkter": [{ "lon", "lat", "id" }] }` | Alle planer |

- Højst **50** id’er pr. kald. Dubletter slås sammen.
- Kaldet tæller **ét kald pr. unikt id** mod kvoten — en batch er ikke billigere end enkeltopslag, men hurtigere og færre forespørgsler. Kun de id’er, der kunne slås op, faktureres som forbrug.
- Batch kræver Pro-planen; på en lavere plan svarer API’et `402`, og kaldet tæller ikke. `dhm/kote:batch` er undtaget og virker på alle planer, fordi den prissættes præcis som de enkelte koter.

## Delvise fejl

Et batch-kald svarer `200`, også når nogle id’er fejler. Hvert id lander enten i `results` eller i `errors` med sin egen status, så ét ukendt BFE ikke vælter resten. Kun en ugyldig body giver `400` for hele kaldet.

```json
{
  "results": [
    { "bfe": 5651067, "ejendom": { "type": "ejendom", … }, "stale": false }
  ],
  "errors": [
    { "bfe": 999999999, "error": "property not found", "status": 404 }
  ],
  "requested": 2,
  "succeeded": 1,
  "failed": 1
}
```

- Matriklen, adresse og DAGI har samme form; nøglen i hvert element er `bfe`, `uuid` eller `kode`, og DAGI-svaret gentager `tema`.
- Datavask har ingen `errors`: hver adressestreng får et svar i `results` i samme rækkefølge, og en streng uden match får `kategori: "C"` og `antal: 0`.
- `dhm/kote:batch` svarer med `punkter` og `fejl` samt `forespurgt`, `besvaret` og `fejlet`. Et punkt, der ikke kunne besvares, står i `fejl` — aldrig som en kote på `null` blandt de andre. Giv punkterne et `id`, så du kan parre svarene med dine hjørner.
- `stale: true` på et element betyder, at netop det blev leveret fra vores cachede kopi. Se [Svar, null og friskhed](https://grundfast.dk/docs/svar-og-headers#friskhed).

## Med SDK’et

```ts
import { GrundfastClient } from '@grundfast/sdk';

const gf = new GrundfastClient({ apiKey: 'gf_live_...' });

// Op til 50 i ét kald — delvise fejl i errors:
const { results, errors } = await gf.ejendomBatch([5651067, 999999999]);
for (const e of errors) console.warn(e.bfe, e.status, e.error);

// En vilkårligt lang liste: SDK'et deler den i kald á 50 og giver ejendommene én ad gangen.
// BFE'er, der fejler, springes over — brug ejendomBatch, hvis du skal se dem.
const mineBfeer: number[] = [5651067 /* … */];
for await (const { bfe, ejendom } of gf.ejendomStream(mineBfeer)) {
  console.log(bfe, ejendom.antal_bygninger);
}

const matrikler = await gf.matrikelBatch([5651067]);
const adresser = await gf.adresseBatch(['0a3f509b-eeab-32b8-e044-0003ba298018']);
const kommuner = await gf.dagiBatch('kommune', [101, 851]);
const vasket = await gf.datavaskBatch(['raadhuspladsen 1 saeby', 'Vejlebyvej 17 Vodskov']);
const koter = await gf.dhmKoter([
  { lon: 10.2036, lat: 56.1577, id: 'hjørne-a' },
  { lon: 10.2038, lat: 56.1578, id: 'hjørne-b' },
]);
```

SDK’et prøver batch-kald igen ved midlertidige fejl, fordi de er sikre at gentage: et kald, der endte i en serverfejl, er lagt tilbage i kvoten.

## Asynkron eksport

Til større lister findes en eksport, der kører i baggrunden og leverer én fil. Den kræver Scale-planen.

| Egenskab | Værdi |
| --- | --- |
| Registre | `bbr` og `matrikel` (liste af `bfe`), `adresse` (liste af `uuid`) |
| Formater | `csv`, `ndjson`, `geojson` |
| Størrelse | Højst 5.000 id’er pr. job |
| Forbrug | Ét kald pr. unikt id, trukket ved oprettelsen. Id’er, der ikke kunne hentes, krediteres tilbage. |
| Opbevaring | Filen gemmes i en periode efter jobbet er færdigt; `resultAvailable` siger, om den stadig kan hentes. |

1. **Opret jobbet** — [POST /v1/export](https://grundfast.dk/docs/api/export-create) med register, format og id’er. Svaret er `201` med `{ "job": { "id", "status": "queued", … } }`.

```bash
curl https://api.grundfast.dk/v1/export \
  -H "Authorization: Bearer gf_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "register": "bbr", "format": "csv", "bfe": [5651067] }'
```

2. **Vent på at det bliver færdigt** — Hent [GET /v1/export/:id](https://grundfast.dk/docs/api/export-get), indtil `status` er `completed` (eller `failed`). `requested`, `succeeded` og `failed` viser, hvor mange id’er der blev hentet.

3. **Hent filen** — [GET /v1/export/:id/download](https://grundfast.dk/docs/api/export-download) returnerer filen. Er jobbet ikke færdigt endnu, svarer den `409`.

```ts
const job = await gf.createExport({ register: 'bbr', format: 'csv', bfe: mineBfeer });

let status = job;
while (status.status === 'queued' || status.status === 'processing') {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  status = await gf.getExport(job.id);
}
if (status.status === 'failed') throw new Error(status.error ?? 'eksporten fejlede');

const csv = await gf.downloadExport(job.id); // string
console.log(status.succeeded, 'af', status.requested, 'ejendomme');
```

> En eksport er afgrænset af den liste id’er, du sender — det er ikke et udtræk af hele registret. `createExport()` prøves aldrig igen automatisk, så en netværksfejl ikke opretter et ekstra job. Se også `GET /v1/export`, som lister dine seneste jobs.
