API du site#
Lisez et écrivez le contenu d'un site NAMES LEGAL via HTTPS : services, projets, articles de blog, membres de l'équipe, événements, offres d'emploi, FAQ et plus encore. L'API fait partie du module complémentaire Accès API du site.
Démarrage rapide#
- Dans la console du site, Add-ons & abonnement : activez Accès API.
- Modules → API : créez une clé. Copiez-la maintenant — elle ne s'affiche qu'une seule fois.
- Appelez l'API :
KEY="nlk_3fa9c2d1_Jx0..."
curl -H "Authorization: Api-Key $KEY" "https://ecolehorizon.names.legal/api/v1/team/?lang=fr"
{
"count": 12,
"next": "https://ecolehorizon.names.legal/api/v1/team/?lang=fr&page=2",
"previous": null,
"results": [
{"id": 4, "first_name": "Awa", "last_name": "Diop", "position": "Mathematics teacher",
"profile_image": "https://ecolehorizon.names.legal/media/demo/team/awa.jpg",
"updated_at": "2026-09-20T08:12:44+02:00"}
]
}
URL de base#
https://<site>/api/v1/
<site> est l'adresse propre du site (ecolehorizon.names.legal ou son domaine personnalisé). Il n'y a pas de préfixe de langue dans le chemin. v1 est la seule version ; un changement non rétrocompatible arriverait sous la forme v2, en parallèle.
L'URL racine liste toutes les collections. Une référence interactive (Swagger) et le schéma OpenAPI sont disponibles sur chaque site :
| URL | Accès | |
|---|---|---|
| Référence interactive | /api/v1/docs/ |
Clé API (tout scope) ou un compte console |
| Schéma OpenAPI 3 | /api/v1/schema/ |
Clé API (tout scope) ou un compte console |
Authentification#
- Le propriétaire du site active le module complémentaire Accès API.
- Dans la console, Modules → API, il crée une clé et choisit son scope. La clé complète est affichée une seule fois ; seuls un préfixe et un hachage sont conservés.
- Envoyez la clé avec chaque requête.
GET /api/v1/services/ HTTP/1.1
Host: ecolehorizon.names.legal
Authorization: Api-Key nlk_3fa9c2d1_Jx0...
Authorization: Bearer <key> et X-Api-Key: <key> sont également acceptés. Les clés ressemblent à nlk_<8 caractères hexadécimaux>_<secret>.
Scopes#
| Scope | Autorise |
|---|---|
read (par défaut) |
GET sur toutes les collections |
write |
GET, POST, PUT, PATCH |
Le scope s'applique à toute la clé ; il n'y a pas de scope par collection. Une clé peut être révoquée à tout moment depuis la console.
Erreurs que vous pouvez rencontrer#
| Statut | Signification |
|---|---|
401 |
Clé absente, inconnue ou révoquée : {"detail": "Invalid API key."} |
403 |
Le module complémentaire n'est pas actif, ou une clé read a tenté d'écrire |
404 |
/docs/ et /schema/ lorsque le module complémentaire n'est pas actif |
405 |
DELETE — la suppression se fait uniquement depuis la console |
429 |
Limite de débit dépassée |
400 |
Erreur de validation : {"field": ["message"]} |
Langue#
Les champs traduits sont renvoyés dans une langue à la fois. Choisissez-la avec ?lang= :
curl -H "Authorization: Api-Key $KEY" "https://ecolehorizon.names.legal/api/v1/services/?lang=fr"
La langue doit être l'une de celles publiées par le site ; sinon la réponse est 400. Sans lang, la langue par défaut du site est utilisée. La réponse porte un en-tête Content-Language.
Pagination, filtres et tri#
Les listes sont paginées par numéro de page :
{
"count": 42,
"next": "https://ecolehorizon.names.legal/api/v1/team/?page=2",
"previous": null,
"results": [ ... ]
}
| Paramètre | Effet |
|---|---|
page, page_size |
Numéro et taille de page (par défaut 25, maximum 100) |
search |
Recherche plein texte sur les principaux champs textuels de la collection |
ordering |
Champ de tri, - pour l'ordre décroissant : ?ordering=-updated_at |
| filtres de champ | Correspondance exacte, par exemple ?category__slug=bachelor&is_featured=true |
since |
Uniquement les fiches modifiées depuis une date : ?since=2026-09-01T00:00:00Z |
since rend la synchronisation incrémentale peu coûteuse : conservez l'horodatage de votre dernier appel et transmettez-le la fois suivante.
Limites de débit#
- 120 requêtes par minute et par clé API (chaque clé a son propre compteur).
- Sessions console : 120 par minute. Appels anonymes : 30 par minute.
Au-delà de la limite, l'API répond 429 ; patientez et réessayez.
Collections#
| Chemin | Méthodes | Remarques |
|---|---|---|
services/ |
GET, POST, PUT, PATCH | Catégorie via category_slug (facultatif) |
projects/ |
GET, POST, PUT, PATCH | category_slug obligatoire |
blog/ |
GET, POST, PUT, PATCH | Articles publiés uniquement ; category_slug obligatoire |
team/ |
GET, POST, PUT, PATCH | Membres actifs uniquement |
testimonials/ |
GET, POST, PUT, PATCH | Témoignages réels uniquement |
publications/ |
GET, POST, PUT, PATCH | category_slug facultatif |
resources/ |
GET, POST, PUT, PATCH | Le fichier lui-même se gère dans la console |
events/ |
GET, POST, PUT, PATCH | Dates au format ISO 8601 |
jobs/ |
GET, POST, PUT, PATCH | Offres actives uniquement ; category_slug obligatoire |
faq/ |
GET, POST, PUT, PATCH | category_slug facultatif |
bookable-items/ |
GET | Nécessite le module complémentaire Appointments |
bookings/ |
POST | Crée une réservation ; nécessite une clé write |
Chaque fiche est accessible à <collection>/<id>/. Les champs de chaque collection sont listés dans la référence ci-dessous.
Écriture#
POSTcrée,PUTremplace,PATCHmet à jour certains champs.- Les catégories sont lues comme un objet
{"id", "name", "slug"}et écrites aveccategory_slug. Un slug inconnu est refusé. - Les images et fichiers sont en lecture seule dans l'API : chargez-les depuis la console, ou laissez le connecteur de données les télécharger depuis une URL.
- Le HTML envoyé dans les champs de texte enrichi est nettoyé : les scripts et gestionnaires d'événements sont retirés.
curl -X POST "https://ecolehorizon.names.legal/api/v1/faq/?lang=fr" \
-H "Authorization: Api-Key $KEY" -H "Content-Type: application/json" \
-d '{"question": "Quand ont lieu les inscriptions ?", "answer": "<p>Du 1er au 30 juin.</p>"}'
Réserver un rendez-vous#
curl -X POST "https://ecolehorizon.names.legal/api/v1/bookings/" \
-H "Authorization: Api-Key $KEY" -H "Content-Type: application/json" \
-d '{"item": 3, "customer_name": "Awa Diop", "customer_email": "awa@example.com",
"date": "2026-10-02", "time": "14:30"}'
time est exprimé dans le fuseau horaire du site. Un créneau qui n'est plus disponible est refusé avec 400. Les nouvelles réservations démarrent au statut pending.
Recettes#
Reproduire une collection de façon incrémentale#
Lisez tout une première fois, puis seulement ce qui a changé. Conservez l'horodatage de votre précédente exécution et transmettez-le comme since.
Python
import time
import requests
BASE = "https://ecolehorizon.names.legal/api/v1/"
HEADERS = {"Authorization": "Api-Key nlk_3fa9c2d1_Jx0..."}
def fetch_all(collection, since=None, lang="fr"):
url = f"{BASE}{collection}/"
params = {"lang": lang, "page_size": 100, "ordering": "updated_at"}
if since:
params["since"] = since
while url:
response = requests.get(url, headers=HEADERS, params=params, timeout=15)
if response.status_code == 429:
time.sleep(int(response.headers.get("Retry-After", "5")))
continue
response.raise_for_status()
data = response.json()
yield from data["results"]
url, params = data["next"], None # "next" already carries the parameters
for member in fetch_all("team", since="2026-09-01T00:00:00Z"):
print(member["id"], member["first_name"], member["last_name"])
JavaScript (Node.js 18+)
const BASE = "https://ecolehorizon.names.legal/api/v1/";
const HEADERS = { Authorization: "Api-Key nlk_3fa9c2d1_Jx0..." };
async function* fetchAll(collection, since, lang = "fr") {
const first = new URL(`${BASE}${collection}/`);
first.search = new URLSearchParams({ lang, page_size: "100", ordering: "updated_at",
...(since ? { since } : {}) });
let url = first.toString();
while (url) {
const response = await fetch(url, { headers: HEADERS });
if (response.status === 429) {
const wait = Number(response.headers.get("Retry-After") || 5);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
continue;
}
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const data = await response.json();
yield* data.results;
url = data.next;
}
}
for await (const post of fetchAll("blog", "2026-09-01T00:00:00Z")) {
console.log(post.id, post.title);
}
Créer ou mettre à jour une fiche#
PHP
<?php
$base = 'https://ecolehorizon.names.legal/api/v1/';
$headers = ['Authorization: Api-Key nlk_3fa9c2d1_Jx0...', 'Content-Type: application/json'];
function call($method, $url, $headers, $body = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$answer = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
return [$status, json_decode($answer, true)];
}
// Create an event (in French)
[$status, $event] = call('POST', $base . 'events/?lang=fr', $headers, [
'title' => 'Journée portes ouvertes',
'slug' => 'journee-portes-ouvertes-2026',
'description' => '<p>Visite du campus et rencontre avec les enseignants.</p>',
'start_date' => '2026-11-14T09:00:00+01:00',
'end_date' => '2026-11-14T16:00:00+01:00',
'location' => 'Campus de Genève',
]);
// Change only the location later
call('PATCH', $base . "events/{$event['id']}/?lang=fr", $headers, ['location' => 'Aula']);
lang détermine la langue dans laquelle un champ traduit est écrit : renvoyez la même fiche avec ?lang=en pour ajouter la version anglaise.
Erreurs et limites en pratique#
| Situation | Que faire |
|---|---|
429 Too Many Requests |
Attendez le nombre de secondes indiqué dans l'en-tête Retry-After, puis réessayez. |
400 à l'écriture |
Lisez le corps de la réponse : chaque champ liste ses problèmes, {"category_slug": ["Object with slug=x does not exist."]}. |
403 sur une requête write |
La clé est read : créez une clé read and write. |
401 |
La clé a été révoquée ou mal saisie. |
Erreur réseau ou 5xx |
Réessayez avec un délai croissant (1 s, 2 s, 4 s…). Les lectures peuvent être répétées sans risque ; pour un POST, vérifiez d'abord que la fiche n'a pas été créée. |
Référence par collection#
Générée à partir du code de l'API : les champs, droits d'accès et paramètres de requête sont toujours ceux de la plateforme en cours d'exécution. Les champs traduits sont lus et écrits dans la langue choisie avec lang.
/api/v1/services/
GETPOSTPUTPATCH
- Filtres
slugcategory__slugis_featured - search
titledescription - ordering
ordertitleupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 200 | lecture et écriture |
title |
string ≤ 100 | lecture et écriture obligatoire à la création |
description |
string | lecture et écriture obligatoire à la création |
icon |
string ≤ 50 | lecture et écriture obligatoire à la création |
color |
string ≤ 25 | lecture et écriture |
category |
object | lecture seule |
category_slug |
slug | écriture seule |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/projects/
GETPOSTPUTPATCH
- Filtres
slugcategory__slugstatusis_featured - search
titledescriptionclienttechnologies - ordering
ordertitleproject_dateupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 50 | lecture et écriture obligatoire à la création |
title |
string ≤ 150 | lecture et écriture obligatoire à la création |
description |
string | lecture et écriture obligatoire à la création |
detailed_description |
string | lecture et écriture |
category |
object | lecture seule |
category_slug |
slug | écriture seule obligatoire à la création |
client |
string ≤ 100 | lecture et écriture |
status |
choicecompleted, in_progress, on_hold, planning |
lecture et écriture |
project_date |
string ≤ 20 | lecture et écriture |
duration |
string ≤ 50 | lecture et écriture |
team_size |
string ≤ 50 | lecture et écriture |
technologies |
string ≤ 200 | lecture et écriture |
tags |
string ≤ 200 | lecture et écriture |
image |
file (URL) | lecture seule |
demo_url |
url ≤ 200 | lecture et écriture |
project_url |
url ≤ 200 | lecture et écriture |
github_url |
url ≤ 200 | lecture et écriture |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
created_at |
datetime | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/blog/
GETPOSTPUTPATCH
- Filtres
slugcategory__slugstatustags__slug - search
titleexcerptcontent - ordering
published_datetitleupdated_at(par défaut-published_date)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 200 | lecture et écriture obligatoire à la création |
title |
string ≤ 200 | lecture et écriture obligatoire à la création |
excerpt |
string ≤ 500 | lecture et écriture obligatoire à la création |
content |
string | lecture et écriture obligatoire à la création |
category |
object | lecture seule |
category_slug |
slug | écriture seule obligatoire à la création |
tags |
list of slug | lecture seule |
featured_image |
file (URL) | lecture seule |
published_date |
datetime | lecture et écriture |
read_time |
integer | lecture et écriture |
status |
choicedraft, published, featured |
lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/team/
GETPOSTPUTPATCH
- Filtres
member_typeis_featured - search
first_namelast_namepositionspecialties - ordering
orderlast_nameupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
first_name |
string ≤ 100 | lecture et écriture obligatoire à la création |
last_name |
string ≤ 100 | lecture et écriture obligatoire à la création |
position |
string ≤ 150 | lecture et écriture obligatoire à la création |
short_bio |
string ≤ 300 | lecture et écriture |
bio |
string | lecture et écriture |
profile_image |
file (URL) | lecture seule |
email |
email ≤ 254 | lecture et écriture |
phone |
string ≤ 20 | lecture et écriture |
linkedin |
url ≤ 200 | lecture et écriture |
github |
url ≤ 200 | lecture et écriture |
twitter |
url ≤ 200 | lecture et écriture |
instagram |
url ≤ 200 | lecture et écriture |
facebook |
url ≤ 200 | lecture et écriture |
website |
url ≤ 200 | lecture et écriture |
specialties |
string ≤ 200 | lecture et écriture |
years_experience |
integer | lecture et écriture |
education |
string ≤ 200 | lecture et écriture |
location |
string ≤ 100 | lecture et écriture |
languages |
string ≤ 100 | lecture et écriture |
member_type |
choicefounder, lead, senior, developer, designer, manager, consultant, intern |
lecture et écriture |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
updated_at |
datetime | lecture seule |
/api/v1/testimonials/
GETPOSTPUTPATCH
- Filtres
rating - search
namepositiontestimonial - ordering
orderratingupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
name |
string ≤ 100 | lecture et écriture obligatoire à la création |
position |
string ≤ 100 | lecture et écriture obligatoire à la création |
testimonial |
string | lecture et écriture obligatoire à la création |
rating |
integer | lecture et écriture |
image |
file (URL) | lecture seule |
order |
integer | lecture et écriture |
updated_at |
datetime | lecture seule |
/api/v1/publications/
GETPOSTPUTPATCH
- Filtres
slugcategory__slugyearis_featured - search
titleauthorsjournalabstract - ordering
orderyeartitleupdated_at(par défautorder,-year)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 200 | lecture et écriture |
title |
string ≤ 255 | lecture et écriture obligatoire à la création |
authors |
string ≤ 255 | lecture et écriture obligatoire à la création |
journal |
string ≤ 255 | lecture et écriture |
year |
string ≤ 10 | lecture et écriture obligatoire à la création |
abstract |
string | lecture et écriture |
link |
url ≤ 200 | lecture et écriture |
category |
object | lecture seule |
category_slug |
slug | écriture seule |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/resources/
GETPOSTPUTPATCH
- Filtres
slugresource_typeis_free - search
titledescription - ordering
ordertitlecreated_atupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 200 | lecture et écriture obligatoire à la création |
title |
string ≤ 200 | lecture et écriture obligatoire à la création |
description |
string | lecture et écriture obligatoire à la création |
resource_type |
choiceguide, ebook, whitepaper, template, checklist, case_study |
lecture et écriture |
file |
file (URL) | lecture seule |
file_size |
string | lecture seule |
thumbnail |
file (URL) | lecture seule |
is_free |
boolean | lecture et écriture |
requires_email |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
created_at |
datetime | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/events/
GETPOSTPUTPATCH
- Filtres
slugevent_typeis_onlineis_featured - search
titledescriptionlocation - ordering
start_datetitleupdated_at(par défaut-start_date)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 200 | lecture et écriture obligatoire à la création |
title |
string ≤ 200 | lecture et écriture obligatoire à la création |
description |
string | lecture et écriture obligatoire à la création |
event_type |
choiceconference, workshop, webinar, seminar, training, meeting |
lecture et écriture |
start_date |
datetime | lecture et écriture obligatoire à la création |
end_date |
datetime | lecture et écriture obligatoire à la création |
timezone |
string ≤ 64 | lecture et écriture |
location |
string ≤ 200 | lecture et écriture obligatoire à la création |
is_online |
boolean | lecture et écriture |
meeting_url |
url ≤ 200 | lecture et écriture |
registration_url |
url ≤ 200 | lecture et écriture |
registration_deadline |
datetime | lecture et écriture |
max_attendees |
integer | lecture et écriture |
featured_image |
file (URL) | lecture seule |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
created_at |
datetime | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/jobs/
GETPOSTPUTPATCH
- Filtres
slugcategory__slugcontract_typeremote_optionexperience_levelis_featuredis_urgent - search
titledescriptionlocationskills_required - ordering
published_datetitleupdated_at(par défaut-published_date)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
slug |
string ≤ 50 | lecture et écriture |
title |
string ≤ 200 | lecture et écriture obligatoire à la création |
description |
string | lecture et écriture obligatoire à la création |
responsibilities |
string | lecture et écriture |
requirements |
string | lecture et écriture obligatoire à la création |
qualifications |
string | lecture et écriture |
benefits |
string | lecture et écriture |
category |
object | lecture seule |
category_slug |
slug | écriture seule obligatoire à la création |
location |
string ≤ 200 | lecture et écriture obligatoire à la création |
contract_type |
choicefull_time, part_time, contract, freelance, internship, temporary |
lecture et écriture |
remote_option |
choiceonsite, remote, hybrid |
lecture et écriture |
experience_level |
choiceentry, junior, mid, senior, lead, executive |
lecture et écriture |
education_level |
string ≤ 100 | lecture et écriture |
languages_required |
string ≤ 200 | lecture et écriture |
skills_required |
string | lecture et écriture |
skills_preferred |
string | lecture et écriture |
tags |
string ≤ 500 | lecture et écriture |
salary_min |
decimal | lecture et écriture |
salary_max |
decimal | lecture et écriture |
salary_currency |
string ≤ 3 | lecture et écriture |
salary_period |
choicehour, month, year |
lecture et écriture |
expected_start_date |
date | lecture et écriture |
application_deadline |
date | lecture et écriture |
is_active |
boolean | lecture et écriture |
is_featured |
boolean | lecture et écriture |
is_urgent |
boolean | lecture et écriture |
published_date |
datetime | lecture seule |
seo_title |
string ≤ 70 | lecture et écriture |
seo_description |
string ≤ 160 | lecture et écriture |
og_image |
id | lecture seule |
created_at |
datetime | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/faq/
GETPOSTPUTPATCH
- Filtres
category__slugis_featured - search
questionanswer - ordering
orderupdated_at(par défautorder)
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
question |
string ≤ 300 | lecture et écriture obligatoire à la création |
answer |
string | lecture et écriture obligatoire à la création |
category |
object | lecture seule |
category_slug |
slug | écriture seule |
is_featured |
boolean | lecture et écriture |
order |
integer | lecture et écriture |
updated_at |
datetime | lecture seule |
/api/v1/bookable-items/
GET
| Champ | Type de champ | Accès |
|---|---|---|
id |
integer | lecture seule |
label |
string | lecture seule |
duration_minutes |
integer | lecture seule |
price |
decimal | lecture seule |
currency |
string | lecture seule |
updated_at |
datetime | lecture seule |
/api/v1/bookings/
POST
| Champ | Type de champ | Accès |
|---|---|---|
item |
integer | lecture et écriture obligatoire à la création |
customer_name |
string ≤ 120 | lecture et écriture obligatoire à la création |
customer_email |
lecture et écriture obligatoire à la création | |
customer_phone |
string ≤ 40 | lecture et écriture |
date |
date | lecture et écriture obligatoire à la création |
time |
string | lecture et écriture obligatoire à la création |
note |
string | lecture et écriture |
Recommandations de sécurité#
- Conservez les clés sur votre serveur. Ne diffusez jamais une clé dans une application mobile ou dans du code exécuté dans le navigateur.
- Utilisez une clé
readpartout où vous n'avez pas besoin d'écrire. - Une clé par intégration : en révoquer une ne casse pas les autres.