# Metered TURN Server Service — llms.txt reference Metered TURN Servers as a Service provide a globally distributed relay network for WebRTC traffic. The service guarantees connectivity through NATs and firewalls via TURN (Traversal Using Relays around NAT). - 31+ regions, 100+ edge PoPs, <30 ms latency, 99.999% uptime SLA - UDP, TCP, TLS, DTLS on ports 80/443. No bandwidth caps per allocation. - Standards: STUN (RFC 5389), TURN (RFC 5766), ICE - Private high-speed backbone for cross-continent relay - REST API for credentials, usage tracking, project isolation - Dashboard: https://dashboard.metered.ca | Pricing: https://www.metered.ca/pricing ## Getting Started 1. **Sign up** at https://dashboard.metered.ca/signup to create a free Metered account. 2. Your **app name** (e.g. `yourappname`) is shown on the Dashboard home page. It forms part of the REST API base URL: `https://yourappname.metered.live/api/v1/turn/`. 3. Go to **Dashboard → Developers** to find your **Secret Key**. Use this for all server-side API calls (creating credentials, fetching usage, managing projects). Never expose it in front-end code. 4. **Create a TURN credential**: Go to **Dashboard → TURN Server** and click **"Add Credential"**, or use the Create Credential REST API. Each credential gets: - `username` and `password` — used in the ICE server config for WebRTC connections - `apiKey` — a credential-scoped key, **safe for front-end use**, used only with the Get Credential API to fetch the ICE server config 5. Click **"Show ICE Servers Array"** on any credential in the Dashboard to get a ready-to-use ICE config for your WebRTC app. **Three types of API keys:** - **`secretKey`** — account-scoped, server-side only. Found in Dashboard → Developers. - **`apiKey`** — credential-scoped, safe for front-end. Returned when creating a credential. Used only for the Get Credential endpoint. - **`projectApiKey`** — project-scoped, alternative to secretKey for project-level operations. Found in Dashboard → TURN Server → Projects. --- ## Overview & Setup ICE tries connections in order: host candidates (direct), STUN (public IP discovery), then TURN (full relay fallback). TURN guarantees connectivity through symmetric NAT, carrier-grade NAT, and strict firewalls. The relay forwards encrypted payloads (SRTP/DTLS) without decrypting them. Use cases: voice/video calls, live streaming, IoT devices, AI/3D/AR/VR, surveillance, DataChannels (chat, gaming, file-share). ### Custom Domain Point a CNAME at Metered infrastructure (global or region-specific hostname). TURN over TCP and UDP work with CNAME; TURN over TLS does not. --- ## TURN URLs & Ports ### Global Endpoint (recommended) `global.relay.metered.ca` — automatic geo-routing to nearest edge. STUN: `stun:stun.relay.metered.ca:80` | Port | Protocol | URI | |------|----------|--------------------------------------------------| | 80 | UDP | turn:global.relay.metered.ca:80 | | 80 | TCP | turn:global.relay.metered.ca:80?transport=tcp | | 443 | UDP | turn:global.relay.metered.ca:443 | | 443 | TLS | turns:global.relay.metered.ca:443?transport=tcp | Best practice: include multiple entries (UDP first, then TCP/TLS) for ICE fallback. ### Recommended ICE Servers Array ```json [ { "urls": "stun:stun.relay.metered.ca:80" }, { "urls": "turn:global.relay.metered.ca:80", "username": "", "credential": "" }, { "urls": "turn:global.relay.metered.ca:80?transport=tcp", "username": "", "credential": "" }, { "urls": "turn:global.relay.metered.ca:443", "username": "", "credential": "" }, { "urls": "turns:global.relay.metered.ca:443?transport=tcp", "username": "", "credential": "" } ] ``` ### Region-Specific Endpoints All use ports 80/443. Paid plans only (free plan: `standard.relay.metered.ca` only). | Region | Endpoint | API region value | |------------------|--------------------------------|---------------------| | Global | global.relay.metered.ca | global | | North America | na.relay.metered.ca | north_america | | Europe | europe.relay.metered.ca | europe | | European Union | eu.relay.metered.ca | eu | | Asia | asia.relay.metered.ca | asia | | Oceania | oceania.relay.metered.ca | oceania | | US West | us-west.relay.metered.ca | us_west | | US Central | us-central.relay.metered.ca | us_central | | US East | us-east.relay.metered.ca | us_east | | Canada Central | ca-central.relay.metered.ca | canada_central | | Canada East | ca-east.relay.metered.ca | canada_east | | Europe West | eu-west.relay.metered.ca | europe_west | | Europe Central | eu-central.relay.metered.ca | europe_central | | Asia West | asia-west.relay.metered.ca | asia_west | | Asia East | asia-east.relay.metered.ca | asia_east | | Middle East | middle-east.relay.metered.ca | middle_east | | Canada | ca.relay.metered.ca | canada | | USA | us.relay.metered.ca | usa | | UK | uk.relay.metered.ca | uk | | Singapore | sg.relay.metered.ca | singapore | | India | in.relay.metered.ca | india | | Seoul | seoul.relay.metered.ca | seoul | | France | fr.relay.metered.ca | france | | Germany | de.relay.metered.ca | germany | | Italy | it.relay.metered.ca | italy | | Japan | jp.relay.metered.ca | japan | | Sweden | se.relay.metered.ca | sweden | | Spain | es.relay.metered.ca | spain | | Netherlands | nl.relay.metered.ca | netherlands | | Australia | au.relay.metered.ca | australia | | Qatar | qa.relay.metered.ca | qatar | | Standard (free) | standard.relay.metered.ca | standard | --- ## Creating Credentials **Dashboard**: Log into https://dashboard.metered.ca -> TURN Server page -> "Add Credential" -> "Show ICE Servers Array" to get the config. **REST API**: POST to Create Credential endpoint (see below). Response includes `username`, `password`, and `apiKey`. Use the `apiKey` to call Get Credential from the front-end. The `apiKey` is credential-scoped and safe for front-end use. The `secretKey` must never be exposed in front-end code. --- ## Region Pinning **Dashboard**: Select region from dropdown; "Show ICE Servers Array" returns region-scoped endpoints. **API**: Pass `region` query param to Get Credential API for a specific region. Omit it to use the dashboard default. Different credentials can target different regions. **Dynamic updates**: If you use the Get Credential API (with `apiKey`), changing the dashboard region automatically updates what the API returns. --- ## REST API — Authentication Replace `` with your app name (Dashboard -> Developers). **v1 endpoints** (base: `https://.metered.live/api/v1/turn/`): Get Credential, Create Credential, Enable Credential, Disable Credential, Delete Credential, Current Usage, Current Usage by Date, Current Usage for User. **v2 endpoints** (base: `https://.metered.live/api/v2/turn/`): Update Credential Label, List Credentials, Current Usage by User, Daily Usage by User, Delete Credential by Label, all Project endpoints. **v2 payment endpoint** (base: `https://.metered.live/api/v2/payment/`): Get Usage Charges — note this uses `/api/v2/payment/`, NOT `/api/v2/turn/`. Each endpoint below shows its full path including the correct version prefix. Auth methods (all via query param): 1. `apiKey` — credential-scoped, safe for front-end. Only for Get Credential endpoint. 2. `secretKey` — account-scoped, server-side only. Dashboard -> Developers. 3. `projectApiKey` — project-scoped, alternative to secretKey for project endpoints. --- ## REST API — Credentials ### Get TURN Credential (ICE Servers Array) Returns the ICE Servers array. Safe for front-end use. ``` GET /api/v1/turn/credentials?apiKey= ``` Query: `apiKey` (required), `region` (optional, overrides dashboard default). Response `200 OK` — array of ICE server objects (STUN + TURN entries with urls, username, credential). Errors: `400` (no API key), `401` (invalid API key). ### Create TURN Credential ``` POST /api/v1/turn/credential?secretKey= Content-Type: application/json ``` Body (JSON): - `expiryInSeconds` (integer, optional) — auto-expire after this many seconds - `label` (string, optional) — a label for the credential Response `200 OK` — object with `username`, `password`, `expiryInSeconds`, `label`, `apiKey`. Errors: `400` (invalid expiry or not subscribed). ### Enable TURN Credential Re-enables a previously disabled credential. ``` POST /api/v1/turn/credential/enable?secretKey= ``` Body: `{ "username": "" }` Response `200 OK`: `{ "message": "Credential enabled successfully" }` Errors: `400` (missing username, already enabled), `401` (invalid key, not found). ### Disable TURN Credential ``` POST /api/v1/turn/credential/disable?secretKey= ``` Body: `{ "username": "" }` Response `200 OK`: `{ "message": "Turn credential disabled successfully" }` Errors: `400` (missing username, already disabled), `401` (not found). ### Delete TURN Credential ``` DELETE /api/v1/turn/credential?secretKey= ``` Body: `{ "username": "" }` Response `200 OK`: `{ "success": true, "message": "credential removed" }` Errors: `400` (credential not found). ### Update TURN Credential Label ``` POST /api/v2/turn/credential/update_label?secretKey=&username= ``` Body: `{ "label": "" }` Response `200 OK` — object with `username`, `password`, `expiryInSeconds`, `label`, `apiKey`. Errors: `400` (credential not found). ### List TURN Credentials (v2) ``` GET /api/v2/turn/credentials?secretKey= ``` Query params: - `secretKey` (string, required) - `all` (boolean, optional) — include expired credentials; default false - `page` (integer, optional) — page number for pagination - `label` (string, optional) — filter by label Response `200 OK` — object with `data[]` and `pagination`. Each credential in `data[]`: `username`, `password`, `apiKey`, `expiryInSeconds`, `label`, `expired` (bool). `pagination`: `total_records`, `current_page`, `total_pages`, `next_page`, `prev_page`. Errors: `400` (not subscribed, not available under free plan). ### Delete TURN Credential by Label Deletes all active (non-expired) credentials matching a label. ``` DELETE /api/v2/turn/credential/by_label?secretKey=&label=