Skip to content

antoniocosta/RTPPlayAPI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RTP Play API Client

Unofficial API client for RTP Play (Portuguese public broadcaster). Supports all known endpoints for channels, programs, episodes, search, and media assets.

If you like this or find it useful, support the project by buying me a coffee.

Buy Me A Coffee

Status

Auth: Working — HMAC-SHA256 authentication compatible with the current RTP Play mobile app.

Two things must be right beyond the credentials/HMAC, or the server returns a blanket {"error":"Not Authenticated"} (or a Fastly 404/403 at the edge):

  1. TLS fingerprint. RTP's Fastly edge blocks common "scripting" TLS fingerprints. Python's requests/urllib are rejected — the Python client here uses curl_cffi to present a browser fingerprint. Node's native https is accepted, so the Node client needs no impersonation.

  2. IP reputation (not geography). The API is not geo-restricted and works from clean IPs in any country (PT, ES, GB, DE, US all verified). Only heavily-abused hosting ranges are blocked at the edge. If your IP is blocked, route through any clean proxy. (Video playback of some titles is separately geo-restricted to Portugal, but listing/metadata is not.)

Quick Start

Python

pip install curl_cffi   # required — plain requests/urllib are blocked by RTP's TLS fingerprinting
import sys
sys.path.append('python')
from rtpplay_api import RTPPlayAPI

api = RTPPlayAPI()

# List channels
channels = api.get_channels()
print(channels)

# Search for a program
results = api.search('telejornal')

# Get episodes (program_id 16048 = Telejornal 2026)
episodes = api.list_episodes('16048')

# Get episode with video asset URLs
episode = api.get_episode('16048', '942036')

Node.js

const RTPPlayAPI = require('./node/rtpplay_api');
const api = new RTPPlayAPI();

// List channels
const channels = await api.getChannels();
console.log(channels);

// Search
const results = await api.search('telejornal');

// Get episodes
const episodes = await api.listEpisodes('16048');

// Get episode with assets
const episode = await api.getEpisode('16048', '942036');

Using a proxy

# SOCKS5 proxy
api = RTPPlayAPI(proxy='socks5h://127.0.0.1:1080')

# HTTP proxy
api = RTPPlayAPI(proxy='http://proxy.example.com:8080')
// Node needs a proxy only if your IP is blocklisted. Pass any http(s).Agent:
const { SocksProxyAgent } = require('socks-proxy-agent');
const api = new RTPPlayAPI({ agent: new SocksProxyAgent('socks5h://127.0.0.1:1080') });

Authentication

Uses HMAC-SHA256 authentication matching the RTP Play mobile app.

How it works:

  1. Generate a millisecond timestamp
  2. Interleave the timestamp digits into the auth key: iterating the key, insert one timestamp char before the key char at positions where keyIndex % 5 == 3
  3. Compute HMAC-SHA256(interleaved_key, timestamp + auth_url) as lowercase hex, US-ASCII
  4. Send name + hash + timestamp in RTP-Play-Auth / RTP-Play-Auth-Hash / RTP-Play-Auth-Timestamp headers to https://rtpplayapi.rtp.pt/play/api/2/token-manager
  5. Receive a bearer token (~8h lifetime); send it as Authorization: Bearer <token> on all /play/api/1/ requests

Credential — exactly one works (verified against the live server; the others return "Not Authenticated"):

Name Key Works
MOBILE_ANDROID_RTPPLAY_2023 lEoCFdSOqTyUjh8KcSrTyXzyrXQvqTkaIYy2YLSaVmWaH8
RTPPLAY_ANDROID_NEW HdadKT32JSDUddKga34EdfZDFlI3sSw7
MOBILE_ANDROID_RTPPLAY_NEW Nk9QpgDLtzEgBfIVBJkY41uFsQJZsb3G

The issued token's user field is "Mobile Android 2023". The auth URL is part of the signed message, so it must match exactly.

The old iOS credentials (RTPPLAY_MOBILE_IOS) are dead as of June 2026.

API Endpoints

Method Description
list_episodes(program_id) List episodes for a program (paginated, 30/page)
get_episode(program_id, episode_id) Get episode details + video assets
get_program(program_id) Get program metadata
get_channels() List all channels
get_channel(channel_id) Get channel details
get_channel_epg(channel_id, date) Electronic Program Guide
get_live_tv_channels() Live TV with EPG
get_live_radio_channels() Live radio channels
get_programs(limit, category_id, ...) List programs with filters
get_categories() Content categories
get_collection(collection_id) Collection details
get_slideshow() Featured/homepage content
get_popular(asset_type, page) Popular content
search(query, page) Full-text search
get_asset(asset_id) Media asset details

See API.md for detailed endpoint reference with request/response examples.

Structure

├── python/rtpplay_api.py   # Python implementation (requires: curl_cffi)
├── node/rtpplay_api.js     # Node.js implementation (no runtime deps; socks-proxy-agent optional)
├── API.md                  # Complete API reference
└── README.md               # This file

Testing from your machine

Quick test to verify auth works from your IP:

cd python
python -c "
from rtpplay_api import RTPPlayAPI
api = RTPPlayAPI()   # add proxy='socks5h://127.0.0.1:1080' if your IP is blocklisted
channels = api.get_channels()
print(f'OK — got {len(channels)} channels')
"

If this prints channel data, auth is working. AuthenticationError means one of: wrong TLS fingerprint (make sure curl_cffi is installed and used), a blocklisted IP (route through a clean proxy), or a clock-skewed timestamp.

History

  • July 2026: Root-caused why auth kept failing — RTP's Fastly edge blocks Python's requests/urllib TLS fingerprint. Switched the Python client to curl_cffi (browser impersonation); Node's native TLS was already accepted. Verified the API works from clean IPs worldwide (not geo/residential-gated). Auth URL is rtpplayapi.rtp.pt.
  • June 2026: Old iOS auth stopped working. Replaced with Android HMAC-SHA256 auth.
  • Original: Based on iOS credentials from yt-dlp and guipenedo/rtp-play-api.

License

Educational and research purposes only. Content © RTP (Rádio e Televisão de Portugal).

Credits

  • HMAC-SHA256 auth support (2026)
  • yt-dlp project — original iOS authentication (now defunct)
  • guipenedo/rtp-play-api — original API discovery

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors