Streaming API

How to play RadioMV stations in your app, website, or web service, and how to show now-playing metadata. Everything on this page is public — no API key or registration required.

Quick Start

I want to…Use this URL
Play a station in an app or browser (HLS, adaptive quality)https://stream.radiomv.com/stream/{station}.m3u8
Play a station on a simple / hardware MP3 playerhttp://stream.radiomv.com/stream/{station}.m3u — fetch it, then follow the URL inside
Show what's playing right nowhttps://stream.radiomv.com/api/metadata/{station}
Get live now-playing updates (no polling)https://stream.radiomv.com/api/metadata/{station}/events (SSE)

Replace {station} with an ID from the station list, e.g.:

GET /stream/english.m3u8

Base URL & Hostname Rules

Always start from https://stream.radiomv.com. It geo-routes each listener to a nearby server automatically.

Never hardcode a *.radiomv.live hostname Playlists returned by stream.radiomv.com contain absolute URLs on *.radiomv.live hosts — that is by design (it sends audio traffic directly to the server nearest the listener). Those hostnames are internal infrastructure: they can change, move, or disappear at any time. Follow them when a playlist hands them to you; never store them in your app, config, or database.
If you restrict outbound hosts, allow both domains A browser player pointed at stream.radiomv.com will fetch audio segments from *.radiomv.live. For a web page that means CSP directives like:

media-src https://stream.radiomv.com https://*.radiomv.live;
connect-src https://stream.radiomv.com https://*.radiomv.live;

HTTPS vs HTTP: use https:// everywhere you can. Plain http:// is deliberately kept working for the audio endpoints (.mp3, .m3u) so embedded and hardware players without TLS support can connect.

Stations

Live Stations

24/7 curated programs and music.

Station IDNameLanguage
slavicSlavicRussian
slavic-musicSlavic MusicRussian
slavic-kidsSlavic KidsRussian
englishEnglishEnglish
spanishSpanishSpanish
germanGermanGerman

Bible Stations

Continuous audio Bible reading, 24/7.

Station IDsName
slavic-nt / slavic-otSlavic New / Old Testament
english-nt / english-otEnglish New / Old Testament
spanish-nt / spanish-otSpanish New / Old Testament
german-nt / german-otGerman New / Old Testament

From an integration standpoint, Bible stations behave exactly like the live stations — same URLs, same players, same metadata API (the title is the current book and chapter). The only difference is that their HLS masters have no ultra quality tier.

Discover stations at runtime GET /api/metadata returns one entry per station — use it to build your station list dynamically instead of hardcoding it.

Option 1 — HLS (Recommended)

HLS is the recommended way to play RadioMV in any modern environment: iOS, Android, web browsers, smart TVs, VLC, ffmpeg. It adapts quality to the listener's bandwidth automatically.

One URL is all you need — the master playlist:

GET /stream/{station}.m3u8

Give that URL to your player. It discovers the quality variants, picks one, and pulls audio segments on its own.

Quality Tiers

What the master playlist advertises today:

VariantBitrateCodec
lofi~24–32 kbpsHE-AAC v2
midfi64 kbpsHE-AAC
hifi128 kbpsAAC-LC
ultra248 kbpsAAC-LC

Bible stations omit the ultra tier. Segments are 6-second fMP4/CMAF (.m4s).

Do not construct variant or segment URLs yourself Tiers, bitrates, and URL layout can change; the master playlist is the contract. The variant URLs it returns include a query string (e.g. ?sid=...) — pass URLs through unmodified, exactly as any standard HLS player already does.

Web Embed (hls.js + Safari)

Safari plays HLS natively; other browsers need hls.js:

<audio id="player" controls></audio>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
    const url = "https://stream.radiomv.com/stream/english.m3u8";
    const audio = document.getElementById("player");
    if (audio.canPlayType("application/vnd.apple.mpegurl")) {
        audio.src = url;                    // Safari / iOS
    } else if (Hls.isSupported()) {
        const hls = new Hls();
        hls.loadSource(url);
        hls.attachMedia(audio);
    }
