Salta ai contenuti

API del sito

Con l’API del sito il sito della radio prende dal pannello programmi, palinsesto (con le variazioni) e speaker, senza ricopiarli a mano. È pensata per i siti statici (Astro, Next.js, Hugo, Eleventy…) che leggono i dati al momento della build, sul server: la chiave non deve mai arrivare al browser.

La chiave la crea la direzione della radio (Station Manager, Direttore, Editore).

  1. Nel pannello apri Palinsesto → Impostazioni e trova il riquadro Sito della radio.
  2. Premi Crea la chiave. La chiave si vede una volta sola: copiala subito.
  3. Mettila nelle variabili d’ambiente del sito (es. RADIOPANEL_API_KEY), mai nel codice delle pagine.

Nello stesso riquadro trovi l’Indirizzo dell’API, le ultime cifre della chiave attiva, Crea una nuova chiave (quella vecchia smette subito di funzionare) e Revoca. Senza chiave i dati non escono dal pannello. La chiave identifica anche la radio: non serve indicarla nella richiesta.

GET https://pannello.example.it/public/sito/api.php?risorsa=tutto
X-Api-Key: rps_la-tua-chiave

La chiave comincia sempre con rps_.

  • La chiave va nell’intestazione X-Api-Key, oppure in Authorization: Bearer <chiave>.
  • Sono accettati solo GET e HEAD.
  • L’API non risponde ai browser di altri siti (nessuna intestazione CORS): chiamala dal server o dallo script di build.
// src/lib/radiopanel.js - letto una volta durante la build
const API = 'https://pannello.example.it/public/sito/api.php';
export async function datiRadio() {
const res = await fetch(`${API}?risorsa=tutto`, {
headers: { 'X-Api-Key': import.meta.env.RADIOPANEL_API_KEY },
});
if (!res.ok) throw new Error(`RadioPanel: errore ${res.status}`);
return res.json();
}

Il parametro risorsa sceglie cosa leggere:

risorsa Contenuto
tutto (predefinito) radio, programmi, speaker, palinsesto e adesso in una sola risposta. Una build di solito fa solo questa chiamata.
radio I dati della radio.
programmi I programmi non archiviati.
speaker Le persone con il profilo pubblico attivo.
palinsesto Settimana tipo e prossimi 7 giorni con le variazioni.
adesso Programma in onda, prossimo e brano, al momento della chiamata.

Ogni risposta riuscita ha success, la versione del formato (oggi 1) e generato (data e ora della risposta, ISO 8601), più le chiavi della risorsa chiesta.

Codice Quando
401 Chiave mancante o non valida (anche se la radio è disabilitata).
404 risorsa non valida.
405 Metodo diverso da GET/HEAD.
429 Troppe chiavi sbagliate dallo stesso indirizzo: aspetta i secondi indicati in Retry-After.
500 Errore interno: riprova tra qualche minuto.

Gli errori rispondono con { "success": false, "message": "…" }.

Esempio abbreviato di ?risorsa=tutto:

