Developers

API del sitio#

Lea y escriba el contenido de un sitio NAMES LEGAL por HTTPS: servicios, proyectos, entradas de blog, miembros del equipo, eventos, empleos, preguntas frecuentes y más. La API forma parte del add-on Acceso API del sitio.

Inicio rápido#

  1. En la consola del sitio, Complementos y suscripción: active Acceso API.
  2. Módulos → API: cree una clave. Cópiela ahora: se muestra una sola vez.
  3. Llame a la 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 base#

https://<site>/api/v1/

<site> es la dirección propia del sitio (ecolehorizon.names.legal o su dominio personalizado). No hay prefijo de idioma en la ruta. v1 es la única versión; un cambio incompatible llegaría como v2 junto a ella.

La URL raíz lista todas las colecciones. Una referencia interactiva (Swagger) y el esquema OpenAPI están disponibles en cada sitio:

URL Acceso
Referencia interactiva /api/v1/docs/ Clave API (cualquier alcance) o una cuenta de consola
Esquema OpenAPI 3 /api/v1/schema/ Clave API (cualquier alcance) o una cuenta de consola

Autenticación#

  1. El propietario del sitio activa el add-on Acceso API.
  2. En la consola, Módulos → API, crea una clave y elige su alcance. La clave completa se muestra una sola vez; solo se almacenan un prefijo y un hash.
  3. Envíe la clave con cada solicitud.
GET /api/v1/services/ HTTP/1.1
Host: ecolehorizon.names.legal
Authorization: Api-Key nlk_3fa9c2d1_Jx0...

Authorization: Bearer <key> y X-Api-Key: <key> también se aceptan. Las claves tienen el formato nlk_<8 caracteres hexadecimales>_<secreto>.

Alcances#

Alcance Permite
read (por defecto) GET en todas las colecciones
write GET, POST, PUT, PATCH

El alcance se aplica a toda la clave; no existe un alcance por colección. Una clave puede revocarse en cualquier momento desde la consola.

Errores que puede encontrar#

Estado Significado
401 Clave ausente, desconocida o revocada: {"detail": "Invalid API key."}
403 El add-on no está activo, o una clave read intentó escribir
404 /docs/ y /schema/ cuando el add-on no está activo
405 DELETE — la eliminación se hace solo desde la consola
429 Límite de solicitudes superado
400 Error de validación: {"field": ["message"]}

Idioma#

Los campos traducidos se devuelven en un idioma a la vez. Elíjalo con ?lang=:

curl -H "Authorization: Api-Key $KEY" "https://ecolehorizon.names.legal/api/v1/services/?lang=fr"

El idioma debe ser uno de los idiomas publicados por el sitio; de lo contrario la respuesta es 400. Sin lang, se usa el idioma predeterminado del sitio. La respuesta incluye un encabezado Content-Language.

Paginación, filtros y ordenación#

Las listas están paginadas por número de página:

{
  "count": 42,
  "next": "https://ecolehorizon.names.legal/api/v1/team/?page=2",
  "previous": null,
  "results": [ ... ]
}
Parámetro Efecto
page, page_size Número y tamaño de página (25 por defecto, máximo 100)
search Búsqueda de texto completo en los campos de texto principales de la colección
ordering Campo de ordenación, - para descendente: ?ordering=-updated_at
filtros de campo Coincidencia exacta, por ejemplo ?category__slug=bachelor&is_featured=true
since Solo fichas modificadas desde una fecha: ?since=2026-09-01T00:00:00Z

since hace que la sincronización incremental sea económica: guarde la hora de su última llamada y pásela la próxima vez.

Límites de solicitudes#

  • 120 solicitudes por minuto y por clave API (cada clave tiene su propio contador).
  • Sesiones de consola: 120 por minuto. Llamadas anónimas: 30 por minuto.

Superado el límite, la API responde 429; espere y reintente.

Colecciones#

