Édition N° 042 Paris · celyn.io avril mmxxvi
Catalogue général · collection données
celyn.
Le goût du temps, mis en données
n° 042 — avril mmxxvi

celyn Culture API — Documentation

Référence des endpoints de la verticale culture

Les verticales actu et sport sont servies sur celyn.io avec la même clé, mais une clé créée ici ne les ouvre pas d'office : /api/sport/* et /api/news/* répondent 403 tant que le scope actu n'a pas été activé sur la clé. Accès sur demande.

Authentication

Include your API key in every request header:

x-api-key: ck_your_api_key_here

Base URL

Rate Limits

1,000 requests per hour per API key.

Headers included in every response (per-instance, approximate — celyn.io runs behind a load balancer, so these count against whichever server handled the request, not a global total):

X-RateLimit-Limit X-RateLimit-Remaining X-RateLimit-Reset

Error Responses

StatusMeaningExample Body
400Bad request{ "error": "q parameter is required" }
401Missing/invalid API key{ "error": "Missing API key in x-api-key header" }
404Not found{ "error": "Oeuvre not found" }
422Validation error{ "error": "Invalid date format for 'from'" }
429Rate limited{ "error": "Rate limit exceeded" } + Retry-After header
500Server error{ "error": "Internal Server Error" }

Couverture des données

Relevé du 2026-09-24, arrondi à la baisse. Les chiffres en direct sont sur la page d'accueil et le détail par type sur /dataviz.

Œuvres par type

Expositions28 200+
Concerts19 100+
Films12 700+
Livres10 300+
Titres musicaux6 000+
Séries TV3 900+
Pièces de théâtre3 700+
Albums3 500+
Jeux vidéo3 200+
Festivals1 600+

Lieux. Plus de 64 800 lieux (cinémas, théâtres, salles de concert, musées, festivals, bars et restaurants).

Événements. Plus de 17 100 événements à venir référencés à cette date, dans tous les types (concerts, théâtre, musées, cinéma, festivals, ateliers, littérature).

Géographie. France, avec une couverture plus dense dans les grandes villes : Paris, Nantes, Toulouse, Marseille, Bordeaux, Lyon, Rennes, Lille, Bayonne, Nice. Hors de ces zones, la couverture dépend des sources locales et peut être partielle : vérifiez sur votre territoire avant de vous engager.

Fraîcheur. Les événements et séances sont réingérés en continu (plusieurs fois par jour) ; les fiches œuvres sont enrichies chaque nuit. Le champ syncAt des listes et les paramètres since et cursor de /api/events permettent une synchronisation incrémentale.

Exemples d'intégration

Le même appel (événements autour de Paris) en trois langages. Remplacez YOUR_KEY par votre clé (créée gratuitement dans le portail).

curl

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/events?lat=48.8566&lng=2.3522&radius_km=10&limit=5"

JavaScript / TypeScript (fetch)

const res = await fetch(
  "https://celyn.io/api/events?lat=48.8566&lng=2.3522&radius_km=10&limit=5",
  { headers: { "x-api-key": process.env.CELYN_API_KEY } },
);
if (!res.ok) throw new Error(`celyn API ${res.status}`);
const { data, count } = await res.json();
console.log(count, data[0].title, data[0].venue?.city);

Swift (CelynKit)

import CelynKit

let client = CultureAPIClient(apiKey: "YOUR_KEY")   // base URL: https://celyn.io/api

let page = try await client.events.list(.init(
    lat: 48.8566, lng: 2.3522, radiusKm: 10, limit: 5
))
for event in page.data {
    print(event.title, event.startTime, event.venue?.name ?? "-")
}

// Any endpoint, decoded into your own model:
struct MyOeuvre: Decodable { let id: String; let title: String }
let oeuvre: MyOeuvre = try await client.get("oeuvres/UUID")

CelynKit est le package Swift utilisé par l'application Keskonfé (iOS 17+, SPM). Il n'est pas encore publié sur un dépôt public : écrivez-nous pour l'obtenir. Il décode les dates ISO 8601 et convertit snake_case en camelCase pour vous.

Pas de SDK JavaScript publié pour l'instant : utilisez fetch comme ci-dessus, ou générez un client depuis openapi.json.

Events

GET /api/events

List events with optional geo, date, and category filters

Parameters