</script>

CORS is open (Access-Control-Allow-Origin: *) on all streaming and metadata endpoints, so this works from any web origin.

Option 2 — MP3 (Simple Players & Hardware)

A single fixed-quality 128 kbps MP3 stream, Icecast/SHOUTcast-compatible. Works with anything that can play an MP3 URL: <audio> tags, VLC, Winamp, internet-radio hardware, microcontrollers.

Preferred pattern — resolve, then play:

  1. Fetch the playlist:
    GET /stream/{station}.m3u
  2. The response body is one line: the direct MP3 stream URL for the server assigned to this listener.
  3. Connect your player to that URL.
  4. On every reconnect or new playback session, re-fetch the .m3u first. Never save the resolved URL — it points at one specific server, and a saved URL stops working if that server is replaced. Devices that re-resolve heal automatically; devices that cache the final URL are the ones that end up "connecting forever."

Direct URL (also supported — e.g. for a plain <audio> tag where the playlist indirection is impractical):

GET /stream/{station}.mp3

ICY (SHOUTcast) Metadata

If your client sends the header Icy-MetaData: 1, the stream interleaves now-playing titles in standard ICY format. The response includes:

icy-name: English
icy-br: 128
icy-metaint: 16000

icy-metaint is the byte interval between metadata blocks. If you don't send Icy-MetaData: 1, you get a clean MP3 stream with no interleaved metadata — use the JSON metadata API instead.

Now-Playing Metadata

JSON, no auth, CORS-open. Titles for Bible stations are the current book and chapter; for live stations, the current track or program.

GET /api/metadata

Array — now playing on all stations.

GET /api/metadata/{station}

Object — now playing on one station:

{
    "station": "english",
    "name": "English",
    "title": "The Righteous Requirement of the Law | Clip 1 | Derek Prince",
    "artist": "J Scott Rakozy",
    "updated_at": 1787445124
}

updated_at is Unix seconds. artist may be empty. Treat unknown extra fields as forward-compatible additions.

GET /api/metadata/{station}/events

Server-Sent Events stream — holds the connection open and pushes each change as a data: line with the same JSON shape:

const es = new EventSource("https://stream.radiomv.com/api/metadata/english/events");
es.onmessage = (e) => {
    const now = JSON.parse(e.data);
    document.getElementById("title").textContent = now.title;
};
Prefer SSE over polling If you must poll instead, poll GET /api/metadata/{station} no more than once every 10–15 seconds. All endpoints are rate-limited per IP; well-behaved clients will never notice the limits.

Testing From the Command Line

# Now playing
curl -s https://stream.radiomv.com/api/metadata/english

# Live metadata feed (Ctrl-C to stop)
curl -N https://stream.radiomv.com/api/metadata/english/events

# HLS master playlist
curl -s https://stream.radiomv.com/stream/english.m3u8

# Play in VLC / ffplay / mpv
vlc https://stream.radiomv.com/stream/english.m3u8
ffplay -nodisp https://stream.radiomv.com/stream/english.m3u8

# MP3: resolve the playlist, then probe the stream it points to
curl -s http://stream.radiomv.com/stream/english.m3u

Identify Your App (User-Agent)

Please send a User-Agent that names your app or service — not your HTTP library's default. It's how we recognize your integration in our listener analytics, notice when one of your releases changes behavior, and reach you before making a change that would affect you.

The shape we'd like (standard HTTP User-Agent format):

User-Agent: YourApp/2.1 (Android 14; https://yourapp.example)

Nothing is gated on the format — a missing or generic User-Agent still plays. But an identifiable one gets you better support and a heads-up before changes.

Being a Good Citizen

Stability of This Contract

The URLs and JSON shapes documented here are the supported public interface. Anything you discover beyond this page (internal hostnames, undocumented paths, extra fields) may change without notice. If this page and observed behavior disagree, tell us — we'll fix whichever is wrong.