{
"success": true,
"versione": 1,
"generato": "2026-11-14T10:00:00+01:00",
"radio": {
"nome": "Radio Esempio",
"nome_breve": "RE",
"codename": "radioesempio",
"sito": "https://www.radioesempio.it",
"streaming": "https://stream.radioesempio.it/live",
"nowplaying": "https://azuracast.radioesempio.it/api/nowplaying/1",
"whatsapp": "+39 333 1234567",
"fuso_orario": "Europe/Rome",
"richieste": "https://pannello.example.it/public/rpigroup/request_radioesempio",
"statistiche": {
"script": "https://pannello.example.it/public/sito/statistiche.js",
"radio": "radioesempio",
"codice": "<script defer src=\"https://pannello.example.it/public/sito/statistiche.js\" data-radio=\"radioesempio\"></script>"
}
},
"programmi": [
{
"id": 12,
"slug": "buongiorno-radio",
"titolo": "Buongiorno Radio",
"sottotitolo": "La sveglia della città",
"immagine": "https://pannello.example.it/public/sito/immagine?f=…",
"descrizione_html": "<p>Notizie, musica e ospiti.</p>",
"descrizione_testo": "Notizie, musica e ospiti.",
"conduttori": [
{ "nome": "Mario Rossi", "speaker": "mario-rossi" },
{ "nome": "Luca", "speaker": null }
]
}
],
"speaker": [
{
"id": 7,
"slug": "mario-rossi",
"nome": "Mario Rossi",
"ruolo": "Conduttore del mattino",
"ruolo_pubblico": "Conduttore del mattino",
"ruolo_base": "Speaker",
"ruolo_preciso": "",
"bio": "In radio dal 2010.",
"immagine": "https://pannello.example.it/public/sito/immagine?f=…",
"social": { "instagram": "https://www.instagram.com/mario.rossi" },
"programmi": ["buongiorno-radio"]
}
],
"palinsesto": {
"settimana": [
{
"giorno": 1, "giorno_nome": "Lunedì", "inizio": "07:00", "fine": "09:00",
"titolo": "Buongiorno Radio", "programma": "buongiorno-radio",
"speaker": { "nome": "Mario Rossi", "speaker": "mario-rossi" }
}
],
"prossimi_giorni": [
{
"giorno": 1, "giorno_nome": "Lunedì", "inizio": "07:00", "fine": "09:00",
"titolo": "Buongiorno Radio", "programma": "buongiorno-radio",
"speaker": { "nome": "Mario Rossi", "speaker": "mario-rossi" },
"data": "2026-11-16",
"inizio_iso": "2026-11-16T07:00:00+01:00",
"fine_iso": "2026-11-16T09:00:00+01:00",
"variazione": false
}
]
},
"adesso": {
"in_onda": {
"titolo": "Buongiorno Radio", "programma": "buongiorno-radio",
"speaker": { "nome": "Mario Rossi", "speaker": "mario-rossi" },
"inizio_iso": "2026-11-14T07:00:00+01:00", "fine_iso": "2026-11-14T09:00:00+01:00"
},
"prossimo": null,
"brano": { "titolo": "Titolo", "artista": "Artista", "testo": "Artista - Titolo" },
"online": true
}
}
Campo Contenuto
nome, nome_breve, codename Nome completo, nome breve e codice della radio.
sito, streaming, whatsapp Sito web, indirizzo dello streaming e numero WhatsApp, come in Gestisci Radio (stringa vuota se mancano).
nowplaying Indirizzo pubblico del “now playing” di AzuraCast: il sito lo può leggere dal browser per il brano in onda. Vuoto se la radio non lo ha.
fuso_orario Fuso orario di orari e date (es. Europe/Rome).
richieste Indirizzo del modulo delle richieste della radio (vedi sotto); vuoto se la radio non ne ha uno.
statistiche Script delle analitiche del sito: script, radio e il codice pronto. null se la radio non ha il Sito web impostato o le analitiche sono spente.

Programmi non archiviati, in ordine di nome.