latnumberLatitude for geo search
lngnumberLongitude for geo search
radius_kmnumberSearch radius in km (default: 20)
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)
categorystringOeuvre type: film, play, concert, etc.
topicstringFilter by topic keyword(s)
age_minnumberMinimum recommended age
age_maxnumberMaximum recommended age
gradestringSchool grade filter — maternelle: PS, MS, GS; primaire: CP, CE1, CE2, CM1, CM2; collège: 6eme→3eme; lycée: seconde, premiere, terminale
limitnumberMax results (default: 50, max: 200)
offsetnumberPagination offset

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/events?lat=43.3&lng=5.4&radius_km=10&from=2026-04-01&limit=5"

Response

{
  "data": [
    {
      "id": "3f0c9a52-6f1d-4c1e-9d55-0a1b2c3d4e5f",
      "title": "Titre d'exemple",
      "category": "cinema",
      "description": null,
      "imageUrl": null,
      "image": {
        "url": null,
        "sourceUrl": null,
        "status": "unavailable"
      },
      "sourceUrl": null,
      "ticketUrl": null,
      "startTime": "2026-10-03T20:30:00.000Z",
      "endTime": null,
      "price": 9.5,
      "isFree": 0,
      "occasion": null,
      "buyUrl": null,
      "isSoldOut": 0,
      "ticketsCheckedAt": null,
      "isActive": 1,
      "createdAt": "2026-09-20T08:00:00.000Z",
      "updatedAt": "2026-09-21T08:00:00.000Z",
      "syncUpdatedAt": "2026-09-21T08:00:00.000Z",
      "mentionCount": 0,
      "festivalEditionId": null,
      "festival": null,
      "_links": {
        "recommendations": "/api/recommendations/b2a4c6e8-1357-4bdf-a024-68ace02468bd",
        "similar": "/api/oeuvres/b2a4c6e8-1357-4bdf-a024-68ace02468bd/similar",
        "opinions": "/api/oeuvres/b2a4c6e8-1357-4bdf-a024-68ace02468bd/opinions"
      },
      "curriculumTags": null,
      "venue": {
        "id": "7b1e2d90-4a6c-4f0e-8c21-5d6e7f8a9b0c",
        "supabaseId": null,
        "name": "Cinéma Exemple",
        "address": "1 rue de l'Exemple",
        "city": "Paris",
        "latitude": 48.8566,
        "longitude": 2.3522,
        "venueType": "cinema",
        "website": null
      },
      "oeuvre": {
        "id": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
        "title": "Titre d'exemple",
        "originalTitle": null,
        "oeuvreType": "film",
        "year": 2025,
        "director": "Nom Réalisateur",
        "author": null,
        "description": "Synopsis d'exemple.",
        "genres": [
          "drame"
        ],
        "imageUrl": null,
        "duration": 112,
        "publisher": null,
        "isbn": null,
        "updatedAt": "2026-09-20T08:00:00.000Z",
        "topics": null,
        "ageMin": null,
        "ageMax": null
      },
      "series": null
    }
  ],
  "count": 1,
  "syncAt": "2026-09-24T10:00:00.000Z",
  "isDelta": false,
  "nextCursor": null,
  "hasMore": false
}
GET /api/events/series

List recurring event series (same oeuvre, multiple showings)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/events/series"

Response

{ "data": [{ "id": "uuid", "title": "Anora", "occurrenceCount": 3 }] }
GET /api/events/suppressed

List suppressed events (duplicate/expired/cancelled/invalid) with reason

Parameters

limitnumberMax results
offsetnumberPagination offset

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/events/suppressed"

Response

{ "data": [{ "id": "uuid", "suppressedAt": "...", "reason": "duplicate" }] }
POST /api/events/submissions

Submit a user-suggested event for review

Parameters

bodyjsonRequest body (JSON)

Example

curl -X POST -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "...", "category": "concert", "venue_name": "...", "lat": 48.88, "lng": 2.34, "start_time": "2026-12-31T20:00:00Z", "device_id_hash": "<64 hex>" }' \
  "https://celyn.io/api/events/submissions"

Response

{ "id": "uuid", "status": "pending" }
GET /api/events/:id

Get a single event with venue and oeuvre

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/events/UUID"

Response

