Skip to main content

Overview

This guide walks you through creating a basic Stremio addon that provides movie catalogs and streams. We’ll build a simple HTTP server that implements the addon protocol.
While this guide uses Rust examples, you can create addons in any language that can serve HTTP. The official addon SDK provides JavaScript/Node.js implementation.

Prerequisites

Step 1: Create the Manifest

The manifest defines your addon’s capabilities. Create a JSON file or endpoint at /manifest.json:
manifest.json
{
  "id": "com.example.movies",
  "version": "1.0.0",
  "name": "Example Movies",
  "description": "Provides free movies and streams",
  "logo": "https://example.com/logo.png",
  "types": ["movie"],
  "resources": ["catalog", "stream"],
  "catalogs": [
    {
      "id": "top",
      "type": "movie",
      "name": "Top Movies",
      "extra": [
        {
          "name": "skip"
        }
      ]
    }
  ],
  "behaviorHints": {
    "adult": false,
    "p2p": false
  }
}
1

Choose a unique ID

Use reverse domain notation: com.yourname.addonname
2

Define supported types

List content types: movie, series, tv, channel, etc.
3

Specify resources

Include resources you’ll implement: catalog, meta, stream, subtitles
4

Configure catalogs

Define browsable content collections with optional filters

Step 2: Implement the Catalog Resource

Create an endpoint at /catalog/{type}/{id}.json:
catalog-handler.js
app.get('/catalog/:type/:id.json', (req, res) => {
  const { type, id } = req.params;
  
  // Parse skip parameter from query
  const skip = parseInt(req.query.skip) || 0;
  
  if (type !== 'movie' || id !== 'top') {
    return res.status(404).json({ error: 'Catalog not found' });
  }
  
  const metas = [
    {
      id: 'tt1254207',
      type: 'movie',
      name: 'Big Buck Bunny',
      poster: 'https://image.tmdb.org/t/p/w500/example.jpg',
      posterShape: 'poster',
      description: 'A large rabbit escapes...',
      releaseInfo: '2008',
      runtime: '10 min'
    },
    // Add more movies...
  ];
  
  res.json({
    metas: metas.slice(skip, skip + 100)
  });
});

Key Points

Each meta must have id, type, and name. Other fields are optional but recommended.
Provide high-quality poster URLs. Use posterShape to indicate aspect ratio.

Step 3: Implement the Stream Resource

Create an endpoint at /stream/{type}/{id}.json:
stream-handler.js
app.get('/stream/:type/:id.json', (req, res) => {
  const { type, id } = req.params;
  
  if (type !== 'movie') {
    return res.status(404).json({ error: 'Type not supported' });
  }
  
  // Example: Big Buck Bunny
  if (id === 'tt1254207') {
    return res.json({
      streams: [
        {
          url: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
          name: '1080p',
          description: 'High quality stream'
        },
        {
          url: 'https://example.com/720p.mp4',
          name: '720p',
          description: 'Medium quality stream'
        }
      ]
    });
  }
  
  // No streams available
  res.json({ streams: [] });
});

Stream Types

Direct URL

{
  "url": "https://example.com/video.mp4"
}

Torrent

{
  "infoHash": "24c8...",
  "fileIdx": 0
}

YouTube

{
  "ytId": "dQw4w9WgXcQ"
}

External

{
  "externalUrl": "https://..."
}

Step 4: Add CORS Headers

CORS headers are required for web clients to access your addon.
cors-middleware.js
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', '*');
  next();
});

Step 5: Test Your Addon

Local Testing

  1. Start your server:
node server.js
  1. Test the manifest:
curl http://localhost:3000/manifest.json
  1. Test a catalog:
curl http://localhost:3000/catalog/movie/top.json
  1. Test streams:
curl http://localhost:3000/stream/movie/tt1254207.json

Install in Stremio

For local testing, use a tunneling service like ngrok:
ngrok http 3000
Then in Stremio:
  1. Go to Addons
  2. Click the puzzle icon (Community Addons)
  3. Paste your ngrok URL with /manifest.json
  4. Click Install

Complete Example

Here’s a minimal working addon server:
server.js
const express = require('express');
const app = express();

// CORS middleware
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', '*');
  next();
});

// Manifest
app.get('/manifest.json', (req, res) => {
  res.json({
    id: 'com.example.movies',
    version: '1.0.0',
    name: 'Example Movies',
    description: 'Free movie streams',
    types: ['movie'],
    resources: ['catalog', 'stream'],
    catalogs: [
      {
        id: 'top',
        type: 'movie',
        name: 'Top Movies'
      }
    ]
  });
});

// Catalog
app.get('/catalog/:type/:id.json', (req, res) => {
  res.json({
    metas: [
      {
        id: 'tt1254207',
        type: 'movie',
        name: 'Big Buck Bunny',
        poster: 'https://image.tmdb.org/t/p/w500/example.jpg'
      }
    ]
  });
});

// Streams
app.get('/stream/:type/:id.json', (req, res) => {
  if (req.params.id === 'tt1254207') {
    res.json({
      streams: [
        {
          url: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
          name: '1080p'
        }
      ]
    });
  } else {
    res.json({ streams: [] });
  }
});

app.listen(3000, () => {
  console.log('Addon running on port 3000');
});

Advanced Features

Meta Resource

Provide detailed information with episodes:
app.get('/meta/:type/:id.json', (req, res) => {
  res.json({
    meta: {
      id: 'tt0944947',
      type: 'series',
      name: 'Game of Thrones',
      poster: 'https://example.com/poster.jpg',
      description: 'Nine noble families fight...',
      videos: [
        {
          id: 'tt0944947:1:1',
          title: 'Winter Is Coming',
          season: 1,
          episode: 1,
          released: '2011-04-17T00:00:00Z'
        }
      ]
    }
  });
});

Subtitles Resource

app.get('/subtitles/:type/:id.json', (req, res) => {
  res.json({
    subtitles: [
      {
        id: 'en',
        lang: 'eng',
        url: 'https://example.com/subtitles/en.srt'
      }
    ]
  });
});

Configuration

For configurable addons:
{
  "behaviorHints": {
    "configurable": true,
    "configurationRequired": false
  }
}
Then use URL parameters:
https://example.com/{apiKey}/manifest.json

Deployment

Vercel

Deploy serverless Node.js addons

Heroku

Deploy any language/framework

Railway

Easy deployment with automatic HTTPS

Deployment Checklist

1

HTTPS Required

Stremio requires HTTPS for production addons
2

CORS Headers

Ensure all endpoints return proper CORS headers
3

Error Handling

Return proper HTTP status codes and error messages
4

Caching

Implement cache headers to reduce server load

Best Practices

  • Cache responses when possible
  • Use CDNs for images and static content
  • Implement pagination for large catalogs
  • Return empty arrays instead of errors when no content is available
  • Return { streams: [] } when no streams are available
  • Return { metas: [] } for empty catalogs
  • Use appropriate HTTP status codes
  • Log errors for debugging
  • Use high-quality poster images
  • Provide accurate metadata
  • Include descriptions and release information
  • Use standard ID formats (IMDb, TMDB, etc.)
  • Validate all input parameters
  • Rate limit your endpoints
  • Don’t expose API keys in responses
  • Use HTTPS in production

Resources

Official SDK

JavaScript/Node.js addon SDK

Addon Examples

Example addons and templates

Testing Tool

Validate your addon manifest

Community

Get help from the community

Next Steps

Build docs developers (and LLMs) love