# PeerTube Scraper: Videos, Channels, Accounts & Search (`perconey/peertube-scraper`) Actor

Scrape any PeerTube instance via the official /api/v1 REST API. Videos, channels, accounts, search - cross-instance federation routing. No browser, no proxies, no auth. Pay only per result item.

- **URL**: https://apify.com/perconey/peertube-scraper.md
- **Developed by:** [Perconey](https://apify.com/perconey) (community)
- **Categories:** Social media, Developer tools, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 result items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

### What does PeerTube Scraper do?

**PeerTube Scraper** pulls structured data from any [PeerTube](https://joinpeertube.org/) instance via the **official `/api/v1/*` REST API**. Videos with views, likes/dislikes, duration, embed link, thumbnails. Channels with subscriber counts. Accounts with follower counts. Full-text search. The actor calls the documented public API directly: no browser, no proxies, no cookies, no anti-bot fight. One actor works with **every PeerTube-protocol server** in the Fediverse: framatube.org, video.blender.org, tilvids.com, peertube.cpy.re, makertube.net, and hundreds more.

Try it instantly: pick **getVideos**, leave instance as `https://framatube.org`, click Start. You get the 30 newest Framatube videos (title, duration, views, likes, channel) in under 3 seconds for $0.03.

### Why use PeerTube Scraper?

- **Federation researchers**: Compare video communities across instances. Same channel concept exists on lots of servers with different culture and policies.
- **Indie video creators**: Find PeerTube instances that match your niche. Use `searchChannels` to discover where your audience lives outside of YouTube / Vimeo.
- **OSS maintainers**: Track talks and demos about your project. Schedule daily `searchVideos` runs with your project name.
- **Trend analysts**: Both likes AND dislikes are exposed (unlike YouTube). Compute controversy ratios on educational / political videos.
- **Academic researchers**: PeerTube is a federated decentralized video platform on ActivityPub - rich material for network science studies.
- **YouTube alternatives advocates**: Build a discovery layer that surfaces high-quality PeerTube content to mainstream audiences.

### How to use PeerTube Scraper

1. Open the **Input** tab.
2. Pick an **action** from the dropdown. `getVideos` is the simplest starting point.
3. Set **instance** (default `https://framatube.org`). To scrape a different server, paste its URL.
4. For channel / account / video-detail / search actions, fill **queries**.
5. Tune **sort** (-publishedAt / -views / -likes / -trending) and **maxItems** (default 30).
6. Toggle **nsfw** off if you want to skip adult content.
7. Click **Start**.

#### Query format by action

Action | Query format
--- | ---
getVideos | leave empty (uses instance + sort)
searchVideos | free-text search query
getVideoDetail | video uuid (e.g. `9845e20c-7236-4270-8b69-88530a61f616`) or numeric id
getChannels | leave empty (uses instance)
searchChannels | free-text search query
getChannelVideos | `name@host` (e.g. `blender_open_movies@video.blender.org`)
getAccountProfile | `name@host` (e.g. `framasoft@framatube.org`)
getAccountVideos | `name@host`

### Input

Field | Required | Description
--- | --- | ---
`action` | yes | Which API call to make. Eight options.
`instance` | yes | PeerTube instance URL. Default https://framatube.org.
`queries` | sometimes | Required for search / detail / channel / account actions. Empty for the per-instance feeds.
`maxItems` | no | Max items per query. Default 30.
`sort` | no | `-publishedAt` (default), `-views`, `-likes`, `-trending`, or `publishedAt`.
`nsfw` | no | Include NSFW content. Default true (passes through with the nsfw flag for downstream filtering).

### Output

Every item carries `_type` (`video` / `channel` / `account` / `error`) plus `_action` and `_instance`.

```json
{
    "_type": "video",
    "_action": "getVideos",
    "_instance": "https://framatube.org",
    "id": 65272,
    "uuid": "9845e20c-7236-4270-8b69-88530a61f616",
    "name": "Why Free Software Matters for Climate Justice",
    "duration": 1284,
    "views": 12450,
    "likes": 312,
    "dislikes": 4,
    "comments": 27,
    "publishedAt": "2026-05-10T14:00:00Z",
    "nsfw": false,
    "isLive": false,
    "thumbnail_url": "https://framatube.org/lazy-static/thumbnails/...",
    "embed_url": "https://framatube.org/videos/embed/9845e20c-...",
    "url": "https://framatube.org/w/9845e20c-...",
    "channel": { "name": "framasoft", "displayName": "Framasoft", "host": "framatube.org" },
    "account": { "name": "framasoft", "displayName": "Framasoft", "host": "framatube.org" }
}
````

You can download the dataset in JSON, CSV, XML, Excel, RSS or HTML format from the Output tab.

### Data fields

Type | Key fields
\--- | ---
`video` | id, uuid, name, description, duration, views, likes, dislikes, comments, publishedAt, nsfw, isLive, category, language, thumbnail\_url, embed\_url, url, channel, account
`channel` | id, name, displayName, host, description, followersCount, followingCount, url, avatar, ownerAccount
`account` | id, name, displayName, host, description, followersCount, followingCount, url, avatar

### Pricing

**Pay-per-result: $0.001 per item.** No flat monthly fee.

Cost examples:

- Daily 30 newest Framatube videos: **$0.03**
- 1,000 videos from a creator: **$1.00**
- 50 channels matching a search term: **$0.05**
- Top-100 most-viewed videos on an instance: **$0.10**

### Tips

- **Cross-instance routing is automatic.** Write `blender_open_movies@video.blender.org` and the actor talks to video.blender.org even if your `instance` field points to framatube.org. Same pattern as Mastodon / Lemmy.
- **Likes AND dislikes are exposed.** Compute `controversy = min(likes, dislikes) / max(likes, dislikes)` to find polarizing content.
- **Sort options matter.** PeerTube does NOT have a "trending" endpoint, but `-trending` as a sort parameter approximates it.
- **NSFW filtering is client-side.** The API does not let you exclude NSFW on the server; the actor passes the flag through and (optionally) filters in-process.

### FAQ, disclaimers, support

**Is this legal?** The actor calls each PeerTube instance's official public REST API with documented endpoints. Public read access is the design intent of the AGPL-licensed PeerTube software. We send a clear User-Agent identifying the actor and honor rate-limit / Retry-After headers.

**Will I get rate-limited?** PeerTube's per-IP rate limits are generous for read-only traffic and the actor backs off on 429 / Retry-After. For very heavy scraping consider donating to the instance you're hitting most.

**Why is getChannelVideos failing for a channel?** PeerTube channel records live on the channel's HOME instance. Use the `name@host` format like `blender_open_movies@video.blender.org`. The actor auto-routes.

**What about live streams?** `isLive: true` videos appear in the feed. The actor returns metadata only - it does not download the stream.

**Bug or feature request?** Open an Issue on the actor's Issues tab. I usually respond within a day.

**Need a scraper for Mastodon, Lemmy, Bluesky, Hacker News, dev.to, arxiv?** See my other actors at https://apify.com/perconey.

# Actor input Schema

## `action` (type: `string`):

Pick the action. getVideos / getChannels / searchVideos / searchChannels work without queries. The rest need a handle (name@host) or video id/uuid.

## `instance` (type: `string`):

Base URL of the instance to query. Cross-instance routing is automatic for channel/account handles in the form name@host - the actor switches to the right instance even if you pass a different one here.

## `queries` (type: `array`):

One entry per query. searchVideos / searchChannels: free text. getVideoDetail: video id or uuid (e.g. 9845e20c-7236-4270-8b69-88530a61f616). Channel / account actions: name@host (e.g. blender\_open\_movies@video.blender.org).

## `sort` (type: `string`):

PeerTube accepts a prefix - for descending: -publishedAt = newest first, -views = most-viewed, -likes = most-liked, -trending = trending. Leave on -publishedAt for the canonical feed.

## `maxItems` (type: `integer`):

Stop after this many items per query. The actor paginates in batches of 100 (PeerTube's max per request).

## `nsfw` (type: `boolean`):

PeerTube marks adult content with nsfw=true. Default is to include them with the flag passed through; toggle off to skip.

## Actor input object example

```json
{
  "action": "getVideos",
  "instance": "https://framatube.org",
  "queries": [],
  "sort": "-publishedAt",
  "maxItems": 30,
  "nsfw": true
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "instance": "https://framatube.org",
    "queries": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("perconey/peertube-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "instance": "https://framatube.org",
    "queries": [],
}

# Run the Actor and wait for it to finish
run = client.actor("perconey/peertube-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "instance": "https://framatube.org",
  "queries": []
}' |
apify call perconey/peertube-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=perconey/peertube-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "PeerTube Scraper: Videos, Channels, Accounts & Search",
        "description": "Scrape any PeerTube instance via the official /api/v1 REST API. Videos, channels, accounts, search - cross-instance federation routing. No browser, no proxies, no auth. Pay only per result item.",
        "version": "0.1",
        "x-build-id": "M1kGWKgcsYcDwnPGF"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/perconey~peertube-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-perconey-peertube-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/perconey~peertube-scraper/runs": {
            "post": {
                "operationId": "runs-sync-perconey-peertube-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/perconey~peertube-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-perconey-peertube-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "action"
                ],
                "properties": {
                    "action": {
                        "title": "What do you want to scrape?",
                        "enum": [
                            "getVideos",
                            "searchVideos",
                            "getVideoDetail",
                            "getChannels",
                            "searchChannels",
                            "getChannelVideos",
                            "getAccountProfile",
                            "getAccountVideos"
                        ],
                        "type": "string",
                        "description": "Pick the action. getVideos / getChannels / searchVideos / searchChannels work without queries. The rest need a handle (name@host) or video id/uuid.",
                        "default": "getVideos"
                    },
                    "instance": {
                        "title": "PeerTube instance URL",
                        "type": "string",
                        "description": "Base URL of the instance to query. Cross-instance routing is automatic for channel/account handles in the form name@host - the actor switches to the right instance even if you pass a different one here.",
                        "default": "https://framatube.org"
                    },
                    "queries": {
                        "title": "Queries",
                        "type": "array",
                        "description": "One entry per query. searchVideos / searchChannels: free text. getVideoDetail: video id or uuid (e.g. 9845e20c-7236-4270-8b69-88530a61f616). Channel / account actions: name@host (e.g. blender_open_movies@video.blender.org).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "sort": {
                        "title": "Sort order (getVideos / getChannelVideos / getAccountVideos)",
                        "enum": [
                            "-publishedAt",
                            "-views",
                            "-likes",
                            "-trending",
                            "publishedAt"
                        ],
                        "type": "string",
                        "description": "PeerTube accepts a prefix - for descending: -publishedAt = newest first, -views = most-viewed, -likes = most-liked, -trending = trending. Leave on -publishedAt for the canonical feed.",
                        "default": "-publishedAt"
                    },
                    "maxItems": {
                        "title": "Max items per query",
                        "minimum": 0,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Stop after this many items per query. The actor paginates in batches of 100 (PeerTube's max per request).",
                        "default": 30
                    },
                    "nsfw": {
                        "title": "Include NSFW content",
                        "type": "boolean",
                        "description": "PeerTube marks adult content with nsfw=true. Default is to include them with the flag passed through; toggle off to skip.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