{
  "id": "3f0c9a52-6f1d-4c1e-9d55-0a1b2c3d4e5f",
  "title": "Titre d'exemple",
  "category": "cinema",
  "description": null,
  "imageUrl": null,
  "image": {
    "url": null,
    "sourceUrl": null,
    "status": "unavailable"
  },
  "sourceUrl": null,
  "ticketUrl": null,
  "startTime": "2026-10-03T20:30:00.000Z",
  "endTime": null,
  "price": 9.5,
  "isFree": 0,
  "occasion": null,
  "buyUrl": null,
  "isSoldOut": 0,
  "ticketsCheckedAt": null,
  "isActive": 1,
  "createdAt": "2026-09-20T08:00:00.000Z",
  "updatedAt": "2026-09-21T08:00:00.000Z",
  "syncUpdatedAt": "2026-09-21T08:00:00.000Z",
  "mentionCount": 0,
  "festivalEditionId": null,
  "festival": null,
  "_links": {
    "recommendations": "/api/recommendations/b2a4c6e8-1357-4bdf-a024-68ace02468bd",
    "similar": "/api/oeuvres/b2a4c6e8-1357-4bdf-a024-68ace02468bd/similar",
    "opinions": "/api/oeuvres/b2a4c6e8-1357-4bdf-a024-68ace02468bd/opinions"
  },
  "venue": {
    "id": "7b1e2d90-4a6c-4f0e-8c21-5d6e7f8a9b0c",
    "supabaseId": null,
    "name": "Cinéma Exemple",
    "address": "1 rue de l'Exemple",
    "city": "Paris",
    "latitude": 48.8566,
    "longitude": 2.3522,
    "venueType": "cinema",
    "website": null
  },
  "oeuvre": {
    "id": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
    "title": "Titre d'exemple",
    "originalTitle": null,
    "oeuvreType": "film",
    "year": 2025,
    "director": "Nom Réalisateur",
    "author": null,
    "description": "Synopsis d'exemple.",
    "genres": [
      "drame"
    ],
    "imageUrl": null,
    "duration": 112,
    "publisher": null,
    "isbn": null,
    "updatedAt": "2026-09-20T08:00:00.000Z"
  },
  "series": null
}

Venues

GET /api/venues

List venues with optional geo or city filter

Parameters

latnumberLatitude for geo search
lngnumberLongitude for geo search
radius_kmnumberSearch radius in km (default: 20)
citystringFilter by city name
limitnumberMax results (default: 50, max: 200)
offsetnumberPagination offset
includestringbeta · sur demande (scope establishment) establishment → adds `establishment` {kind, accessibility, erp, capacity, sources[]} (beta, key scope 'establishment' on request, else 403). Weak ETag + Cache-Control: private, max-age=3600. Credit each sources[] entry (licence + observedAt) when redistributing.
accessiblebooleanbeta · sur demande (scope establishment) true → only venues whose entrance is accessible per Acceslibre (fact confidence >= 0.70). Scope 'establishment'.
erp_typestringbeta · sur demande (scope establishment) Comma-separated ERP type letters (e.g. L,Y). Partial fact (Paris: Préfecture de Police Ad'AP list). Scope 'establishment'.
capacity_minnumberbeta · sur demande (scope establishment) Minimum hall gauge (seats). Scope 'establishment'.

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/venues?city=Paris&limit=10"

Response

{
  "data": [
    {
      "id": "7b1e2d90-4a6c-4f0e-8c21-5d6e7f8a9b0c",
      "supabaseId": null,
      "name": "Cinéma Exemple",
      "address": "1 rue de l'Exemple",
      "city": "Paris",
      "latitude": 48.8566,
      "longitude": 2.3522,
      "venueType": "cinema",
      "venueCategories": null,
      "website": null,
      "metadata": null,
      "imageUrl": null,
      "imageAttribution": null,
      "geofenceRadius": null,
      "mentionCount": 0
    }
  ],
  "count": 1
}
GET /api/venues/:id

Get a venue with its event count

Parameters

includestringbeta · sur demande (scope establishment) establishment → adds `establishment` {kind, accessibility, erp, capacity, sources[]} (beta, key scope 'establishment' on request, else 403). Weak ETag + Cache-Control: private, max-age=3600. Credit each sources[] entry (licence + observedAt) when redistributing.

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/venues/UUID"

Response

{
  "id": "7b1e2d90-4a6c-4f0e-8c21-5d6e7f8a9b0c",
  "supabaseId": null,
  "name": "Cinéma Exemple",
  "address": "1 rue de l'Exemple",
  "city": "Paris",
  "latitude": 48.8566,
  "longitude": 2.3522,
  "venueType": "cinema",
  "venueCategories": null,
  "website": null,
  "metadata": null,
  "imageUrl": null,
  "imageAttribution": null,
  "geofenceRadius": null,
  "mentionCount": 0,
  "source": null,
  "sourceId": null,
  "isActive": 1,
  "createdAt": "2026-09-01T08:00:00.000Z",
  "updatedAt": "2026-09-21T08:00:00.000Z",
  "eventCount": 42
}
GET /api/venues/:id/seances

List upcoming seances at a venue

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/venues/UUID/seances"

Response

{ "data": [{ "id": "uuid", "startsAt": "..." }] }
GET /api/venues/:id/mentions

Critic/social opinions mentioning a venue (restaurant/bar reviews)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/venues/UUID/mentions"

Response

{ "data": [{ "criticName": "...", "opinionSummary": "..." }] }
GET /api/venues/trending

"À la mode" venues ranked by recency × authority × sentiment

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/venues/trending"

Response

{ "data": [{ "id": "uuid", "name": "...", "trendingScore": 0.82 }] }
POST /api/venues/submissions

Submit a user-suggested venue for review

Parameters

bodyjsonRequest body (JSON)

Example

curl -X POST -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "...", "category": "bar", "address": "...", "lat": 48.88, "lng": 2.34, "device_id_hash": "<64 hex>" }' \
  "https://celyn.io/api/venues/submissions"

