API Guidelines
Best practices, caching recommendations, and what to expect when integrating with the RadioMV API.
Overview
The RadioMV API consists of static JSON files served from the RadioMV CDN (stream.radiomv.com). This means:
- No authentication required
- Generous per-IP rate limits — well-behaved clients never notice them
- Geo-routed delivery from the server nearest the listener
- High availability
Do's
Always fetch URLs from app config
Never hardcode stream, metadata, or schedule URLs. Fetch
appv3.json first and resolve each station's relative paths against config.baseUrl. This ensures your app automatically gets the correct URLs when we release new versions.
Recommended Flow
// 1. Fetch app config (hardcode only this URL)
const config = await fetch('https://stream.radiomv.com/static/appv3.json');
const data = await config.json();
// 2. Resolve the station's schedule path against baseUrl
const station = data.stations.find(s => s.id === "english");
if (station.schedule) {
// 3. Fetch the schedule (not every station has one)
const schedule = await fetch(data.config.baseUrl + station.schedule);
const programs = (await schedule.json()).schedule;
}
Cache responses appropriately
Schedule files change ~3 times per year. App configuration changes more frequently but not constantly. Cache aggressively.
Caching Recommendations
| File | Update Frequency | Recommended Cache |
|---|---|---|
appv3.json |
Weekly to monthly | 24 hours, check version on app launch |
| Schedule files | ~3 times per year | 7 days, check lastUpdated periodically |
Implementation Best Practices
- Store a local copy — Bundle a fallback JSON with your app for offline support
- Use conditional fetching — Compare
config.versionorlastUpdatedbefore processing - Handle gracefully — If fetch fails, use cached data without crashing
- Respect timezone — All schedule times are in
America/Los_Angeles - Use HLS first —
streams.hlsis the preferred stream;streams.mp3is the fallback - Check
isMain— Filter by this flag when showing primary station list
Version Checking Example
// Smart fetching with version check
async function fetchWithVersionCheck(url, cachedData) {
try {
const response = await fetch(url);
const data = await response.json();
// For appv3.json
if (data.config?.version === cachedData?.config?.version) {
return cachedData; // No changes
}
// For schedule files
if (data.lastUpdated === cachedData?.lastUpdated) {
return cachedData; // No changes
}
return data; // New data
} catch (error) {
return cachedData; // Fallback to cache
}
}
Don'ts
Avoid these common mistakes
Following these guidelines ensures your integration remains stable and performant.
Things to Avoid
- Don't hardcode schedule URLs — Always get them from
appv3.jsonvia each station'sschedulefield - Don't poll constantly — No need to fetch more than once per app session or daily
- Don't hardcode station IDs — Always fetch from
appv3.jsonas stations may change - Don't ignore
schemaVersion— Major version changes indicate breaking changes - Don't assume field existence — Some fields are optional (e.g.,
artworkUrl16x9,stationScheduleUrl) - Don't parse day names — Days are numbers (0-6), not strings
- Don't assume 12-hour time — Times are 24-hour format
- Don't rely on array order — Sort schedules by
startTimefor display
What to Expect
Update Frequency
| Change Type | Frequency | Examples |
|---|---|---|
| Artwork updates | Seasonally (~4x/year) | Holiday themes, seasonal artwork |
| Schedule changes | Rarely (~3x/year) | New programs, time slot changes |
| Stream URL changes | Very rare | Infrastructure updates |
| New stations | Very rare | New language or channel launch |
| Schema changes | Very rare | New fields, structure changes |
Breaking Changes Policy
We follow semantic versioning for schedule files:
- Major version bump (2.0.0) — Breaking changes. Fields renamed, removed, or type changed. Update required.
- Minor version bump (1.1.0) — New optional fields. Backwards compatible.
- Patch version bump (1.0.1) — Documentation only. No code changes needed.
Backwards Compatibility
We strive to maintain backwards compatibility. New fields will be added as optional. Existing fields will not be removed without a major version bump.
Availability
- Files are served via Cloudflare CDN
- Global edge locations for fast access
- No planned maintenance windows
- Always have a cached fallback in your app
Error Handling
Recommended Pattern
async function loadStationData() {
const CACHE_KEY = 'radiomv_app_config';
try {
// Try to fetch fresh data
const response = await fetch(
'https://stream.radiomv.com/static/appv3.json',
{ timeout: 10000 }
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// Validate essential fields exist
if (!data.stations || !Array.isArray(data.stations)) {
throw new Error('Invalid response structure');
}
// Cache the valid response
localStorage.setItem(CACHE_KEY, JSON.stringify(data));
return data;
} catch (error) {
console.warn('Failed to fetch, using cache:', error);
// Fall back to cached data
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
return JSON.parse(cached);
}
// Fall back to bundled data
return require('./fallback-config.json');
}
}
Timezone Handling
All schedule times are in America/Los_Angeles (Pacific Time). To display in the user's local timezone:
function convertToLocalTime(timeStr, stationTimezone) {
// timeStr is "HH:MM" format
const [hours, minutes] = timeStr.split(':').map(Number);
// Create date in station timezone
const today = new Date();
const dateStr = today.toISOString().split('T')[0];
const stationDate = new Date(
`${dateStr}T${timeStr}:00`
);
// Format in user's local timezone
return stationDate.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit',
timeZone: stationTimezone,
timeZoneName: 'short'
});
}
Support
For API issues or questions:
- Check this documentation first
- Review the example code provided
- Contact via the settings URLs in
appv3.json