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 hosted on Cloudflare. This means:
- No authentication required
- No rate limiting
- Fast global edge delivery
- High availability
Do's
Always fetch schedule URLs from app config
Never hardcode schedule URLs. Always fetch
appv2.json first and use the stationScheduleUrl field from each station. 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://api.radiomv.studio/appv2.json');
const data = await config.json();
// 2. Get schedule URL from station object
const station = data.stations.find(s => s.id === "1");
const scheduleUrl = station.stationScheduleUrl;
// 3. Fetch schedule using the URL from config
const schedule = await fetch(scheduleUrl);
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 |
|---|---|---|
appv1.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 — Check
audioPrioritybut prefer HLS for better compatibility - 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 appv1.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
appv2.jsonviastationScheduleUrl - Don't poll constantly — No need to fetch more than once per app session or daily
- Don't hardcode station IDs — Always fetch from
appv2.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://api.radiomv.studio/appv1.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
appv1.json