Response

{ "id": "uuid", "status": "pending" }

Seances

GET /api/seances

List screenings/showtimes

Parameters

venue_idstringFilter by venue UUID
oeuvre_idstringFilter by oeuvre UUID
latnumberLatitude for geo search
lngnumberLongitude for geo search
radius_kmnumberSearch radius (default: 20)
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)
limitnumberMax results (default: 50, max: 200)
offsetnumberPagination offset

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/seances?oeuvre_id=UUID&from=2026-04-01"

Response

{ "data": [{ "id": "uuid",
    "startsAt": "2026-04-03T20:30:00Z",
    "room": "Salle 1", "price": 9.5,
    "oeuvre": { "title": "Anora", "type": "film" },
    "venue": { "name": "Le Varietes" } }],
  "count": 1 }
GET /api/seances/:id

Get a single seance by ID

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/seances/UUID"

Response

{ "id": "uuid", "startsAt": "2026-04-03T20:30:00Z",
  "oeuvre": { "title": "Anora", ... },
  "venue": { "name": "Le Varietes", ... } }

Oeuvres

GET /api/oeuvres

List curated oeuvres with type/topic filters

Parameters

oeuvre_typestringFilter by type: film, play, concert, etc.
topicstringFilter by topic keyword(s)
sortstring"mentioned": most recently discussed first; adds sourceCount, lastMentionedAt, releaseDateTheatricalFr/DigitalFr/TvFr, streamingLatestArrivalAt, nowShowing, nextEpisodeAt (TV rows: next episode air date, YYYY-MM-DD or null), and awards/latestAwardAt on oeuvres with a literary prize selection. Every list shape carries editorialRanks [{list, tier: top10|top25|top50|top100}] on oeuvres present in an editorial best-of list (key absent otherwise). "upcoming": films releasing in FR cinemas within within_days (default 60), playable trailer (trailerUrl) first, then external trailer page (trailerExternalUrl), then soonest; adds the same release/nowShowing fields
within_daysnumbersort=upcoming window in days (default 60, clamped 1..180)
awardstringOnly oeuvres with a literary prize selection/win for this prize slug (goncourt, renaudot, femina, medicis, ...)
awardedboolean"true": only oeuvres with any literary prize selection/win
providerstringFR streaming platform(s): TMDB provider id or slug from /api/oeuvres/providers (netflix, prime-video, crunchyroll, france-tv, ...), comma-separated = any of. Matches offers included with the service (flatrate/free/ads), not rent/buy
animeboolean"true": only anime (Japanese animation); "false": exclude anime
platformstringGame platform(s) (games only): ps5, ps4, xbox-series, xbox-one, switch, switch-2, pc, mac, ios, android — comma-separated = any of; an unknown slug matches nothing. Game rows carry `platforms`; with sort=upcoming&type=game (games with a Europe/Worldwide release in within_days, soonest first) rows add `upcomingRelease` {date, platforms}
limitnumberMax results (default: 50, max: 200)
offsetnumberPagination offset

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/oeuvres?oeuvre_type=film&limit=10"

Response

{
  "data": [
    {
      "id": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
      "title": "Titre d'exemple",
      "oeuvreType": "film",
      "director": "Nom Réalisateur",
      "author": null,
      "publisher": null,
      "year": 2025,
      "imageUrl": null,
      "trailerUrl": null,
      "trailerExternalUrl": null,
      "genres": [
        "drame"
      ],
      "description": "Synopsis d'exemple.",
      "topics": null,
      "curriculumTags": null,
      "ageMin": null,
      "ageMax": null,
      "releaseStatus": null,
      "nextAirDate": null,
      "lastAirDate": null,
      "isAnime": null,
      "streamingProviderIds": [],
      "createdAt": "2026-09-01T08:00:00.000Z",
      "updatedAt": "2026-09-21T08:00:00.000Z"
    }
  ],
  "count": 1,
  "total": 1,
  "syncAt": "2026-09-24T10:00:00.000Z",
  "isDelta": false,
  "nextCursor": null,
  "hasMore": false
}
GET /api/oeuvres/providers Beta

