{"openapi":"3.1.0","info":{"version":"1.0.0","title":"CampingFrance Public API (read-only)","description":"Public, read-only API over the CampingFrance directory of ~22,000 European campsites. Search and campsite detail only. Establishment data is public; no personal contact information is exposed. Rate-limited per IP. For agentic clients, an MCP server is also available (see /.well-known/mcp.json on the site origin)."},"servers":[{"url":"https://api.campingfrance.com","description":"Public API"}],"tags":[{"name":"public","description":"Public read-only campsite search & detail."}],"components":{"schemas":{"PublicSearchResponse":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicCampsiteListItem"}},"facets":{"type":"object","properties":{"byType":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"byStars":{"type":"array","items":{"type":"object","properties":{"stars":{"type":"integer"},"count":{"type":"integer"}},"required":["stars","count"]}},"commonFeatures":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"count":{"type":"integer"}},"required":["code","name","count"]}}},"required":["byType","byStars","commonFeatures"]}},"required":["total","limit","offset","items","facets"]},"PublicCampsiteListItem":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"stars":{"type":"integer","nullable":true},"city":{"type":"string","nullable":true},"postalCode":{"type":"string","nullable":true},"department":{"type":"string","nullable":true},"region":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"canonicalPath":{"type":"string","nullable":true},"distanceKm":{"type":"number","nullable":true}},"required":["slug","name","type","stars","city","postalCode","department","region","country","canonicalPath","distanceKm"]},"PublicError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"PublicCampsite":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"stars":{"type":"integer","nullable":true},"canonicalPath":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"address":{"type":"object","nullable":true,"properties":{"street":{"type":"string","nullable":true},"postalCode":{"type":"string","nullable":true},"city":{"type":"string"},"department":{"type":"string","nullable":true},"region":{"type":"string","nullable":true},"country":{"type":"string"},"gps":{"type":"object","nullable":true,"properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"]}},"required":["street","postalCode","city","department","region","country","gps"]},"seasons":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"openDate":{"type":"string","nullable":true},"closeDate":{"type":"string","nullable":true}},"required":["type","openDate","closeDate"]}},"features":{"type":"array","items":{"$ref":"#/components/schemas/PublicFeature"}},"priceRange":{"type":"object","nullable":true,"properties":{"minEur":{"type":"number"},"maxEur":{"type":"number"}},"required":["minEur","maxEur"]},"lastVerifiedAt":{"type":"string","nullable":true},"photos":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"legend":{"type":"string","nullable":true}},"required":["url","legend"]},"description":"Public photo gallery, ordered. May be partial. Empty array when the campsite has no published photo."}},"required":["id","slug","name","type","stars","canonicalPath","description","address","seasons","features","priceRange","lastVerifiedAt","photos"]},"PublicFeature":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"value":{"type":"string","nullable":true},"valueLabel":{"type":"string","nullable":true,"description":"Human-readable French label for `value`, for the 9 features with a bounded vocabulary (payment methods, spoken languages, currencies, shade, ground type, service-point brand/management/location, rental capacity). Multi-valued features yield the labels joined by \", \", IN THE ORDER OF `value` (never re-sorted). An option with no sourced label falls back to its raw code. null when the feature has no bounded vocabulary, or when no option has a label. THIS IS A DISPLAY STRING, NOT A PARALLEL ARRAY: empty tokens in `value` (e.g. \"a||b\") are dropped from the label, so the label count may be lower than `value.split(\"|\").length` and positions are NOT guaranteed to line up. STRICTLY ADDITIVE: `value` is still served verbatim and remains the parsable surface — parse `value`, display this.","example":"Carte Bleue - Visa, Chèques vacances"},"present":{"type":"boolean"},"verifiedAt":{"type":"string","nullable":true},"periods":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"recurrence":{"type":"string","enum":["none","annual"]},"label":{"type":"string","nullable":true}},"required":["from","to","recurrence","label"]}},"detail":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Curated amenity attributes (equipment facts only), filtered against the seeded detail schemas: proximity (on_site, distance_m), swimming (water_type, beach_type, supervised), measures (altitude_m, surface_ha), counts, durations, coverage, prices, opening hours… Keys mirror the JSON-LD PropertyValue attribute names. Never contains provenance or personal data: keys are allowlisted and free-text values carrying a contact (email, phone, URL) or exceeding 255 chars are omitted. null when no detail is collected.","example":{"on_site":false,"distance_m":300}}},"required":["code","name","category","value","valueLabel","present","verifiedAt","periods","detail"]}},"parameters":{}},"paths":{"/public/campings":{"get":{"tags":["public"],"summary":"Search campsites (read-only, public)","description":"Paginated, pruned campsite search over ~22,000 European campsites. Supports country, region/department, city, proximity (near=lat,lng,radiusKm, max 50km), establishment type (camping / service / airestat), amenities (features, max 10), stars, free text and school-holiday overlap (open_during). Establishment data only — no personal contact information is ever exposed. Rate-limited per IP.","parameters":[{"schema":{"type":"string","enum":["fre","eng","ger","spa","dut"],"default":"fre","description":"Language code (fre=French default, eng, ger, spa, dut).","example":"fre"},"required":false,"description":"Language code (fre=French default, eng, ger, spa, dut).","name":"lang","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code (e.g. FR).","example":"FR"},"required":false,"description":"ISO 3166-1 alpha-2 country code (e.g. FR).","name":"country","in":"query"},{"schema":{"type":"string","maxLength":8,"description":"Region admin1 code (e.g. '93').","example":"93"},"required":false,"description":"Region admin1 code (e.g. '93').","name":"region","in":"query"},{"schema":{"type":"string","maxLength":8,"description":"Department admin2 code (e.g. '39').","example":"39"},"required":false,"description":"Department admin2 code (e.g. '39').","name":"department","in":"query"},{"schema":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Numeric city id."},"required":false,"description":"Numeric city id.","name":"cityId","in":"query"},{"schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,\\d+(\\.\\d+)?$","description":"Geographic proximity filter as 'lat,lng,radiusKm' (decimal degrees, km). Returns campsites within radiusKm of the point, sorted by distance ascending. Max radius 50km. When set, `distanceKm` is populated.","example":"46.6186,5.7008,30"},"required":false,"description":"Geographic proximity filter as 'lat,lng,radiusKm' (decimal degrees, km). Returns campsites within radiusKm of the point, sorted by distance ascending. Max radius 50km. When set, `distanceKm` is populated.","name":"near","in":"query"},{"schema":{"type":"string","description":"Comma-separated campsite types (OR logic). Available: 'camping' (regular campsite), 'service' (aire de services for motorhomes), 'airestat' (aire de stationnement). Decoy types (piege/airservpie/airstatpie) are not meaningful here. Use the `byType` facet to discover counts. Max 6 values.","example":"camping,service"},"required":false,"description":"Comma-separated campsite types (OR logic). Available: 'camping' (regular campsite), 'service' (aire de services for motorhomes), 'airestat' (aire de stationnement). Decoy types (piege/airservpie/airstatpie) are not meaningful here. Use the `byType` facet to discover counts. Max 6 values.","name":"type","in":"query"},{"schema":{"type":"string","description":"Comma-separated feature codes the campsite must have (AND logic). Max 10 codes. Examples: 'wifi', 'pisplenair', 'piscchauf', 'animalemp'. Use the `commonFeatures` facet from any list response to discover codes with counts.","example":"wifi,pisplenair"},"required":false,"description":"Comma-separated feature codes the campsite must have (AND logic). Max 10 codes. Examples: 'wifi', 'pisplenair', 'piscchauf', 'animalemp'. Use the `commonFeatures` facet from any list response to discover codes with counts.","name":"features","in":"query"},{"schema":{"type":"string","description":"Comma-separated star ratings (0-5, OR logic). **0 = unclassified.** Max 6 values.","example":"0,4,5"},"required":false,"description":"Comma-separated star ratings (0-5, OR logic). **0 = unclassified.** Max 6 values.","name":"stars","in":"query"},{"schema":{"type":"string","enum":["paques","printemps","toussaint","noel","hiver","ete","ascension"],"description":"Restrict to campsites with a dated period overlapping a French school-holiday window. Pass the label only (server resolves the dates).","example":"paques"},"required":false,"description":"Restrict to campsites with a dated period overlapping a French school-holiday window. Pass the label only (server resolves the dates).","name":"open_during","in":"query"},{"schema":{"type":"string","enum":["A","B","C"],"description":"School zone for open_during."},"required":false,"description":"School zone for open_during.","name":"zone","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":120,"description":"Free-text search on the campsite name (and slug). Case-insensitive AND accent-insensitive: \"emeraude\" matches \"ÉMERAUDE\". Leading/trailing and repeated whitespace is collapsed ON BOTH SIDES — the term AND the stored name — so \"camp des gorges\" matches the stored \"CAMP  DES GORGES\"; a blank term is ignored (no filter). Wildcard characters are NOT supported: \"%\", \"_\" and \"\\\" are matched literally, so \"100% nature\" searches for that exact string. Results are ordered by relevance (exact name first, then names starting with the term, then the rest) — never by any commercial signal.","example":"emeraude"},"required":false,"description":"Free-text search on the campsite name (and slug). Case-insensitive AND accent-insensitive: \"emeraude\" matches \"ÉMERAUDE\". Leading/trailing and repeated whitespace is collapsed ON BOTH SIDES — the term AND the stored name — so \"camp des gorges\" matches the stored \"CAMP  DES GORGES\"; a blank term is ignored (no filter). Wildcard characters are NOT supported: \"%\", \"_\" and \"\\\" are matched literally, so \"100% nature\" searches for that exact string. Results are ordered by relevance (exact name first, then names starting with the term, then the rest) — never by any commercial signal.","name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":20,"description":"Page size 1-50."},"required":false,"description":"Page size 1-50.","name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"maximum":10000,"default":0,"description":"Pagination offset."},"required":false,"description":"Pagination offset.","name":"offset","in":"query"}],"responses":{"200":{"description":"Search results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSearchResponse"}}}},"400":{"description":"Invalid query.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicError"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicError"}}}}}}},"/public/campings/{slug}":{"get":{"tags":["public"],"summary":"Get a campsite by slug (read-only, public)","description":"Full public detail of one campsite: address, GPS, opening seasons, dated amenities (with last-verified dates) and price range. No personal contact data. Rate-limited per IP.","parameters":[{"schema":{"type":"string","description":"Campsite slug.","example":"les-claies"},"required":true,"description":"Campsite slug.","name":"slug","in":"path"},{"schema":{"type":"string","enum":["fre","eng","ger","spa","dut"],"default":"fre","description":"Language code (fre=French default, eng, ger, spa, dut).","example":"fre"},"required":false,"description":"Language code (fre=French default, eng, ger, spa, dut).","name":"lang","in":"query"}],"responses":{"200":{"description":"Campsite detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCampsite"}}}},"404":{"description":"No campsite for this slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicError"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicError"}}}}}}}}}