Skip to main content
Return to marketplacePublic API

API Documentation Dataset

API for consuming the marketplace datasets. Public endpoints for catalog and metadata, authenticated endpoints with API key for pay-per-query queries (€5 per 1,000 queries, credits that never expire).

https://www.federico-calò.com/en/blog/2022-01-31-angular-spring-boot-and-oollama-part-one.html120 req/min · 5000 req/dayData Formats: JSON - CSV - NDJSON - Server-Sent Events (SSE)

Authentication and API key

The read-only endpoints (catalog, metadata, schema, sample) are public and do not require authentication. The POST /query calls and complete downloads, however, require a personal API key, which can be generated from the account area after purchasing credits.

Registrati o accedi, poi vai su Profilo → tab API per generare la tua chiave gratuita.

  1. Buy credits from the Account → Premium page (€5 for 1,000 queries, VAT excluded).
  2. Generate an API key from the Account → API key area: you obtain its value only once, store it in a secret manager.
  3. Pass the key in the X-Api-Key header of every authenticated request. The backend deducts the exact credit consumed based on the number of rows returned.
curl
curl -H 'X-Api-Key: fc_live_xxxxx' \
  https://federicocalo.dev/api/v1/datasets

Manage your API key and credits

From the API tab of your profile, you can generate your first API key for free, monitor your monthly usage of the included 100 free queries, and purchase additional pay-per-query credits.

Go to Profile → API tab

Response format (envelope)

All JSON responses use the same envelope, both for success and error cases. The success field is the source of truth: ignore date when success: false.

JSON
{
  "success": true,
  "data": { ... },
  "error": null,
  "meta": {
    "requestId": "150d99e7-d413-4fe0-8c9c-1dea2925e709",
    "timestamp": "2026-04-25T14:39:27Z",
    "durationMs": 42
  }
}
Fields of the response envelope
FieldTypeDescription
successbooleanIf the request has been successfully completed.
dataany | nullPreloaded payload for each endpoint, null on error.
errorobject | nullObject with code fields, message, and details present only in case of error.
meta.requestIdstringCorrelation ID, to be cited in support tickets.

Errors and rate limiting

They use standard HTTP codes for errors. Calls above the rate limit receive 429 Too Many Requests with a header of Retry-After; queries without credits receive 402 Payment Required.

ActiveCodeMeaning
400BAD_REQUESTInvalid parameters (slug, limit, filters).
401UNAUTHORIZEDMissing or invalid API key.
402NO_CREDITSInsufficient credits to complete the query.
404NOT_FOUNDThe slug dataset was not found or published.
429RATE_LIMITEDExceeded limit of 120 requests per minute; use the header Retry-After.
500INTERNAL_ERRORInternal error; cite requestId to support.

Catalogue

GET/api/v1/datasets

Returns the paginated list of published datasets, ordered with featured at top. Public endpoint, no auth.

curl
curl https://federicocalo.dev/api/v1/datasets
GET/api/v1/datasets/search?q={testo}&limit=10&lang=it

Semantic search through embedding (using sentence-transformers all-MiniLM-L6-v2) with cross-encoder re-ranking. Parameters: q (2–200 char), limit (1–50, default 10), lang (it / en).

curl
curl 'https://federicocalo.dev/api/v1/datasets/search?q=appalti+pubblici&limit=5'

Meta data

GET/api/v1/datasets/{slug}

Full header of the dataset: title, description, license, current version, row count, file size, list of tags, link to methodology and changelog.

GET/api/v1/datasets/{slug}/schema

JSON Schema of the fields: column name, type (string, integer, number, boolean, datetime), nullable, description, example. Generated from the Parquet header when available.

GET/api/v1/datasets/{slug}/sample

Sample preview (max 100 lines). Public endpoint, does not consume credits and is cached for 5 minutes.

GET/api/v1/datasets/{slug}/methodology

Markdown of methodology: sources, selection criteria, transformations, refresh frequency, QA validations.

GET/api/v1/datasets/{slug}/changelog

Changelog in Markdown format following semantic versioning (breaking changes, schema differences, fixes, deprecations).

Discovery

GET/api/v1/datasets/{slug}/related?limit=5

Dataset related to the table dataset_relations (manually curated relationships) with a fallback on related_dataset_slugs[] when empty.

GET/api/v1/datasets/relations/graph

D3-ready graph (nodes[] / edges[]) of all relationships between datasets. Caching response for 1 hour.