Ruta Métodos Notas
services/ GET, POST, PUT, PATCH Categoría por category_slug (opcional)
projects/ GET, POST, PUT, PATCH category_slug obligatorio
blog/ GET, POST, PUT, PATCH Solo entradas publicadas; category_slug obligatorio
team/ GET, POST, PUT, PATCH Solo miembros activos
testimonials/ GET, POST, PUT, PATCH Solo testimonios reales
publications/ GET, POST, PUT, PATCH category_slug opcional
resources/ GET, POST, PUT, PATCH El archivo en sí se gestiona en la consola
events/ GET, POST, PUT, PATCH Fechas en ISO 8601
jobs/ GET, POST, PUT, PATCH Solo vacantes activas; category_slug obligatorio
faq/ GET, POST, PUT, PATCH category_slug opcional
bookable-items/ GET Requiere el add-on de Citas
bookings/ POST Crea una reserva; requiere una clave write

Cada ficha es accesible en <collection>/<id>/. Los campos de cada colección están listados en la referencia siguiente.

Escritura#

  • POST crea, PUT reemplaza, PATCH actualiza algunos campos.
  • Las categorías se leen como un objeto {"id", "name", "slug"} y se escriben con category_slug. Un slug desconocido se rechaza.
  • Las imágenes y los archivos son de solo lectura en la API: súbalos en la consola, o deje que el conector de datos los descargue desde una URL.
  • El HTML enviado en campos de texto enriquecido se limpia: se eliminan scripts y manejadores de eventos.
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>"}'

Reservar una cita#

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á en la zona horaria del sitio. Un horario que ya no esté disponible se rechaza con 400. Las nuevas reservas comienzan como pending.

Recetas#

Sincronizar una colección de forma incremental#

Lea todo una vez, luego solo lo que haya cambiado. Guarde la hora de su ejecución anterior y páselo como 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);
}

Crear o actualizar una ficha#

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 decide en qué idioma se escribe un campo traducido: envíe la misma ficha de nuevo con ?lang=en para añadir la versión en inglés.

Errores y límites en la práctica#

Situación Qué hacer
429 Too Many Requests Espere el número de segundos indicado en el encabezado Retry-After, luego reintente.
400 al escribir Lea el cuerpo: cada campo enumera sus problemas, {"category_slug": ["Object with slug=x does not exist."]}.
403 con una solicitud write La clave es read: cree una clave read and write.
401 La clave fue revocada o escrita de forma incorrecta.
Error de red o 5xx Reintente con un retardo creciente (1 s, 2 s, 4 s…). Las lecturas se pueden reintentar de forma segura; para POST, compruebe primero que la ficha no se haya creado ya.

Referencia por colección#

Generada a partir del código de la API: los campos, los derechos de acceso y los parámetros de consulta son siempre los de la plataforma en ejecución. Los campos traducidos se leen y escriben en el idioma elegido con lang.

/api/v1/services/

GETPOSTPUTPATCH

  • Filtros slug category__slug is_featured
  • search title description
  • ordering order title updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 200 lectura y escritura
title string ≤ 100 lectura y escritura obligatorio al crear
description string lectura y escritura obligatorio al crear
icon string ≤ 50 lectura y escritura obligatorio al crear
color string ≤ 25 lectura y escritura
category object solo lectura
category_slug slug solo escritura
is_featured boolean lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
updated_at datetime solo lectura

/api/v1/projects/

GETPOSTPUTPATCH

  • Filtros slug category__slug status is_featured
  • search title description client technologies
  • ordering order title project_date updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 50 lectura y escritura obligatorio al crear
title string ≤ 150 lectura y escritura obligatorio al crear
description string lectura y escritura obligatorio al crear
detailed_description string lectura y escritura
category object solo lectura
category_slug slug solo escritura obligatorio al crear
client string ≤ 100 lectura y escritura
status choice
completed, in_progress, on_hold, planning
lectura y escritura
project_date string ≤ 20 lectura y escritura
duration string ≤ 50 lectura y escritura
team_size string ≤ 50 lectura y escritura
technologies string ≤ 200 lectura y escritura
tags string ≤ 200 lectura y escritura
image file (URL) solo lectura
demo_url url ≤ 200 lectura y escritura
project_url url ≤ 200 lectura y escritura
github_url url ≤ 200 lectura y escritura
is_featured boolean lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
created_at datetime solo lectura
updated_at datetime solo lectura