FR streaming platforms (Netflix, Prime Video, Crunchyroll, France.tv, ...) with how many oeuvres each includes

Parameters

min_countnumberHide platforms with fewer oeuvres (default 1)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/oeuvres/providers"

Response

{ "data": [{ "providerId": 8, "slug": "netflix", "name": "Netflix",
  "logoUrl": "https://image.tmdb.org/t/p/w92/....jpg",
  "oeuvreCount": 812, "tvCount": 640, "filmCount": 172, "animeCount": 41,
  "memberProviderIds": [8, 1796] }],
  "count": 14, "region": "FR", "attribution": "Where-to-watch data by JustWatch via TMDB" }
GET /api/oeuvres/game-platforms Beta

Game platforms (PS5, Xbox Series, Switch, Switch 2, PC, ...) with how many games each has and how many release in the next 180 days

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/oeuvres/game-platforms"

Response

{ "data": [{ "slug": "ps5", "name": "PS5", "gameCount": 412, "upcomingCount": 38 }],
  "count": 7, "attribution": "Game data by IGDB.com" }
GET /api/oeuvres/:id

Get an oeuvre enriched with podcast critic opinions (and literary prize awards, editorial list tiers)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/oeuvres/UUID"

Response

{ "id": "uuid", "title": "Anora",
  "oeuvreType": "film", "director": "Sean Baker",
  "year": 2024,
  "opinions": [{ "criticName": "Xavier Leherpeur",
    "sentiment": "very_positive",
    "showName": "Le Masque et la Plume" }],
  "opinionCount": 3 }
GET /api/oeuvres/:id/similar

Find similar oeuvres via vector search

Parameters

limitnumberMax results (default: 10, max: 50)

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/oeuvres/UUID/similar?limit=5"

Response

{ "data": [{ "id": "uuid",
    "title": "The Florida Project",
    "oeuvreType": "film",
    "director": "Sean Baker",
    "distance": 0.123 }] }

Podcasts

GET /api/podcasts/sources

List all tracked podcast shows

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/podcasts/sources"

Response

{ "data": [{ "id": "uuid",
    "showName": "Le Masque et la Plume",
    "station": "France Inter",
    "category": "cinema" }] }
GET /api/podcasts/episodes

List episodes with oeuvre mention counts

Parameters

source_idstringFilter by podcast source UUID
limitnumberMax results (default: 20, max: 100)

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/podcasts/episodes?limit=5"

Response

{ "data": [{ "id": "uuid",
    "title": "Les films de la semaine",
    "showName": "Le Masque et la Plume",
    "status": "enriched",
    "mentionCount": 6 }] }
GET /api/podcasts/about/:oeuvre_id

Get podcast segments discussing a specific oeuvre

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/podcasts/about/UUID"

Response

{ "data": [{ "criticName": "Xavier Leherpeur",
    "opinionSummary": "A triumph of independent cinema...",
    "sentiment": "very_positive",
    "showName": "Le Masque et la Plume",
    "segmentStart": 340, "segmentEnd": 580 }] }
GET /api/podcasts/about-source/:source_id

Get podcast segments from a specific source discussing any oeuvre

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/podcasts/about-source/UUID"

Response

{ "data": [{ "rawTitle": "Anora",
    "sentiment": "very_positive",
    "opinionSummary": "..." }] }
GET /api/podcasts/feed

Chronological feed of recent podcast mentions

Parameters

limitnumberMax results (default: 20, max: 50)
source_typestringOnly items from sources of this type: rss (audio podcasts), youtube (videos), instagram, … Unknown value → 400

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/podcasts/feed?limit=10"

Response

{ "data": [{ "id": "uuid", "rawTitle": "Anora",
    "showName": "Le Masque et la Plume" }] }
GET /api/podcasts/recommendations

Recommend episodes by semantic similarity to an oeuvre

Parameters

similar_tostringOeuvre UUID (required)
limitnumberMax results (default: 10, max: 50)

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/podcasts/recommendations?similar_to=UUID&limit=5"

Response

{ "data": [{ "episodeId": "uuid",
    "episodeTitle": "Les films de la semaine",
    "showName": "Le Masque et la Plume",
    "distance": 0.234 }] }

Recommendations

GET /api/recommendations/:oeuvreId

Multi-dimensional ranked recommendations based on a source oeuvre

