Overview
The BoxRadio API is a JSON REST API served on a dedicated API host. There is no /api path prefix — resources live at the root, for example /radios and /genres.
| Audience | Endpoints | Authentication |
|---|---|---|
| Public (web) | Radios, genres, single-station metadata | None |
Response format: Successful responses use Content-Type: application/json. Collection and single-resource responses wrap the payload in a top-level data key.
Web clients should pass platform=web on list endpoints.
Base URL
| Environment | Base URL |
|---|---|
| Production | https://boxradioedge.radioapi.me |
All paths below are relative to the base URL. Example: list radios → GET https://boxradioedge.radioapi.me/radios?platform=web.
The API host only serves JSON API routes. Browser requests to the API domain always negotiate JSON responses (including 404 errors).
Request conventions
Recommended headers
| Header | Value | When |
|---|---|---|
Accept | application/json | Recommended on every request |
Query parameters
- Booleans: Pass
1,true, orfeatured=1— all are treated as true.
Identifiers in URLs
Path parameters such as {radio} and {genre} accept either:
- The resource UUID, or
- The resource slug (URL-friendly string, e.g.
box-radio).
Pagination
List endpoints return the full result set in one response. There is no cursor or page parameter.
Caching recommendations
| Endpoint | Suggested TTL |
|---|---|
GET /radios, GET /genres | 5–15 minutes (or until user refresh) |
GET /radios/{id} | Same as list |
GET /metadata/{radio} | 15–30 seconds (server caches ~30s for proxied metadata) |
Public endpoints
List radios
Returns radio stations for web, optionally filtered by featured flag or search query.
GET /radios?platform=webQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
platform | web | Yes | Scopes results to web |
featured | boolean | No | Only stations featured on web |
q | string | No | Case-sensitive substring search on name, description, and slug |
Behaviour notes
- Results are sorted alphabetically by
name.
Example request
curl -sS -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/radios?platform=web"Example request — search
curl -sS -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/radios?platform=web&q=rock"Example response — 200 OK
{
"data": [
{
"id": "9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"name": "Box Radio",
"slug": "box-radio",
"image": "https://ik.imagekit.io/boxradio/radios/box-radio.jpg",
"tag": "Pop",
"metadata": {
"static": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f.json",
"external": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"internal": "https://boxradioedge.radioapi.me/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f"
},
"streams": [
{
"name": "Main Stream",
"url": "https://stream.example.com/live",
"type": "icecast",
"is_default": true
}
]
}
]
}JavaScript example
const baseUrl = 'https://boxradioedge.radioapi.me';
const res = await fetch(
`${baseUrl}/radios?platform=web`,
{ headers: { Accept: 'application/json' } }
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data: radios } = await res.json();Get radio
Returns a single station. Includes description (not present on list items).
GET /radios/{slug_or_uuid}Path parameters
| Parameter | Description |
|---|---|
slug_or_uuid | Station UUID or slug |
Example request
curl -sS -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/radios/box-radio"Example response — 200 OK
{
"data": {
"id": "9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"name": "Box Radio",
"slug": "box-radio",
"description": "Your home for pop hits and live shows.",
"image": "https://ik.imagekit.io/boxradio/radios/box-radio.jpg",
"tag": "Pop",
"metadata": {
"static": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f.json",
"external": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"internal": "https://boxradioedge.radioapi.me/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f"
},
"streams": [
{
"name": "Main Stream",
"url": "https://stream.example.com/live",
"type": "icecast",
"is_default": true
}
]
}
}Example response — 404 Not Found
{
"message": "Not found."
}Prefer matching UI routes to slug for shareable URLs; use id (UUID) as the stable primary key in local storage and analytics.
List genres
Returns genres enabled for web.
GET /genres?platform=webQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
platform | web | Yes | Returns genres enabled for web |
Example request
curl -sS -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/genres?platform=web"Example response — 200 OK
{
"data": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Pop",
"slug": "pop",
"description": "Chart hits and mainstream pop.",
"image": "https://ik.imagekit.io/boxradio/genres/pop.jpg",
"color": "#FF5733"
}
]
}The radios array is not included on the list endpoint. Load genre detail for station lists inside a genre.
Get genre
Returns one genre with its related web radio stations.
GET /genres/{slug_or_uuid}?platform=webPath parameters
| Parameter | Description |
|---|---|
slug_or_uuid | Genre UUID (id) or slug |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
platform | web | Yes | Filter related radios to those enabled on web |
featured | boolean | No | When true, only radios marked featured in this genre |
Related radios are ordered by featured-in-genre first, then alphabetically by station name.
Example request
curl -sS -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/genres/pop?platform=web"Example response — 200 OK
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Pop",
"slug": "pop",
"description": "Chart hits and mainstream pop.",
"image": "https://ik.imagekit.io/boxradio/genres/pop.jpg",
"color": "#FF5733",
"radios": [
{
"id": "9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"name": "Box Radio",
"slug": "box-radio",
"image": "https://ik.imagekit.io/boxradio/radios/box-radio.jpg",
"tag": "Pop",
"metadata": {
"static": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f.json",
"external": "https://openapi.streamafrica.cloud/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"internal": "https://boxradioedge.radioapi.me/metadata/9b8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f"
},
"streams": [
{
"name": "Main Stream",
"url": "https://stream.example.com/live",
"type": "icecast",
"is_default": true
}
]
}
]
}
}Get station metadata
Returns now playing information for one station.
GET /metadata/{slug_or_uuid}Path parameters
| Parameter | Description |
|---|---|
slug_or_uuid | Station UUID or slug |
Query parameters
| Parameter | Type | Description |
|---|---|---|
history | integer | When set (e.g. 1), include recent track history (forwarded on redirect) |
language | string | Preferred metadata language |
service | string | Override metadata service detection |
genre | string | Genre hint for metadata lookup |
Response modes
| Condition | HTTP | What the client receives |
|---|---|---|
| Station has no metadata source configured | 404 | JSON error message |
| Station is linked to RadioAPI UUID | 302 | Redirect to external metadata URL |
| Otherwise | 200 | JSON body from metadata proxy |
Example request
curl -sS -L -H "Accept: application/json" \
"https://boxradioedge.radioapi.me/metadata/box-radio?history=1&language=en"Example response — 200 OK (proxied JSON)
{
"artist": "Led Zeppelin",
"title": "Stairway to Heaven",
"album": "Led Zeppelin IV",
"artwork": "https://cdn.example.com/artwork/stairway.jpg"
}Example response — 404 Not Found
{
"message": "No metadata source configured for this station"
}Integration tips
- Use
metadata.externalormetadata.staticfrom the radio object when available — fetch RadioAPI directly and skip the redirect round-trip. - Use
metadata.internalfor a consistent URL per station; enable redirect following in your HTTP client. - Polling — Refresh every 15–30 seconds while playing; back off when paused.
async function fetchNowPlaying(radio) {
const url = radio.metadata.internal ?? radio.metadata.external;
if (!url) return null;
const res = await fetch(url, {
headers: { Accept: 'application/json' },
redirect: 'follow',
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Metadata HTTP ${res.status}`);
return res.json();
}Data shapes
Radio (list)
Used in: GET /radios?platform=web.
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (UUID) | No | Stable station identifier |
name | string | No | Display name |
slug | string | No | URL-friendly identifier |
image | string (URL) | Yes | Artwork |
tag | string | Yes | Default genre/tag label |
metadata | object | No | See Metadata links |
streams | array | No | Available streams |
Radio (detail)
Used in: GET /radios/{id}, genre radios arrays.
Same as the list shape, plus description (string, nullable). List items do not include description.
Stream
| Field | Type | Description |
|---|---|---|
name | string | Label shown in stream picker |
url | string | Playback URL |
type | string | Stream type hint for the player |
is_default | boolean | Use when auto-selecting a stream |
Playback tip: Prefer the stream where is_default is true; otherwise use the first item.
Metadata links
| Field | Type | Description |
|---|---|---|
static | string (URL) | null | RadioAPI metadata JSON |
external | string (URL) | null | RadioAPI metadata endpoint |
internal | string (URL) | null | BoxRadio proxy (GET /metadata/{uuid}) |
Genre
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (UUID) | No | Genre identifier |
name | string | No | Display name |
slug | string | No | URL-friendly identifier |
description | string | Yes | Long description |
image | string (URL) | Yes | Genre artwork |
color | string | No | Hex colour for UI accents |
radios | array | — | Genre detail only |
Error handling
| Status | When |
|---|---|
200 | Success |
302 | Metadata redirect to RadioAPI (not an error) |
404 | Unknown radio/genre, or metadata not configured |
503 | Metadata provider unreachable or failed |
Most errors return:
{
"message": "Human-readable summary"
}Recommended handling:
404→ Show “not found” UI; refresh station list503→ Retry metadata with exponential backoff; show “now playing unavailable”302→ Follow redirect (or usemetadata.externaldirectly)
Integration patterns
Recommended boot sequence
GET /radios?platform=web— load the station gridGET /genres?platform=web— browse categories- Poll
GET /metadata/{slug}or usemetadata.externalwhile a station is playing
Choosing a metadata URL
metadata.static → GET JSON file (good for simple polling)
metadata.external → GET live metadata from RadioAPI
metadata.internal → Same data via BoxRadio proxy; handles mixed station configsQuick reference
| Method | Path | Auth | Response wrapper |
|---|---|---|---|
| GET | /radios?platform=web | Public | { "data": [...] } |
| GET | /radios/{slug_or_uuid} | Public | { "data": {...} } |
| GET | /genres?platform=web | Public | { "data": [...] } |
| GET | /genres/{slug_or_uuid}?platform=web | Public | { "data": {...} } |
| GET | /metadata/{slug_or_uuid} | Public | JSON or 302 |