Query and download

Calls in this section consume credits. The exact deduction is ceil(rows / 1) for the returned payload. All require the header X-Api-Key.

POST/api/v1/datasets/{slug}/query

Executes a query with filters on the dataset and returns rows in JSON (CSV/NDJSON format via the Accept header). The body accepts filters, columns, limit (max 10,000), and offset.

curl
curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: fc_live_xxxxx' \
  -d '{"filters":{"region":"Lombardia"},"limit":100}' \
  https://federicocalo.dev/api/v1/datasets/anac-contratti-pubblici-2024/query
POST/api/v1/datasets/{slug}/query/stream

Server-Sent Events variant: returns rows as data: NDJSON events. Suitable for multi-million-row datasets without materializing them in memory. Same body as /query; here limit can go up to 1,000,000.

GET/api/v1/datasets/{slug}/download

Download the full file (CSV/JSON) in the current version. Credit consumption = total number of rows in the dataset.

PERFORMANCE

Cursor-Based Pagination

Classic pagination with offset has O(N) cost: on files with over a million lines, each page requires scrolling through all previous lines. The keyset pagination solves the problem: the server encodes the position of the last returned row in an opaque cursor (Base64 encoded){"lastSortValue":"…","lastId":"…"}) and the next page starts exactly from that point, with a cost of O(1) per page regardless of depth. Fundamental rule: cursor wins - when you send both cursor and offset, offset is ignored.

json - prima pagina (no cursor)
POST /api/v1/datasets/anac-contratti-pubblici-2024/query
X-Api-Key: fc_live_xxxxx
Content-Type: application/json

{
  "filters": { "region": "Lombardia" },
  "limit": 100,
  "sortBy": "importo",
  "sortDirection": "desc"
}

// Risposta (campo nextCursor presente se ci sono altre righe)
{
  "success": true,
  "data": {
    "paginationMode": "offset",
    "rowsReturned": 100,
    "nextCursor": "eyJsYXN0U29ydFZhbHVlIjoiMTIzNDU2IiwibGFzdElkIjoiMTAwIn0",
    "results": [ ... ]
  }
}
json - pagina successiva (con cursor)
POST /api/v1/datasets/anac-contratti-pubblici-2024/query
X-Api-Key: fc_live_xxxxx
Content-Type: application/json

{
  "filters": { "region": "Lombardia" },
  "limit": 100,
  "sortBy": "importo",
  "sortDirection": "desc",
  "cursor": "eyJsYXN0U29ydFZhbHVlIjoiMTIzNDU2IiwibGFzdElkIjoiMTAwIn0"
}

// Se nextCursor è null, hai raggiunto l'ultima pagina.
Campo requestTipoNote
cursorstring (Base64)Cursor opaco dalla risposta precedente. Se presente, offset viene ignorato.
sortBystringNome colonna di ordinamento. Assente = ordine naturale del file (nessun overhead).
sortDirection"asc" | "desc"Default "asc".
limitintegerRighe per pagina: [1, 10 000], default 100.
Campo responseTipoNote
nextCursorstring | nullCursor per la pagina successiva. null = ultima pagina raggiunta.
paginationMode"offset" | "cursor"Indica quale strategia è stata applicata per questa risposta.
totalRowsinteger | nullConteggio totale da metadata cached. null se non disponibile senza scan.

Complete Examples

Requests Library for Python is used to make HTTP requests in the application.

python
import os, requests

API = "https://federicocalo.dev/api/v1"
KEY = os.environ["PORTFOLIO_API_KEY"]

# Catalogo pubblico
catalog = requests.get(f"{API}/datasets").json()
print(len(catalog["data"]), "dataset disponibili")

# Query con filtri (richiede credito)
r = requests.post(
    f"{API}/datasets/anac-contratti-pubblici-2024/query",
    headers={"X-Api-Key": KEY},
    json={"filters": {"importo_min": 100000}, "limit": 500},
    timeout=30,
)
r.raise_for_status()
rows = r.json()["data"]

Fetch API is not supported by this browser or Node environment.

javascript
const API = 'https://federicocalo.dev/api/v1';
const KEY = process.env.PORTFOLIO_API_KEY;

const res = await fetch(
  `${API}/datasets/istat-popolazione-comuni-2024/query`,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': KEY,
    },
    body: JSON.stringify({ filters: { provincia: 'MI' }, limit: 200 }),
  },
);
const { data } = await res.json();