← Retour au blog

Des APIs de données vraiment utiles

Lucian BLETAN

Une API de données n’est pas un entrepôt à ciel ouvert. Elle doit exposer des contrats lisibles, des formats stables et des garanties simples: comment filtrer, paginer, trier, comprendre les erreurs et se mettre à jour sans casser les clients. Le but est de réduire le temps entre la question et la réponse fiable.

prerequis

  • Un entrepôt ou un lac avec des vues stables et documentées.
  • Des schémas décrits (types, unités, périmètre, fraîcheur).
  • Une stratégie d’identité et d’autorisations simple (tokens, scopes).

idees clefs

  • Un contrat de schéma visible et versionné.
  • Pagination, tri et filtres cohérents sur tous les endpoints.
  • Formats simples: JSON pour navigation, CSV/NDJSON/Parquet pour volumes.
  • Horodatage et fuseau explicites, pas d’ambiguite sur les dates.
  • Erreurs claires et idempotence des créations/modifications.
  • Dépréciation annoncée et migration assistée.

pas a pas

etape 1: écrire le contrat

Définir pour chaque endpoint les champs, types, unités, ordre, filtres et limites.

endpoint: GET /api/sales/orders
contract:
  params:
    - name: start_date   # YYYY-MM-DD
    - name: end_date     # YYYY-MM-DD, exclusif
    - name: country      # ex: FR, DE
    - name: limit        # pagination: 1..1000
    - name: cursor       # token opaque
  response:
    - order_id: string
    - order_date: datetime (UTC)
    - amount_eur: number
    - country: string
  sort: order_date asc
  freshness_slo_min: 180

etape 2: normaliser pagination et filtres

Même mécanique partout: limit + cursor, pas d’off-by-one ni de pages aléatoires.

# 1ere page
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.data.pm/api/sales/orders?start_date=2020-01-01&end_date=2020-01-31&limit=500"

# reponse (extrait)
# {
#   "data": [...],
#   "next_cursor": "eyJvcmRlcl9kYXRlIjoiMjAyMC0wMS0xNVQxMjozMDo1MFoiLCJvcmRlcl9pZCI6IjEyMzQifQ=="
# }

# page suivante
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.data.pm/api/sales/orders?cursor=eyJvcm...==&limit=500"

etape 3: soigner les dates et les unités

Toujours préciser timezone, format, unité; fournir un exemple.

{
  "order_date": "2020-01-15T12:30:50Z",
  "amount_eur": 129.90,
  "country": "FR"
}

etape 4: proposer des exports pour le volume

Même endpoint logique, suffixes clairs pour formats bruts.

# CSV
curl -L -H "Authorization: Bearer $TOKEN" \
  "https://api.data.pm/api/sales/orders.csv?start_date=2020-01-01&end_date=2020-01-31" -o orders_2020_01.csv

# NDJSON (une ligne JSON par enregistrement)
curl -L -H "Authorization: Bearer $TOKEN" \
  "https://api.data.pm/api/sales/orders.ndjson?start_date=2020-01-01&end_date=2020-01-31" -o orders_2020_01.ndjson

etape 5: documenter les erreurs et l’idempotence

Des codes HTTP honnêtes et des messages utiles; fournir une cle idempotency.

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "invalid_parameter",
  "message": "start_date must be before end_date",
  "param": "start_date",
  "docs": "https://api.data.pm/docs#errors"
}
# creation idempotente
curl -X POST -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: 3d7f3e8a-8de0-4a2a-b1a1-dc2b18" \
     -H "Content-Type: application/json" \
     -d '{"name":"orders_daily_v1","schedule":"0 5 * * *"}' \
     https://api.data.pm/api/exports

etape 6: versionner sans casser

Garder v1 stable; introduire v2 en parallele et annoncer la depreciation.

deprecation:
- v1 retiree le 2020-06-30
- v2 expose "tax_eur" et "amount_eur" se limite au HT
- guide: https://api.data.pm/migrate-orders-v2

exemples

cas: endpoint de series temporelles simple et robuste

GET /api/metrics/daily?metric=cpa_eur&start=2020-01-01&end=2020-03-01&group_by=country&limit=1000
Authorization: Bearer <token>
{
  "metric": "cpa_eur",
  "granularity": "day",
  "unit": "EUR",
  "data": [
    {"date":"2020-01-01","country":"FR","value":48.3},
    {"date":"2020-01-01","country":"DE","value":46.9}
  ],
  "next_cursor": null
}
-- cote entrepot: vue stable alimentee par ETL
CREATE OR REPLACE VIEW metrics.cpa_daily_v AS
SELECT
  DATE_TRUNC('day', ts)::date AS d,
  country,
  ROUND(SUM(spend_eur)/NULLIF(SUM(conversions),0),2) AS cpa_eur
FROM marketing.daily_stats
GROUP BY 1,2;

pieges frequents

  • filtres differents par endpoint -> confusion -> meme grammaire de filtres partout
  • pages numerotees -> trous/doublons -> utiliser cursor base sur tri deterministe
  • champs caches/renommes -> casse -> contrat de schema et changelog publics
  • fuseaux implicites -> erreurs -> toujours UTC en stockage, TZ explicite si affichage local
  • overfetch JSON -> lenteur -> proposer CSV/NDJSON/Parquet pour gros volumes
  • auth ad hoc -> failles -> tokens scopes, RLS cote entrepot

faq

  • comment limiter la casse lors des changements ? Garder v1 intacte, publier v2 en parallele, fournir un guide de migration et une periode de double publication.
  • faut il tout mettre en temps reel ? Non. Fixez des SLO de fraicheur credibles et annoncez les delais de disponibilite.
  • quels formats recommander ? JSON pour navigation et APIs metier; CSV/NDJSON pour export rapide; Parquet quand il faut compresser et typer.