The Ultimate Guide to Radio Browser API Integration
- Introduction
- API Base URL & Servers
- Quick Start
- Station Endpoints
- Search & Filter
- Categories & Lists
- Statistics & Counts
- Logo & Favicon Handling
- Voting & Click Tracking
- Advanced Queries
- Pagination & Limits
- Code Examples
- Error Handling
- Rate Limiting
- Best Practices
- Use Cases
- FAQ
- Support & Donation
Radio Browser is a free, open-source API providing access to 45,000+ radio stations worldwide. This documentation covers every endpoint, parameter, and technique you need to build powerful radio applications.
| Application | Description |
|---|---|
| π» Radio Directory | Complete station listing website |
| π΅ Music Streaming App | Mobile/Desktop radio player |
| π WordPress Plugin | Like AnowXRadio |
| π± Mobile Apps | iOS/Android radio apps |
| π₯οΈ Desktop Apps | Electron-based radio players |
| π€ Smart Speaker Skills | Alexa, Google Assistant integration |
| π‘ IoT Devices | Raspberry Pi radio projects |
https://de1.api.radio-browser.info/json/
https://nl1.api.radio-browser.info/json/
https://at1.api.radio-browser.info/json/
// Get best server automatically
const servers = [
'https://de1.api.radio-browser.info',
'https://nl1.api.radio-browser.info',
'https://at1.api.radio-browser.info'
];
async function getBestServer() {
for (const server of servers) {
try {
const response = await fetch(`${server}/json/stats`, {
timeout: 5000
});
if (response.ok) return server;
} catch (e) {
continue;
}
}
return servers[0]; // fallback
}| Format | Endpoint Prefix | Content-Type |
|---|---|---|
| JSON | /json/ |
application/json |
| XML | /xml/ |
application/xml |
# Get 10 stations
curl "https://de1.api.radio-browser.info/json/stations?limit=10"fetch('https://de1.api.radio-browser.info/json/stations?limit=10')
.then(res => res.json())
.then(stations => {
stations.forEach(station => {
console.log(`π» ${station.name} - ${station.country}`);
});
});GET /json/stationsGET /json/stations?limit=100&offset=0GET /json/stations/byuuid/{stationuuid}Example:
curl "https://de1.api.radio-browser.info/json/stations/byuuid/96202f73-0601-11e8-ae97-52543be04c81"GET /json/stations/byname/{name}GET /json/stations/bynameexact/{name}{
"changeuuid": "96202f73-0601-11e8-ae97-52543be04c81",
"stationuuid": "96202f73-0601-11e8-ae97-52543be04c81",
"name": "BBC Radio 1",
"url": "http://stream.live.vc.bbcmedia.co.uk/bbc_radio_one",
"url_resolved": "http://stream.live.vc.bbcmedia.co.uk/bbc_radio_one",
"homepage": "https://www.bbc.co.uk/radio1",
"favicon": "https://example.com/logo.png",
"tags": "pop,rock,news,bbc",
"country": "United Kingdom",
"countrycode": "GB",
"state": "London",
"language": "english",
"languagecodes": "en",
"votes": 12500,
"lastchangetime": "2024-01-15 10:30:00",
"codec": "MP3",
"bitrate": 128,
"hls": 0,
"lastcheckok": 1,
"lastchecktime": "2024-01-20 08:00:00",
"clickcount": 50000,
"clicktrend": 150,
"ssl_error": 0,
"geo_lat": 51.5074,
"geo_long": -0.1278,
"has_extended_info": true
}| Field | Type | Description |
|---|---|---|
stationuuid |
string | Unique station identifier |
name |
string | Station name |
url |
string | Original stream URL |
url_resolved |
string | Final resolved URL (after redirects) |
favicon |
string | Logo/Favicon URL |
country |
string | Country name |
countrycode |
string | ISO 3166-1 alpha-2 code |
language |
string | Primary language |
tags |
string | Comma-separated genre tags |
votes |
int | Total upvotes |
codec |
string | Audio codec (MP3, AAC, OGG) |
bitrate |
int | Stream bitrate in kbps |
lastcheckok |
int | 1 = online, 0 = offline |
clickcount |
int | Total plays/clicks |
clicktrend |
int | Recent click trend |
GET /json/stations/search| Parameter | Type | Description | Example |
|---|---|---|---|
name |
string | Station name (partial match) | name=BBC |
nameExact |
boolean | Exact name match | nameExact=true |
country |
string | Country name | country=India |
countrycode |
string | ISO country code | countrycode=IN |
state |
string | State/Province | state=Tamil Nadu |
language |
string | Language | language=tamil |
tag |
string | Genre/Tag | tag=pop |
tagList |
string | Multiple tags (comma) | tagList=pop,rock |
codec |
string | Audio codec | codec=MP3 |
bitrateMin |
int | Minimum bitrate | bitrateMin=128 |
bitrateMax |
int | Maximum bitrate | bitrateMax=320 |
has_geo_info |
boolean | Has coordinates | has_geo_info=true |
has_extended_info |
boolean | Has extended info | has_extended_info=true |
is_https |
boolean | HTTPS stream only | is_https=true |
order |
string | Sort order | order=votes |
reverse |
boolean | Reverse sort | reverse=true |
offset |
int | Pagination offset | offset=0 |
limit |
int | Results per page | limit=100 |
hidebroken |
boolean | Hide offline stations | hidebroken=true |
curl "https://de1.api.radio-browser.info/json/stations/bycountry/India?limit=50"curl "https://de1.api.radio-browser.info/json/stations/bylanguage/tamil?limit=50"curl "https://de1.api.radio-browser.info/json/stations/bytag/classical?limit=50"curl "https://de1.api.radio-browser.info/json/stations/bycountrycodeexact/IN?limit=50"curl "https://de1.api.radio-browser.info/json/stations/search?country=India&language=tamil&tag=music&hidebroken=true&order=votes&limit=100"curl "https://de1.api.radio-browser.info/json/stations/search?bitrateMin=256&codec=MP3&hidebroken=true&limit=50"curl "https://de1.api.radio-browser.info/json/stations/search?is_https=true&limit=50"| Value | Description |
|---|---|
name |
Alphabetical by name |
url |
By stream URL |
homepage |
By homepage URL |
favicon |
By favicon URL |
tags |
By tags |
country |
By country name |
state |
By state |
language |
By language |
votes |
By total votes β |
codec |
By audio codec |
bitrate |
By bitrate |
lastcheckok |
By online status |
lastchecktime |
By last check time |
clicktimestamp |
By last click time |
clickcount |
By total clicks π₯ |
clicktrend |
By click trend π |
changetimestamp |
By last change time |
random |
Random order π² |
GET /json/countriesResponse:
[
{
"name": "India",
"iso_3166_1": "IN",
"stationcount": 1250
},
{
"name": "United States",
"iso_3166_1": "US",
"stationcount": 8500
}
]GET /json/languagesResponse:
[
{
"name": "tamil",
"iso_639": "ta",
"stationcount": 450
},
{
"name": "english",
"iso_639": "en",
"stationcount": 15000
}
]GET /json/tagsResponse:
[
{
"name": "pop",
"stationcount": 5200
},
{
"name": "rock",
"stationcount": 4800
},
{
"name": "classical",
"stationcount": 1200
}
]GET /json/codecsResponse:
[
{
"name": "MP3",
"stationcount": 35000
},
{
"name": "AAC",
"stationcount": 8000
},
{
"name": "OGG",
"stationcount": 2000
}
]GET /json/states/{country}Example:
curl "https://de1.api.radio-browser.info/json/states/India"GET /json/countries?order=stationcount&reverse=true&limit=20
GET /json/languages?order=stationcount&reverse=true&limit=20
GET /json/tags?order=stationcount&reverse=true&hidebroken=trueGET /json/statsResponse:
{
"supported_version": 1,
"software_version": "0.7.24",
"status": "OK",
"stations": 45000,
"stations_broken": 5000,
"tags": 12000,
"clicks_last_hour": 25000,
"clicks_last_day": 500000,
"languages": 450,
"countries": 220
}// Get stations count per country
async function getCountryStats() {
const response = await fetch(
'https://de1.api.radio-browser.info/json/countries?order=stationcount&reverse=true'
);
const countries = await response.json();
countries.forEach(country => {
console.log(`${country.name}: ${country.stationcount} stations`);
});
}// Get stations count per language
async function getLanguageStats() {
const response = await fetch(
'https://de1.api.radio-browser.info/json/languages?order=stationcount&reverse=true'
);
const languages = await response.json();
languages.forEach(lang => {
console.log(`${lang.name}: ${lang.stationcount} stations`);
});
}GET /json/stations/topvote?limit=100GET /json/stations/topclick?limit=100GET /json/stations/lastchange?limit=100GET /json/stations/lastclick?limit=100GET /json/serversconst station = {
name: "BBC Radio 1",
favicon: "https://example.com/logo.png"
};
// Default logo URL
const DEFAULT_LOGO = "https://anowhosting.com/plugins/anowxradio/default-radio.png";
function getStationLogo(station) {
return station.favicon && station.favicon.trim() !== ''
? station.favicon
: DEFAULT_LOGO;
}<img
src="${station.favicon}"
alt="${station.name}"
onerror="this.src='https://anowhosting.com/plugins/anowxradio/default-radio.png'"
loading="lazy"
/>function StationLogo({ station }) {
const DEFAULT_LOGO = "/images/default-radio.png";
const [imgSrc, setImgSrc] = useState(station.favicon || DEFAULT_LOGO);
return (
<img
src={imgSrc}
alt={station.name}
onError={() => setImgSrc(DEFAULT_LOGO)}
className="station-logo"
/>
);
}<template>
<img
:src="logoUrl"
:alt="station.name"
@error="handleImageError"
class="station-logo"
/>
</template>
<script>
export default {
props: ['station'],
data() {
return {
logoUrl: this.station.favicon || '/images/default-radio.png'
}
},
methods: {
handleImageError() {
this.logoUrl = '/images/default-radio.png';
}
}
}
</script>.station-logo {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.station-logo[src=""] {
content: url('/images/default-radio.png');
}// Cache logos in IndexedDB or LocalStorage
async function cacheStationLogo(stationId, logoUrl) {
try {
const response = await fetch(logoUrl);
const blob = await response.blob();
const base64 = await blobToBase64(blob);
localStorage.setItem(`logo_${stationId}`, base64);
} catch (e) {
console.log('Logo caching failed:', e);
}
}When a user plays a station, register the click:
POST /json/url/{stationuuid}Example:
async function registerClick(stationuuid) {
await fetch(
`https://de1.api.radio-browser.info/json/url/${stationuuid}`,
{ method: 'POST' }
);
}
// Usage: Call when user clicks play
playButton.onclick = () => {
registerClick(station.stationuuid);
audioPlayer.play();
};POST /json/vote/{stationuuid}Example:
async function voteStation(stationuuid) {
const response = await fetch(
`https://de1.api.radio-browser.info/json/vote/${stationuuid}`,
{ method: 'POST' }
);
const result = await response.json();
if (result.ok === true) {
console.log('Vote registered successfully!');
} else {
console.log('Vote failed:', result.message);
}
}{
"ok": true,
"message": "voted for station successfully"
}GET /json/stations/search?order=random&limit=10GET /json/stations/search?has_geo_info=true&limit=50async function getStationsNearby(lat, lon, radiusKm = 100) {
const stations = await fetch(
'https://de1.api.radio-browser.info/json/stations/search?has_geo_info=true&limit=1000'
).then(r => r.json());
return stations.filter(station => {
const distance = calculateDistance(lat, lon, station.geo_lat, station.geo_long);
return distance <= radiusKm;
});
}
function calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371; // Earth's radius in km
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}GET /json/stations/broken?limit=100GET /json/stations/search?lastcheckok=0&limit=100async function searchByMultipleTags(tags) {
const tagList = tags.join(',');
const response = await fetch(
`https://de1.api.radio-browser.info/json/stations/search?tagList=${tagList}&limit=100`
);
return response.json();
}
// Usage
searchByMultipleTags(['pop', 'rock', '80s']);async function searchMultipleCountries(countries) {
const results = [];
for (const country of countries) {
const stations = await fetch(
`https://de1.api.radio-browser.info/json/stations/bycountry/${country}?limit=50`
).then(r => r.json());
results.push(...stations);
}
return results;
}
// Usage
searchMultipleCountries(['India', 'Sri Lanka', 'Malaysia']);| Parameter | Default | Max | Description |
|---|---|---|---|
limit |
100000 | 100000 | Results per page |
offset |
0 | - | Skip N results |
class RadioPaginator {
constructor(baseUrl, limit = 50) {
this.baseUrl = baseUrl;
this.limit = limit;
this.offset = 0;
this.hasMore = true;
}
async getNextPage() {
if (!this.hasMore) return [];
const url = `${this.baseUrl}?limit=${this.limit}&offset=${this.offset}`;
const response = await fetch(url);
const stations = await response.json();
this.offset += this.limit;
this.hasMore = stations.length === this.limit;
return stations;
}
reset() {
this.offset = 0;
this.hasMore = true;
}
}
// Usage
const paginator = new RadioPaginator(
'https://de1.api.radio-browser.info/json/stations/bycountry/India',
50
);
// Load first page
const page1 = await paginator.getNextPage();
// Load next page
const page2 = await paginator.getNextPage();let offset = 0;
const limit = 30;
let loading = false;
async function loadMoreStations() {
if (loading) return;
loading = true;
const response = await fetch(
`https://de1.api.radio-browser.info/json/stations/search?country=India&offset=${offset}&limit=${limit}`
);
const stations = await response.json();
stations.forEach(station => {
appendStationToGrid(station);
});
offset += limit;
loading = false;
}
// Intersection Observer for infinite scroll
const observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
loadMoreStations();
}
});
observer.observe(document.querySelector('#load-trigger'));class RadioBrowserAPI {
constructor() {
this.baseUrl = 'https://de1.api.radio-browser.info/json';
}
async searchStations(params = {}) {
const queryString = new URLSearchParams(params).toString();
const response = await fetch(`${this.baseUrl}/stations/search?${queryString}`);
return response.json();
}
async getByCountry(country, limit = 100) {
const response = await fetch(
`${this.baseUrl}/stations/bycountry/${encodeURIComponent(country)}?limit=${limit}`
);
return response.json();
}
async getByLanguage(language, limit = 100) {
const response = await fetch(
`${this.baseUrl}/stations/bylanguage/${encodeURIComponent(language)}?limit=${limit}`
);
return response.json();
}
async getTopVoted(limit = 50) {
const response = await fetch(`${this.baseUrl}/stations/topvote?limit=${limit}`);
return response.json();
}
async getCountries() {
const response = await fetch(`${this.baseUrl}/countries`);
return response.json();
}
async getLanguages() {
const response = await fetch(`${this.baseUrl}/languages`);
return response.json();
}
async getTags() {
const response = await fetch(`${this.baseUrl}/tags`);
return response.json();
}
async registerClick(stationuuid) {
await fetch(`${this.baseUrl}/url/${stationuuid}`, { method: 'POST' });
}
async vote(stationuuid) {
const response = await fetch(
`${this.baseUrl}/vote/${stationuuid}`,
{ method: 'POST' }
);
return response.json();
}
}
// Usage
const api = new RadioBrowserAPI();
// Search Tamil stations
const tamilStations = await api.searchStations({
language: 'tamil',
hidebroken: true,
order: 'votes',
limit: 50
});
// Get Indian stations
const indianStations = await api.getByCountry('India');
// Get top voted
const topStations = await api.getTopVoted(100);import requests
from typing import List, Dict, Optional
class RadioBrowserAPI:
def __init__(self):
self.base_url = "https://de1.api.radio-browser.info/json"
self.headers = {
"User-Agent": "MyRadioApp/1.0"
}
def search_stations(self, **params) -> List[Dict]:
"""Search stations with filters"""
response = requests.get(
f"{self.base_url}/stations/search",
params=params,
headers=self.headers
)
return response.json()
def get_by_country(self, country: str, limit: int = 100) -> List[Dict]:
"""Get stations by country"""
response = requests.get(
f"{self.base_url}/stations/bycountry/{country}",
params={"limit": limit},
headers=self.headers
)
return response.json()
def get_by_language(self, language: str, limit: int = 100) -> List[Dict]:
"""Get stations by language"""
response = requests.get(
f"{self.base_url}/stations/bylanguage/{language}",
params={"limit": limit},
headers=self.headers
)
return response.json()
def get_top_voted(self, limit: int = 50) -> List[Dict]:
"""Get top voted stations"""
response = requests.get(
f"{self.base_url}/stations/topvote",
params={"limit": limit},
headers=self.headers
)
return response.json()
def get_countries(self) -> List[Dict]:
"""Get all countries with station count"""
response = requests.get(
f"{self.base_url}/countries",
headers=self.headers
)
return response.json()
def get_languages(self) -> List[Dict]:
"""Get all languages with station count"""
response = requests.get(
f"{self.base_url}/languages",
headers=self.headers
)
return response.json()
def get_tags(self) -> List[Dict]:
"""Get all tags/genres"""
response = requests.get(
f"{self.base_url}/tags",
headers=self.headers
)
return response.json()
def register_click(self, stationuuid: str) -> Dict:
"""Register station click/play"""
response = requests.post(
f"{self.base_url}/url/{stationuuid}",
headers=self.headers
)
return response.json()
def vote(self, stationuuid: str) -> Dict:
"""Vote for a station"""
response = requests.post(
f"{self.base_url}/vote/{stationuuid}",
headers=self.headers
)
return response.json()
# Usage
if __name__ == "__main__":
api = RadioBrowserAPI()
# Search Tamil stations
tamil_stations = api.search_stations(
language="tamil",
hidebroken=True,
order="votes",
limit=50
)
for station in tamil_stations:
print(f"π» {station['name']} - {station['country']}")
# Get country stats
countries = api.get_countries()
for country in countries[:10]:
print(f"{country['name']}: {country['stationcount']} stations")<?php
class RadioBrowserAPI {
private $baseUrl = 'https://de1.api.radio-browser.info/json';
private function request($endpoint, $params = [], $method = 'GET') {
$url = $this->baseUrl . $endpoint;
if ($method === 'GET' && !empty($params)) {
$url .= '?' . http_build_query($params);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'MyRadioApp/1.0');
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function searchStations($params = []) {
return $this->request('/stations/search', $params);
}
public function getByCountry($country, $limit = 100) {
return $this->request("/stations/bycountry/{$country}", ['limit' => $limit]);
}
public function getByLanguage($language, $limit = 100) {
return $this->request("/stations/bylanguage/{$language}", ['limit' => $limit]);
}
public function getTopVoted($limit = 50) {
return $this->request('/stations/topvote', ['limit' => $limit]);
}
public function getCountries() {
return $this->request('/countries');
}
public function getLanguages() {
return $this->request('/languages');
}
public function getTags() {
return $this->request('/tags');
}
public function registerClick($stationuuid) {
return $this->request("/url/{$stationuuid}", [], 'POST');
}
public function vote($stationuuid) {
return $this->request("/vote/{$stationuuid}", [], 'POST');
}
}
// Usage
$api = new RadioBrowserAPI();
// Get Tamil stations
$stations = $api->searchStations([
'language' => 'tamil',
'hidebroken' => true,
'order' => 'votes',
'limit' => 50
]);
foreach ($stations as $station) {
echo "π» {$station['name']} - {$station['country']}\n";
}const axios = require('axios');
class RadioBrowserAPI {
constructor() {
this.baseUrl = 'https://de1.api.radio-browser.info/json';
this.client = axios.create({
baseURL: this.baseUrl,
headers: {
'User-Agent': 'MyRadioApp/1.0'
}
});
}
async searchStations(params = {}) {
const { data } = await this.client.get('/stations/search', { params });
return data;
}
async getByCountry(country, limit = 100) {
const { data } = await this.client.get(
`/stations/bycountry/${encodeURIComponent(country)}`,
{ params: { limit } }
);
return data;
}
async getByLanguage(language, limit = 100) {
const { data } = await this.client.get(
`/stations/bylanguage/${encodeURIComponent(language)}`,
{ params: { limit } }
);
return data;
}
async getTopVoted(limit = 50) {
const { data } = await this.client.get('/stations/topvote', {
params: { limit }
});
return data;
}
async getCountries() {
const { data } = await this.client.get('/countries');
return data;
}
async getLanguages() {
const { data } = await this.client.get('/languages');
return data;
}
async getTags() {
const { data } = await this.client.get('/tags');
return data;
}
async registerClick(stationuuid) {
const { data } = await this.client.post(`/url/${stationuuid}`);
return data;
}
async vote(stationuuid) {
const { data } = await this.client.post(`/vote/${stationuuid}`);
return data;
}
}
// Usage
(async () => {
const api = new RadioBrowserAPI();
const stations = await api.searchStations({
language: 'tamil',
hidebroken: true,
order: 'votes',
limit: 50
});
stations.forEach(station => {
console.log(`π» ${station.name} - ${station.country}`);
});
})();# Get all stations (limited)
curl "https://de1.api.radio-browser.info/json/stations?limit=50"
# Search by country
curl "https://de1.api.radio-browser.info/json/stations/bycountry/India?limit=50"
# Search by language
curl "https://de1.api.radio-browser.info/json/stations/bylanguage/tamil?limit=50"
# Search by tag
curl "https://de1.api.radio-browser.info/json/stations/bytag/pop?limit=50"
# Combined search
curl "https://de1.api.radio-browser.info/json/stations/search?country=India&language=tamil&hidebroken=true&order=votes&limit=100"
# Top voted stations
curl "https://de1.api.radio-browser.info/json/stations/topvote?limit=100"
# Get all countries
curl "https://de1.api.radio-browser.info/json/countries"
# Get all languages
curl "https://de1.api.radio-browser.info/json/languages"
# Get all tags
curl "https://de1.api.radio-browser.info/json/tags"
# Register click (POST)
curl -X POST "https://de1.api.radio-browser.info/json/url/96202f73-0601-11e8-ae97-52543be04c81"
# Vote (POST)
curl -X POST "https://de1.api.radio-browser.info/json/vote/96202f73-0601-11e8-ae97-52543be04c81"
# Get API stats
curl "https://de1.api.radio-browser.info/json/stats"| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request |
| 404 | Not Found |
| 429 | Too Many Requests |
| 500 | Server Error |
async function safeApiCall(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return { success: true, data };
} catch (error) {
console.error('API Error:', error.message);
return { success: false, error: error.message };
}
}
// Usage
const result = await safeApiCall(
'https://de1.api.radio-browser.info/json/stations?limit=50'
);
if (result.success) {
console.log('Stations:', result.data);
} else {
console.log('Error:', result.error);
}async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (response.ok) return response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1))); // Exponential backoff
}
}
}const servers = [
'https://de1.api.radio-browser.info',
'https://nl1.api.radio-browser.info',
'https://at1.api.radio-browser.info'
];
async function fetchWithFailover(endpoint) {
for (const server of servers) {
try {
const response = await fetch(`${server}${endpoint}`);
if (response.ok) return response.json();
} catch (e) {
continue;
}
}
throw new Error('All servers failed');
}| Aspect | Recommendation |
|---|---|
| Requests/second | Max 2-3 requests |
| Bulk fetching | Use larger limits, fewer requests |
| Caching | Cache results for 5-15 minutes |
| User-Agent | Always set custom User-Agent |
class RateLimiter {
constructor(requestsPerSecond = 2) {
this.minInterval = 1000 / requestsPerSecond;
this.lastRequest = 0;
}
async throttle() {
const now = Date.now();
const elapsed = now - this.lastRequest;
if (elapsed < this.minInterval) {
await new Promise(r => setTimeout(r, this.minInterval - elapsed));
}
this.lastRequest = Date.now();
}
}
// Usage
const limiter = new RateLimiter(2);
async function fetchStations() {
await limiter.throttle();
return fetch('https://de1.api.radio-browser.info/json/stations?limit=50');
}class CachedRadioAPI {
constructor(cacheDurationMs = 300000) { // 5 minutes
this.cache = new Map();
this.cacheDuration = cacheDurationMs;
}
async fetch(url) {
const cached = this.cache.get(url);
if (cached && Date.now() - cached.timestamp < this.cacheDuration) {
return cached.data;
}
const response = await fetch(url);
const data = await response.json();
this.cache.set(url, {
data,
timestamp: Date.now()
});
return data;
}
}| Practice | Description |
|---|---|
| Cache responses | Cache for 5-15 minutes |
Use hidebroken=true |
Filter offline stations |
| Set User-Agent | Identify your app |
Use url_resolved |
Use resolved URL for playback |
| Register clicks | Call /url/{uuid} on play |
| Handle missing favicons | Provide default logo |
| Practice | Description |
|---|---|
| Don't fetch all 45K stations | Use pagination & filters |
| Don't ignore errors | Implement proper error handling |
| Don't hammer the API | Respect rate limits |
| Don't cache forever | Refresh data periodically |
| Don't skip vote/click tracking | Helps improve rankings |
const headers = {
'User-Agent': 'YourAppName/1.0 (yourwebsite.com)',
'Accept': 'application/json'
};<!DOCTYPE html>
<html>
<head>
<title>Simple Radio Player</title>
<style>
.station { padding: 10px; border: 1px solid #ddd; margin: 5px; cursor: pointer; }
.station:hover { background: #f0f0f0; }
.station img { width: 50px; height: 50px; object-fit: cover; }
</style>
</head>
<body>
<h1>π» Tamil Radio Stations</h1>
<audio id="player" controls></audio>
<div id="stations"></div>
<script>
const API = 'https://de1.api.radio-browser.info/json';
const DEFAULT_LOGO = 'https://via.placeholder.com/50?text=π»';
async function loadStations() {
const response = await fetch(
`${API}/stations/search?language=tamil&hidebroken=true&order=votes&limit=20`
);
const stations = await response.json();
const container = document.getElementById('stations');
stations.forEach(station => {
const div = document.createElement('div');
div.className = 'station';
div.innerHTML = `
<img src="${station.favicon || DEFAULT_LOGO}"
onerror="this.src='${DEFAULT_LOGO}'">
<strong>${station.name}</strong>
<small>${station.country}</small>
`;
div.onclick = () => playStation(station);
container.appendChild(div);
});
}
function playStation(station) {
document.getElementById('player').src = station.url_resolved || station.url;
document.getElementById('player').play();
// Register click
fetch(`${API}/url/${station.stationuuid}`, { method: 'POST' });
}
loadStations();
</script>
</body>
</html>async function buildCountryDropdown() {
const response = await fetch(
'https://de1.api.radio-browser.info/json/countries?order=stationcount&reverse=true'
);
const countries = await response.json();
const select = document.getElementById('countrySelect');
countries.forEach(country => {
const option = document.createElement('option');
option.value = country.name;
option.textContent = `${country.name} (${country.stationcount})`;
select.appendChild(option);
});
}
async function filterByCountry(country) {
const response = await fetch(
`https://de1.api.radio-browser.info/json/stations/bycountry/${country}?hidebroken=true&limit=50`
);
const stations = await response.json();
displayStations(stations);
}function import_radio_stations($country, $limit = 100) {
$api_url = "https://de1.api.radio-browser.info/json/stations/bycountry/{$country}";
$api_url .= "?hidebroken=true&order=votes&limit={$limit}";
$response = wp_remote_get($api_url);
if (is_wp_error($response)) {
return false;
}
$stations = json_decode(wp_remote_retrieve_body($response), true);
foreach ($stations as $station) {
// Check if already exists
$existing = get_posts([
'post_type' => 'radio_station',
'meta_key' => 'station_uuid',
'meta_value' => $station['stationuuid']
]);
if (empty($existing)) {
$post_id = wp_insert_post([
'post_title' => $station['name'],
'post_type' => 'radio_station',
'post_status' => 'publish'
]);
update_post_meta($post_id, 'station_uuid', $station['stationuuid']);
update_post_meta($post_id, 'stream_url', $station['url_resolved']);
update_post_meta($post_id, 'favicon', $station['favicon']);
update_post_meta($post_id, 'country', $station['country']);
update_post_meta($post_id, 'language', $station['language']);
update_post_meta($post_id, 'bitrate', $station['bitrate']);
}
}
return count($stations);
}A: Yes! Radio Browser API is completely free and open-source.
A: There's no strict limit, but please be respectful. 2-3 requests/second is recommended.
A: Stations are checked regularly. New stations are added by community.
A: Yes! Visit radio-browser.info to submit your station.
A: Use hidebroken=true to filter out offline stations. Some streams may be geo-restricted.
A: Use codec=MP3 parameter.
A: Use bitrateMin=128 or higher.
A: Yes! But please credit Radio Browser and consider donating.
This documentation is maintained by ABM Rishi for the developer community.
| π§ Contact | π Website | π± WhatsApp |
|---|---|---|
| support@anowhosting.com | anowhosting.com | +94 779688469 |
Radio Browser is maintained by volunteers. Please consider supporting:
- π Website: radio-browser.info
- π» GitHub: github.com/segler-alex/radiobrowser-api-rust
- β Donate: Support the developers!
| Project | Description | Link |
|---|---|---|
| AnowXRadio | WordPress Radio Plugin | anowhosting.com/plugins/anowxradio |
This documentation is provided under MIT License.
Radio Browser API is free and open-source.
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2024-01-20 | Initial release |
| 1.1.0 | 2024-01-25 | Added advanced examples |
Made with β€οΈ by ABM Rishi