{"slug": "build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api", "title": "Build a \"Where to Watch\" feature in 50 lines with the StreamWatchHub API", "summary": "The article introduces StreamWatchHub, a new API that consolidates streaming service and live football broadcaster data across 10 countries into a single REST endpoint with a consistent schema, available on RapidAPI with a free tier of 1,000 calls per month. It demonstrates how to build a production-ready \"where to watch\" feature using roughly 50 lines of Node.js code and a small React component, requiring only one API call per title to retrieve offers with monetization types. The API covers movies, series, and sports matches, and the team is evaluating additional features like a recommendation endpoint while seeking feedback on how to expose a confidence score.", "body_md": "Most teams shipping a \"where can I watch X?\" feature run into the same set of problems:\n\n- JustWatch does not offer a public API.\n- TMDB watch-providers covers the major markets only, without rent prices or deep links.\n- Live sports availability requires a separate broadcaster feed.\n- Platform names, regional catalogs, and deep-link formats all need to be reconciled.\n\n**StreamWatchHub** consolidates this into a single REST endpoint and one consistent schema, covering every streaming service plus live football broadcasters across 10 countries. The API is now live on [RapidAPI](https://rapidapi.com/kavela-kavela-default/api/streamwatchhub-movies-series/pricing) with a free tier (1,000 calls/month), so it can be evaluated without a payment method.\n\nThis post walks through wiring a production-ready \"where to watch\" feature in roughly 50 lines of Node.js plus a small React component.\n\n## What the API returns\n\nA single call per title, with the monetization type flagged on every offer:\n\n```\ncurl \"https://streamwatchhub.p.rapidapi.com/v1/search?q=the+bear&country=US\" \\\n  -H \"X-RapidAPI-Key: $KEY\" \\\n  -H \"X-RapidAPI-Host: streamwatchhub.p.rapidapi.com\"\n```\n\nResponse (trimmed):\n\n```\n{\n  \"results\": [{\n    \"tmdb_id\": 136315,\n    \"title\": \"The Bear\",\n    \"type\": \"series\",\n    \"year\": 2022,\n    \"offers\": [\n      { \"platform\": \"hulu\",     \"type\": \"flatrate\", \"url\": \"https://hulu.com/series/...\" },\n      { \"platform\": \"disney\",   \"type\": \"flatrate\", \"url\": \"https://disneyplus.com/...\" },\n      { \"platform\": \"apple\",    \"type\": \"rent\",     \"price\": \"$2.99\", \"url\": \"https://tv.apple.com/...\" }\n    ]\n  }]\n}\n```\n\nNo per-platform SDKs, no scraping fallbacks. The same response shape applies to movies, series, and football matches — sports responses substitute `kickoff_at`\n\nand `broadcasters[]`\n\nfor `offers[]`\n\n.\n\n## 1. Sign up and obtain an API key\n\n- Visit the\n[RapidAPI listing](https://rapidapi.com/kavela-kavela-default/api/streamwatchhub-movies-series/pricing)and select the free plan. - Copy the issued RapidAPI key and export it:\n`export RAPIDAPI_KEY=...`\n\n## 2. A minimal Node client\n\n``` js\n// swh.js\nconst HOST = \"streamwatchhub.p.rapidapi.com\";\nconst BASE = `https://${HOST}/v1`;\n\nexport async function whereToWatch(query, country = \"US\") {\n  const url = new URL(`${BASE}/search`);\n  url.searchParams.set(\"q\", query);\n  url.searchParams.set(\"country\", country);\n\n  const res = await fetch(url, {\n    headers: {\n      \"X-RapidAPI-Key\":  process.env.RAPIDAPI_KEY,\n      \"X-RapidAPI-Host\": HOST,\n    },\n  });\n  if (!res.ok) throw new Error(`SWH ${res.status}`);\n  return res.json();\n}\n```\n\nNo client library is required. The full surface is seven GET endpoints (`/search`\n\n, `/title/{id}/offers`\n\n, `/series/{id}/season/{n}/offers`\n\n, `/match/{id}/broadcasters`\n\n, `/fixtures`\n\n, `/platforms`\n\n, `/changes`\n\n).\n\n## 3. A drop-in React component\n\n``` js\nimport { useEffect, useState } from \"react\";\nimport { whereToWatch } from \"./swh\";\n\nexport function WhereToWatch({ title, country = \"US\" }) {\n  const [data, setData] = useState(null);\n  const [err,  setErr]  = useState(null);\n\n  useEffect(() => {\n    whereToWatch(title, country).then(setData).catch(setErr);\n  }, [title, country]);\n\n  if (err)        return <p>Couldn't load options.</p>;\n  if (!data)      return <p>Loading…</p>;\n  const hit = data.results?.[0];\n  if (!hit)       return <p>No streaming match in {country}.</p>;\n\n  return (\n    <div>\n      <h3>{hit.title} <small>({hit.year})</small></h3>\n      <ul>\n        {hit.offers.map((o, i) => (\n          <li key={i}>\n            <a href={o.url} target=\"_blank\" rel=\"noopener\">\n              <strong>{o.platform}</strong> — {o.type}\n              {o.price && <> · {o.price}</>}\n            </a>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\nUsage: `<WhereToWatch title=\"The Bear\" country=\"US\" />`\n\n.\n\n## Current coverage\n\n-\n**14,000+ titles**(movies and TV series), refreshed daily -\n**70+ providers**— Netflix, Disney+, HBO Max, Apple TV, OSN+, Shahid, Movistar Plus+, Kinopoisk, Now TV, Sky Go, Paramount+, and others -\n**10 countries** at launch — US, GB, DE, ES, IT, FR, TR, BR, IN, SA (more being added) -\n**Live football**— 23 leagues including the Big Five plus UCL / UEL / UECL and the Saudi Pro League, with per-country broadcaster resolution (the same fixture airs on different channels in each market, and the API resolves the correct one) -\n**Confidence scores** on broadcaster matches so low-confidence entries can be filtered out client-side -\n**Deep links** that open the correct detail page on each platform\n\n## Roadmap\n\n- A dedicated\n**Sports** RapidAPI listing is launching in the coming weeks; football fixtures are already accessible inside the current Movies & Series listing. - Additional countries are scheduled: Mexico, Argentina, Australia, Japan, and Korea.\n- A natural-language\n`/recommend`\n\nendpoint is under evaluation (\"where can I watch the Champions League final in Romania tonight?\") — the team is still assessing whether it adds enough value beyond the structured endpoints.\n\n## Try it\n\n- 🌐 Landing and documentation:\n[streamwatchhub.com/api](https://streamwatchhub.com/api/) - 💚 Free tier on RapidAPI:\n[Movies & Series listing](https://rapidapi.com/kavela-kavela-default/api/streamwatchhub-movies-series/pricing) - 📦 Source examples (cURL, Node, Python, PHP):\n[github.com/Kaveladev/streamwatchhub](https://github.com/Kaveladev/streamwatchhub)\n\nFeedback is welcome before the v1 schema is frozen. One open question for readers: how should `confidence_score`\n\nbe exposed — as a raw number, a `low / medium / high`\n\nenum, or hidden entirely below a threshold? Comments are appreciated.\n\nThanks for reading.", "url": "https://wpnews.pro/news/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api", "canonical_source": "https://dev.to/kavelaltd/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api-5aob", "published_at": "2026-05-23 17:47:14+00:00", "updated_at": "2026-05-23 18:01:44.903650+00:00", "lang": "en", "topics": ["developer-tools", "products", "data", "enterprise-software"], "entities": ["StreamWatchHub", "RapidAPI", "Node.js", "React", "Hulu", "Disney", "Apple"], "alternates": {"html": "https://wpnews.pro/news/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api", "markdown": "https://wpnews.pro/news/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api.md", "text": "https://wpnews.pro/news/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api.txt", "jsonld": "https://wpnews.pro/news/build-a-where-to-watch-feature-in-50-lines-with-the-streamwatchhub-api.jsonld"}}