Parameters

latnumberLatitude for geo proximity scoring
lngnumberLongitude for geo proximity scoring
radius_kmnumberGeo search radius in km (default: 25)
fromstringStart date filter (ISO 8601)
tostringEnd date filter (ISO 8601)
categorystringOeuvre type: film, play, concert, etc.
contextstringUsage context (e.g. family outing, date night)
gradestringSchool grade filter — maternelle: PS, MS, GS; primaire: CP, CE1, CE2, CM1, CM2; collège: 6eme→3eme; lycée: seconde, premiere, terminale
limitnumberMax results (default: 10, max: 30)

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/recommendations/UUID?lat=48.85&lng=2.35&limit=5"

Response

{
  "data": [
    {
      "oeuvreId": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
      "id": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
      "title": "Titre d'exemple",
      "oeuvreType": "film",
      "director": "Nom Réalisateur",
      "author": null,
      "year": 2025,
      "imageUrl": null,
      "score": 0.82,
      "explanation": "très similaire thématiquement, bien noté par les critiques",
      "signals": {
        "embeddingSimilarity": 0.91,
        "curatorWeight": 0.75,
        "temporalDecay": 0.6,
        "geoProximity": 0.4,
        "topicalityBoost": 0.55
      },
      "opinions": [
        {
          "criticName": "Nom Critique",
          "opinionSummary": "Résumé d'exemple.",
          "sentiment": "very_positive",
          "showName": "Émission d'exemple",
          "publishedAt": "2026-09-18T07:00:00.000Z"
        }
      ],
      "nearbyEvents": [
        {
          "venueId": "7b1e2d90-4a6c-4f0e-8c21-5d6e7f8a9b0c",
          "venueName": "Cinéma Exemple",
          "startTime": "2026-10-03T20:30:00.000Z",
          "endTime": null,
          "price": 9.5,
          "distance_km": 2.1
        }
      ]
    }
  ],
  "meta": {
    "sourceOeuvreId": "b2a4c6e8-1357-4bdf-a024-68ace02468bd",
    "weights": {
      "embeddingSimilarity": 0.4,
      "curatorWeight": 0.3,
      "temporalDecay": 0.15,
      "geoProximity": 0.15,
      "topicalityBoost": 0.1
    },
    "candidateCount": 1,
    "cachedAt": "2026-09-24T10:00:00.000Z"
  }
}
POST /api/recommendations

AI-powered recommendations using LLM re-ranking with podcast intelligence

Parameters

bodyjsonRequest body (JSON)

Example

curl -X POST -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "preferences": { "mood": "uplifting",
    "genres": ["drama"] }, "limit": 5 }' \
  "https://celyn.io/api/recommendations"

Response

{ "data": [{ "id": "uuid", "title": "Anora",
    "oeuvreType": "film",
    "recommendation_reason": "Highly praised...",
    "opinions": [{ "sentiment": "very_positive" }] }],
  "reasoning": "Based on your preferences..." }

Batch Recommendations

POST /api/recommendations/batch

Batch multiple recommendation queries with deduplication

Parameters

bodyjsonRequest body (JSON)

Example

curl -X POST -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "queries": [{ "topics": ["jazz"],
    "location": { "lat": 48.85, "lng": 2.35 },
    "limit": 5 }] }' \
  "https://celyn.io/api/recommendations/batch"

Response

{ "results": [{ "query_index": 0,
    "recommendations": [...] }],
  "shared": [] }

Curators

GET /api/curators

List all podcast sources ranked by curator authority

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curators"

Response

{ "data": [{ "sourceId": "uuid",
    "showName": "Le Masque et la Plume",
    "station": "France Inter",
    "credibilityScore": 0.95,
    "tier": "reference",
    "category": "critique",
    "isVerified": true,
    "mentionCount": 87,
    "episodeCount": 31 }] }
GET /api/curators/:source_id

Get detailed authority info for a specific podcast source

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curators/UUID"

Response

{ "data": { "sourceId": "uuid",
    "showName": "Le Masque et la Plume",
    "station": "France Inter",
    "credibilityScore": 0.95,
    "tier": "reference",
    "isVerified": true,
    "notes": "Institution de la critique culturelle depuis 1955",
    "recentMentions": [{ "rawTitle": "Anora",
      "sentiment": "very_positive",
      "criticName": "Xavier Leherpeur",
      "opinionSummary": "A triumph of independent cinema..." }] } }

Search

GET /api/search/thematic

Semantic search across oeuvres, events, and podcasts

Parameters

