Skip to content

Bulk export

Every dataset has a streaming export endpoint — the whole dataset (or a filtered subset) in one request, no pagination:

GET /v1/{domain}/{dataset}/export?format=csv|ndjson
  • format=csv (default) — UTF-8 CSV with a header row, served as text/csv with a Content-Disposition filename.
  • format=ndjson — one JSON object per line (application/x-ndjson), ideal for piping and incremental parsing. format=json is accepted as an alias.
  • The same filter syntax and ?fields= projection as the list endpoint.
  • Rows stream in stable insertion order straight from the database (X-Cache: BYPASS — exports are never cached).
  • Responses carry Last-Modified (the newest successful ingestion), so periodic re-downloads can be conditional instead of unconditional pulls of yearly data.

As a reference point: the ~380k-row vehicle registry (≈21 MB of CSV) exports in about a second on the development stack.

Examples

# Whole dataset to a file
curl -H "Authorization: Bearer $API_KEY" \
  "https://api.canovix.ro/v1/geospatial/siruta/export?format=csv" -o siruta.csv

# Filtered subset, selected columns
curl -H "Authorization: Bearer $API_KEY" \
  "https://api.canovix.ro/v1/transport/vehicle_registry/export?county=CLUJ&fields=manufacturer,vehicle_count" \
  -o cluj.csv

# Stream NDJSON through jq
curl -sN -H "Authorization: Bearer $API_KEY" \
  "https://api.canovix.ro/v1/health/physicians/export?format=ndjson" \
  | jq -r '.county' | sort | uniq -c
# Straight into pandas
import io

import httpx
import pandas as pd

response = httpx.get(
    "https://api.canovix.ro/v1/transport/vehicle_registry/export",
    params={"county": "CLUJ"},
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=120,
)
df = pd.read_csv(io.BytesIO(response.content))

# Or stream row by row without holding the file in memory
import json

with httpx.stream(
    "GET",
    "https://api.canovix.ro/v1/transport/vehicle_registry/export",
    params={"format": "ndjson"},
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=None,
) as response:
    for line in response.iter_lines():
        row = json.loads(line)
        ...
// Save to disk (Node 18+)
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";

const res = await fetch(
  `${BASE}/v1/transport/vehicle_registry/export?format=csv`,
  { headers: { Authorization: `Bearer ${API_KEY}` } },
);
await pipeline(Readable.fromWeb(res.body), createWriteStream("vehicle_registry.csv"));

// Or process NDJSON incrementally
const ndjson = await fetch(
  `${BASE}/v1/transport/vehicle_registry/export?format=ndjson&county=CLUJ`,
  { headers: { Authorization: `Bearer ${API_KEY}` } },
);
let buffer = "";
for await (const chunk of ndjson.body.pipeThrough(new TextDecoderStream())) {
  buffer += chunk;
  const lines = buffer.split("\n");
  buffer = lines.pop();
  for (const line of lines.filter(Boolean)) {
    const row = JSON.parse(line);
    // ...
  }
}

Export vs cursor pagination

Both walk the full dataset. Choose by what's consuming the data:

  • Export — you want a file, a DataFrame, or a database load. One request, one stream, least overhead.
  • Cursor — you want JSON pages with checkpointing: you can stop, remember pagination.next_cursor, and resume later; an export stream restarts from the beginning if interrupted.

Limits

An export holds a database connection for as long as you keep reading, so it carries its own limits on top of your plan's quota:

Limit Value What happens
Rows per export 1,000,000 The stream stops there. X-Max-Rows states the cap on every response.
Exports per account 20 / hour Further exports get 429 with Retry-After.
Concurrent exports 4 (service-wide) Extra exports queue rather than fail.

If a file comes back with exactly the row cap, treat it as truncated: filter it down (the same query syntax as the list endpoint works on /export) or switch to cursor pagination, which walks any dataset in pages and can be resumed.

Exports count as one request against your monthly quota, the same as any other call.