Salta al contenuto principale
Torna al marketplacePUBLIC API

Documentazione API Dataset

REST API per consumare i dataset del marketplace. Endpoint pubblici per catalogo e metadata, endpoint autenticati con API key per query pay-per-query (€5 per 1000 query, crediti che non scadono mai).

Base URL: https://federicocalo.dev/api/v1120 req/min · 5000 req/giornoJSON · CSV · NDJSON · SSE

Autenticazione e API key

Gli endpoint di sola lettura (catalogo, metadata, schema, sample) sono pubblici e non richiedono autenticazione. Le chiamate POST /query e i download completi richiedono invece una API key personale, generabile dall'area account dopo l'acquisto dei crediti.

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

  1. Acquista crediti dalla pagina Account → Premium (€5 per 1000 query, IVA esclusa).
  2. Genera una API key dall'area Account → API key: ne ottieni il valore una sola volta, conservalo in un secret manager.
  3. Passa la key sull'header X-Api-Key di ogni richiesta autenticata. Il backend deduce il credito esatto consumato in base al numero di righe restituite.
curl
curl -H 'X-Api-Key: fc_live_xxxxx' \
  https://federicocalo.dev/api/v1/datasets

Gestire la tua API key e i crediti

Dal tab API del tuo profilo puoi generare gratuitamente la tua prima API key, monitorare il consumo mensile delle 100 query incluse nel piano gratuito e acquistare crediti pay-per-query aggiuntivi.

Vai al Profilo → tab API

Formato risposta (envelope)

Tutte le risposte JSON usano lo stesso envelope, sia in caso di successo che di errore. Il campo success è la fonte di verità: ignora data quando success: false.

JSON
{
  "success": true,
  "data": { ... },
  "error": null,
  "meta": {
    "requestId": "150d99e7-d413-4fe0-8c9c-1dea2925e709",
    "timestamp": "2026-04-25T14:39:27Z",
    "durationMs": 42
  }
}
Campi dell'envelope di risposta
CampoTipoDescrizione
successbooleantrue se la richiesta è andata a buon fine.
dataany | nullPayload tipato per ogni endpoint, null in caso di errore.
errorobject | nullOggetto con campi code, message, details presente solo in caso di errore.
meta.requestIdstringUUID di correlation, da citare nei ticket di supporto.

Errori e rate limiting

Gli errori usano i codici HTTP standard. Le chiamate sopra il rate limit ricevono 429 Too Many Requests con header Retry-After; le query senza crediti ricevono 402 Payment Required.

StatusCodiceSignificato
400BAD_REQUESTParametri non validi (slug, limit, filtri).
401UNAUTHORIZEDAPI key mancante o non valida.
402NO_CREDITSCrediti insufficienti per completare la query.
404NOT_FOUNDSlug dataset non trovato o non pubblicato.
429RATE_LIMITEDLimite 120 req/min superato; usa l'header Retry-After.
500INTERNAL_ERRORErrore interno; cita requestId al supporto.

Catalogo

GET/api/v1/datasets

Restituisce la lista paginata dei dataset pubblicati, ordinati con featured in cima. Endpoint pubblico, no auth.

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

Ricerca semantica via embedding (sentence-transformers all-MiniLM-L6-v2) con re-ranking cross-encoder. Parametri: 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'

Metadata

GET/api/v1/datasets/{slug}

Header completo del dataset: titolo, descrizione, licenza, versione corrente, conteggio righe, dimensione file, lista tag, link a methodology e changelog.

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

JSON Schema dei campi: nome colonna, tipo (string, integer, number, boolean, datetime), nullable, descrizione, esempio. Generato dal Parquet header quando disponibile.

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

Sample di anteprima (max 100 righe). Endpoint pubblico, non consuma crediti ed è cacheato 5 minuti.

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

Markdown della metodologia: fonti, criteri di selezione, trasformazioni, frequenza di refresh, validazioni QA.

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

Changelog markdown semver per versione (breaking changes, schema diff, fix, deprecazioni).

Discovery

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

Dataset correlati basati sulla tabella dataset_relations (relazioni curate manualmente) con fallback su related_dataset_slugs[] quando vuoto.

GET/api/v1/datasets/relations/graph

Grafo D3-ready (nodes[] / edges[]) di tutte le relazioni tra dataset. Risposta cacheata 1 ora.

Query e download

Le chiamate di questa sezione consumano crediti. Il deduzione esatta è ceil(rows / 1) per il payload restituito. Tutte richiedono l'header X-Api-Key.

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

Esegue una query con filtri sul dataset e restituisce le righe in JSON (formato CSV/NDJSON via header Accept). Il body accetta filters, columns, limit (max 10.000), 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

Variante Server-Sent Events: restituisce le righe come data: events NDJSON. Adatto per dataset multi-milione di righe senza materializzare in memoria. Stesso body di /query; limit qui può salire a 1.000.000.

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

Download del file completo (CSV/JSON) nella versione current. Consumo crediti = numero totale di righe del dataset.

PERFORMANCE

Keyset pagination (cursor-based)

La paginazione classica con offset ha costo O(N): su file con oltre 1 milione di righe ogni pagina richiede di scorrere tutte le righe precedenti. La keyset pagination risolve il problema: il server codifica la posizione dell'ultima riga restituita in un cursor opaco (Base64 di {"lastSortValue":"…","lastId":"…"} ) e la pagina successiva ricomincia esattamente da quel punto, con costo O(1) per pagina indipendentemente dalla profondità. Regola fondamentale: cursor wins — quando invii sia cursor sia offset, offset viene ignorato.

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.

Esempi completi

Python (requests)

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"]

Node.js (fetch)

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();