/api/v1/blog/

GETPOSTPUTPATCH

  • Filtros slug category__slug status tags__slug
  • search title excerpt content
  • ordering published_date title updated_at (por defecto -published_date)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 200 lectura y escritura obligatorio al crear
title string ≤ 200 lectura y escritura obligatorio al crear
excerpt string ≤ 500 lectura y escritura obligatorio al crear
content string lectura y escritura obligatorio al crear
category object solo lectura
category_slug slug solo escritura obligatorio al crear
tags list of slug solo lectura
featured_image file (URL) solo lectura
published_date datetime lectura y escritura
read_time integer lectura y escritura
status choice
draft, published, featured
lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
updated_at datetime solo lectura

/api/v1/team/

GETPOSTPUTPATCH

  • Filtros member_type is_featured
  • search first_name last_name position specialties
  • ordering order last_name updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
first_name string ≤ 100 lectura y escritura obligatorio al crear
last_name string ≤ 100 lectura y escritura obligatorio al crear
position string ≤ 150 lectura y escritura obligatorio al crear
short_bio string ≤ 300 lectura y escritura
bio string lectura y escritura
profile_image file (URL) solo lectura
email email ≤ 254 lectura y escritura
phone string ≤ 20 lectura y escritura
linkedin url ≤ 200 lectura y escritura
github url ≤ 200 lectura y escritura
twitter url ≤ 200 lectura y escritura
instagram url ≤ 200 lectura y escritura
facebook url ≤ 200 lectura y escritura
website url ≤ 200 lectura y escritura
specialties string ≤ 200 lectura y escritura
years_experience integer lectura y escritura
education string ≤ 200 lectura y escritura
location string ≤ 100 lectura y escritura
languages string ≤ 100 lectura y escritura
member_type choice
founder, lead, senior, developer, designer, manager, consultant, intern
lectura y escritura
is_featured boolean lectura y escritura
order integer lectura y escritura
updated_at datetime solo lectura

/api/v1/testimonials/

GETPOSTPUTPATCH

  • Filtros rating
  • search name position testimonial
  • ordering order rating updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
name string ≤ 100 lectura y escritura obligatorio al crear
position string ≤ 100 lectura y escritura obligatorio al crear
testimonial string lectura y escritura obligatorio al crear
rating integer lectura y escritura
image file (URL) solo lectura
order integer lectura y escritura
updated_at datetime solo lectura

/api/v1/publications/

GETPOSTPUTPATCH

  • Filtros slug category__slug year is_featured
  • search title authors journal abstract
  • ordering order year title updated_at (por defecto order,-year)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 200 lectura y escritura
title string ≤ 255 lectura y escritura obligatorio al crear
authors string ≤ 255 lectura y escritura obligatorio al crear
journal string ≤ 255 lectura y escritura
year string ≤ 10 lectura y escritura obligatorio al crear
abstract string lectura y escritura
link url ≤ 200 lectura y escritura
category object solo lectura
category_slug slug solo escritura
is_featured boolean lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
updated_at datetime solo lectura

/api/v1/resources/

GETPOSTPUTPATCH

  • Filtros slug resource_type is_free
  • search title description
  • ordering order title created_at updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 200 lectura y escritura obligatorio al crear
title string ≤ 200 lectura y escritura obligatorio al crear
description string lectura y escritura obligatorio al crear
resource_type choice
guide, ebook, whitepaper, template, checklist, case_study
lectura y escritura
file file (URL) solo lectura
file_size string solo lectura
thumbnail file (URL) solo lectura
is_free boolean lectura y escritura
requires_email boolean lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
created_at datetime solo lectura
updated_at datetime solo lectura

/api/v1/events/

GETPOSTPUTPATCH

  • Filtros slug event_type is_online is_featured
  • search title description location
  • ordering start_date title updated_at (por defecto -start_date)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 200 lectura y escritura obligatorio al crear