qstringSearch query (required)
typestringComma-separated: oeuvre, event, podcast
limitnumberMax results (default: 20)
age_minnumberMinimum recommended age
age_maxnumberMaximum recommended age
gradestringSchool grade filter — maternelle: PS, MS, GS; primaire: CP, CE1, CE2, CM1, CM2; collège: 6eme→3eme; lycée: seconde, premiere, terminale

Example

curl -H "x-api-key: YOUR_KEY" \
  "https://celyn.io/api/search/thematic?q=cinema%20francais&type=oeuvre,event&limit=10"

Response

{ "results": [{ "type": "oeuvre", "id": "uuid",
    "title": "Anora", "score": 0.87,
    "topics": ["cinema", "independant"] }],
  "query": "cinema francais", "total": 1 }

Critics

GET /api/critics Beta

List critics with their podcast authority profile

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/critics"

Response

{ "data": [{ "criticName": "Xavier Leherpeur",
    "showName": "Le Masque et la Plume",
    "mentionCount": 87 }] }

Festivals

GET /api/festivals Beta

List upcoming festival editions

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/festivals"

Response

{ "data": [{ "editionId": "uuid",
    "title": "We Love Green", "year": 2026 }] }
GET /api/festivals/:editionId/lineup Beta

Artist grid for a festival edition (stage/day lineup)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/festivals/UUID/lineup"

Response

{ "data": [{ "stage": "La Prairie",
    "entries": [{ "artistName": "...", "slotStart": "..." }] }] }
GET /api/festivals/:editionId/events Beta

Event programme bridged to a festival edition

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/festivals/UUID/events"

Response

{ "data": [{ "id": "uuid", "title": "...", "startTime": "..." }] }
GET /api/festivals/by-oeuvre/:oeuvreId Beta

Which festivals feature a given artist/oeuvre

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/festivals/by-oeuvre/UUID"

Response

{ "data": [{ "editionId": "uuid", "title": "We Love Green", "year": 2026 }] }

Albums

GET /api/albums Beta

List albums with critic opinions

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/albums"

Response

{ "data": [{ "id": "uuid", "title": "..." }] }
GET /api/albums/trending Beta

Trending albums by recent critic mentions

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/albums/trending"

Response

{ "data": [{ "id": "uuid", "title": "..." }] }
GET /api/albums/featured Beta

Editorially featured albums

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/albums/featured"

Response

{ "data": [{ "id": "uuid", "title": "..." }] }
GET /api/albums/:id Beta

Get a single album with critic opinions

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/albums/UUID"

Response

{ "id": "uuid", "title": "...", "opinions": [] }
GET /api/albums/:id/similar Beta

Find similar albums via vector search

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/albums/UUID/similar"

Response

{ "data": [{ "id": "uuid", "title": "...", "distance": 0.1 }] }

Curation

GET /api/curation/trending Beta

Trending curated content

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/trending"

Response

{ "data": [] }
GET /api/curation/feed Beta

Curated content feed

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/feed"

Response

{ "data": [] }
GET /api/curation/sources Beta

Catalogue of curated (featured) sources

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/sources"

Response

{ "data": [] }
GET /api/curation/sources/:id/feed Beta

Feed for a single curated source

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/sources/UUID/feed"

Response

{ "data": [] }
GET /api/curation/sections Beta

Server-driven curated app sections (ville × type)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/sections"

Response

{ "data": [] }
GET /api/curation/sections/:id/content Beta

Content for a curated section

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/sections/UUID/content"

Response

{ "data": [] }
GET /api/curation/selection Beta Accès sur demande

Read this key's curated-source selection

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/selection"

Response

{ "data": [] }
POST /api/curation/selection/:sourceId Beta Accès sur demande

Add a source to this key's curated selection

Example

curl -X POST -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/selection/UUID"

Response

{ "ok": true }
DELETE /api/curation/selection/:sourceId Beta Accès sur demande

Remove a source from this key's curated selection

Example

curl -X DELETE -H "x-api-key: YOUR_KEY" "https://celyn.io/api/curation/selection/UUID"

Response

{ "ok": true }

Embeddings

GET /public/embeddings/oeuvres Beta

Delta-sync feed of oeuvre embedding vectors for on-device ranking (path is /public/embeddings, NOT /api/embeddings: no API key needed)

Parameters

cursorstringOpaque pagination cursor from a previous response
limitnumberMax results per page

Example

curl "https://celyn.io/public/embeddings/oeuvres?limit=100"

Response

{ "data": [{ "id": "uuid", "embedding": [0.01, -0.02, ...] }],
  "nextCursor": "2026-04-01T00:00:00.000Z:uuid" }