Campo Contenuto
id, slug Numero e nome per gli indirizzi del sito (es. buongiorno-radio), unico nella radio.
titolo, sottotitolo Nome e sottotitolo del programma.
immagine Copertina (sempre https://), oppure null.
descrizione_html Descrizione con la formattazione, già ripulita.
descrizione_testo La stessa descrizione in testo semplice (anteprime, meta description).
conduttori Elenco di { nome, speaker }: speaker è lo slug della persona se ha il profilo pubblico, altrimenti null.

Solo le persone con il profilo pubblico attivo (scelto da loro in Modifica Profilo o dalla direzione), in ordine di nome. Email, username, telefono e gli altri dati dell’account non escono mai; delle persone senza profilo pubblico esce solo il nome, nei programmi e nel palinsesto.

Campo Contenuto
id, slug Numero e nome per gli indirizzi del sito.
nome Il nome pubblico (es. nome d’arte) o, se manca, il nome.
ruolo Il ruolo da mostrare: il ruolo pubblico, altrimenti il ruolo preciso, altrimenti il ruolo nel pannello. Usa questo.
ruolo_pubblico, ruolo_preciso, ruolo_base I tre ruoli separati, per i siti che li usano già.
bio Biografia in testo semplice (al massimo 1000 caratteri).
immagine Immagine profilo, oppure null.
social Oggetto con instagram e/o tiktok (indirizzi completi), solo se la persona ha scelto di mostrarli; altrimenti vuoto.
programmi Gli slug dei programmi che conduce.
  • settimana: la settimana tipo. giorno va da 1 (lunedì) a 7 (domenica), inizio e fine sono ore HH:00, titolo è il nome del blocco e programma lo slug del programma corrispondente (o null), speaker è { nome, speaker } o null.
  • prossimi_giorni: i prossimi 7 giorni da oggi, con le stesse chiavi più data (AAAA-MM-GG), inizio_iso e fine_iso (ISO 8601 nel fuso della radio) e variazione (true se la puntata è cambiata solo per quel giorno: speciali, sostituzioni, scambi di turno).

in_onda e prossimo (titolo, programma, speaker, inizio_iso, fine_iso, oppure null), brano (titolo, artista, testo, oppure null) e online (stato dello streaming, null se non noto). Sono i dati al momento della chiamata: in un sito statico invecchiano subito. Per il “in onda adesso” calcola il programma dal browser con prossimi_giorni e leggi il brano da radio.nowplaying.

Gli indirizzi in immagine si usano direttamente nelle pagine del sito, senza chiave. Il nome del file cambia a ogni nuovo caricamento, quindi le immagini si possono tenere in cache per sempre (anche su una CDN). Se una persona disattiva il profilo pubblico o un programma viene archiviato, l’immagine smette di essere servita.

Un sito statico mostra i dati dell’ultima build. Con un deploy hook il pannello ricostruisce il sito da solo quando cambiano programmi, palinsesto, variazioni o profili pubblici.

  1. Crea il deploy hook nel servizio che ospita il sito. In Cloudflare Pages: progetto del sito → Settings → Builds → Deploy hooks → Add deploy hook.
  2. Nel pannello, in Palinsesto → Impostazioni → Aggiornamento automatico del sito, incolla l’indirizzo (deve essere https://) e premi Attiva l’aggiornamento automatico.

Il pannello chiama il deploy hook con una richiesta POST senza corpo:

  • quando i dati pubblicati cambiano, al massimo una volta ogni 5 minuti (più modifiche ravvicinate finiscono nella stessa ricostruzione);
  • comunque una volta al giorno, perché a mezzanotte cambiano le date dei prossimi 7 giorni;
  • subito, con Aggiorna ora il sito (al massimo una volta al minuto).

Nel riquadro vedi l’ultimo aggiornamento e il suo esito. Togli disattiva l’aggiornamento automatico. L’indirizzo del deploy hook viene salvato cifrato.

Dal sito gli ascoltatori possono scrivere alla regia: i messaggi arrivano nelle Richieste del pannello, come quelli del modulo pubblico, con gli stessi limiti contro gli invii ripetuti. L’indirizzo è in radio.richieste.

Questa chiamata parte dal browser dell’ascoltatore e non usa la chiave API:

// richieste: valore di radio.richieste letto durante la build
async function scriviAllaRegia(richieste, nome, messaggio) {
const dati = new FormData();
dati.append('ajax_request', '1');
dati.append('author', nome); // facoltativo, al massimo 100 caratteri
dati.append('message', messaggio); // obbligatorio, al massimo 500 caratteri
const res = await fetch(richieste, { method: 'POST', body: dati });
return res.json(); // { "success": true } oppure { "success": false, "error": "…" }
}

Mostra all’ascoltatore il testo di error quando success è false: ad esempio quando chiede di aspettare prima di inviare un altro messaggio.