title string ≤ 200 lectura y escritura obligatorio al crear
description string lectura y escritura obligatorio al crear
event_type choice
conference, workshop, webinar, seminar, training, meeting
lectura y escritura
start_date datetime lectura y escritura obligatorio al crear
end_date datetime lectura y escritura obligatorio al crear
timezone string ≤ 64 lectura y escritura
location string ≤ 200 lectura y escritura obligatorio al crear
is_online boolean lectura y escritura
meeting_url url ≤ 200 lectura y escritura
registration_url url ≤ 200 lectura y escritura
registration_deadline datetime lectura y escritura
max_attendees integer lectura y escritura
featured_image file (URL) solo lectura
is_featured boolean lectura y escritura
order integer lectura y escritura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
created_at datetime solo lectura
updated_at datetime solo lectura

/api/v1/jobs/

GETPOSTPUTPATCH

  • Filtros slug category__slug contract_type remote_option experience_level is_featured is_urgent
  • search title description location skills_required
  • ordering published_date title updated_at (por defecto -published_date)
CampoTipo de campoAcceso
id integer solo lectura
slug string ≤ 50 lectura y escritura
title string ≤ 200 lectura y escritura obligatorio al crear
description string lectura y escritura obligatorio al crear
responsibilities string lectura y escritura
requirements string lectura y escritura obligatorio al crear
qualifications string lectura y escritura
benefits string lectura y escritura
category object solo lectura
category_slug slug solo escritura obligatorio al crear
location string ≤ 200 lectura y escritura obligatorio al crear
contract_type choice
full_time, part_time, contract, freelance, internship, temporary
lectura y escritura
remote_option choice
onsite, remote, hybrid
lectura y escritura
experience_level choice
entry, junior, mid, senior, lead, executive
lectura y escritura
education_level string ≤ 100 lectura y escritura
languages_required string ≤ 200 lectura y escritura
skills_required string lectura y escritura
skills_preferred string lectura y escritura
tags string ≤ 500 lectura y escritura
salary_min decimal lectura y escritura
salary_max decimal lectura y escritura
salary_currency string ≤ 3 lectura y escritura
salary_period choice
hour, month, year
lectura y escritura
expected_start_date date lectura y escritura
application_deadline date lectura y escritura
is_active boolean lectura y escritura
is_featured boolean lectura y escritura
is_urgent boolean lectura y escritura
published_date datetime solo lectura
seo_title string ≤ 70 lectura y escritura
seo_description string ≤ 160 lectura y escritura
og_image id solo lectura
created_at datetime solo lectura
updated_at datetime solo lectura

/api/v1/faq/

GETPOSTPUTPATCH

  • Filtros category__slug is_featured
  • search question answer
  • ordering order updated_at (por defecto order)
CampoTipo de campoAcceso
id integer solo lectura
question string ≤ 300 lectura y escritura obligatorio al crear
answer string lectura y escritura obligatorio al crear
category object solo lectura
category_slug slug solo escritura
is_featured boolean lectura y escritura
order integer lectura y escritura
updated_at datetime solo lectura

/api/v1/bookable-items/

GET

CampoTipo de campoAcceso
id integer solo lectura
label string solo lectura
duration_minutes integer solo lectura
price decimal solo lectura
currency string solo lectura
updated_at datetime solo lectura

/api/v1/bookings/

POST

CampoTipo de campoAcceso
item integer lectura y escritura obligatorio al crear
customer_name string ≤ 120 lectura y escritura obligatorio al crear
customer_email email lectura y escritura obligatorio al crear
customer_phone string ≤ 40 lectura y escritura
date date lectura y escritura obligatorio al crear
time string lectura y escritura obligatorio al crear
note string lectura y escritura

Recomendaciones de seguridad#

  • Mantenga las claves en su servidor. Nunca incluya una clave en una aplicación móvil ni en código de navegador.
  • Use una clave read siempre que no necesite escribir.
  • Una clave por integración: revocar una no afecta a las demás.