GET /public/embeddings/curators Beta

Delta-sync feed of curator (source) taste-vector centroids (path is /public/embeddings, NOT /api/embeddings: no API key needed)

Parameters

cursorstringOpaque pagination cursor from a previous response
limitnumberMax results per page

Example

curl "https://celyn.io/public/embeddings/curators?limit=100"

Response

{ "data": [{ "sourceId": "uuid", "embedding": [0.03, 0.11, ...] }],
  "nextCursor": "2026-04-01T00:00:00.000Z:uuid" }

Creators

GET /api/creators/instagram Beta

Instagram creator signal (venue mentions extracted from IG captions)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/creators/instagram"

Response

{ "data": [{ "igUsername": "...", "mentionCount": 5 }] }
GET /api/creators Beta

List creators (directors, musicians, authors, etc.)

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/creators"

Response

{ "data": [{ "id": "uuid", "name": "Sean Baker" }] }
GET /api/creators/:id Beta

Get a single creator with bio and works

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/creators/UUID"

Response

{ "id": "uuid", "name": "Sean Baker", "bio": "..." }
GET /api/creators/:id/events Beta

Upcoming events linked to this creator's works

Example

curl -H "x-api-key: YOUR_KEY" "https://celyn.io/api/creators/UUID/events"

Response

{ "data": [] }

Sport (accès sur demande)

GET /api/sport/feed Beta Accès sur demande

Football signal feed (transfers, injuries, lineups, quotes)

Parameters

disciplinestringDiscipline slug (football, rugby, cyclisme, athletisme, trail…); default football, `all` for every discipline

Example

# Access on request — see /portal for contact details.
curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/feed"

Response

{ "data": [{ "id": "uuid", "kind": "transfer", "discipline": "football", "rawText": null }], "meta": { "discipline": "football" } }
GET /api/sport/feed/:id Beta Accès sur demande

Get a single sport feed item

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/feed/UUID"

Response

{ "id": "uuid", "kind": "transfer" }
GET /api/sport/stories Beta Accès sur demande

Clustered football stories

Parameters

disciplinestringDiscipline slug (football, rugby, cyclisme, athletisme, trail…); default football, `all` for every discipline

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/stories"

Response

{ "data": [{ "id": "uuid", "title": "..." }] }
GET /api/sport/stories/:id Beta Accès sur demande

Get a single football story

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/stories/UUID"

Response

{ "id": "uuid", "title": "..." }
GET /api/sport/entities Beta Accès sur demande

Canonical football entities (players, clubs, competitions)

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/entities"

Response

{ "data": [{ "id": "uuid", "name": "..." }] }
GET /api/sport/results Beta Accès sur demande

Race results (running/trail/triathlon): race list, or one race's elite/consented results

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/sport/results?race=trail-de-mios-14km-2026"

Response

{ "data": { "race": { "key": "...", "name": "..." }, "results": [{ "rank": 1, "name": "...", "time": "0:52:46", "publicationBasis": "elite" }] } }

Actu / News (accès sur demande)

GET /api/news/feed Beta Accès sur demande

Actu vertical signal feed (reformulated summaries, never raw text)

Example

# Access on request — see /portal for contact details.
curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/news/feed"

Response

{ "data": [{ "id": "uuid", "kind": "politique", "summary": "..." }] }
GET /api/news/feed/:id Beta Accès sur demande

Get a single news feed item

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/news/feed/UUID"

Response

{ "id": "uuid", "summary": "..." }
GET /api/news/stories Beta Accès sur demande

Clustered news stories

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/news/stories"

Response

{ "data": [{ "id": "uuid", "title": "..." }] }
GET /api/news/stories/:id Beta Accès sur demande

Get a single news story

Example

curl -H "x-api-key: YOUR_STATIC_KEY" "https://celyn.io/api/news/stories/UUID"

Response

{ "data": { "id": "uuid", "title": "...", "events": [], "consensus": { "generatedAt": "2026-09-27T03:40:00.000Z", "eventCount": 82, "independentSourceCount": 12, "facts": [{ "id": "f_1a2b3c4d", "claim": "Le pape Léon XIV célèbre une messe place de la Concorde le 26 septembre.", "facet": "programme", "status": "confirmed", "independentSources": 4, "sources": [{ "name": "Le Monde", "url": "https://..." }], "informativeness": 0.8 }], "contested": [{ "subject": "fidèles place de la Concorde (personne)", "versions": [{ "claim": "...700 000 fidèles...", "sources": [] }, { "claim": "...800 000 fidèles selon le Vatican...", "sources": [] }] }] } } }