# YouTube Video Description: Chapter & Timestamp Checker (`moggedhq/youtube-video-description-chapter-checker`) Actor

Check YouTube video descriptions for missing or invalid chapter timestamps. Fetch public descriptions and durations, then flag a missing 00:00 start, out-of-order times and short chapters. About $0.01 per video; 10 videos cost about $0.10. No login needed.

- **URL**: https://apify.com/moggedhq/youtube-video-description-chapter-checker.md
- **Developed by:** [mogged](https://apify.com/moggedhq) (community)
- **Categories:** Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 video checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Video Description: Chapter & Timestamp Checker

For video editors, channel managers and agencies checking public YouTube descriptions for missing or invalid manual chapter timestamps.

About **$0.01 per complete video check**. Ten complete checks cost about **$0.10**, plus a **$0.00005 run start** at the default memory setting. The default example checks one video. Bronze, Silver and Gold discounts apply. Incomplete checks, unavailable videos, invalid inputs, duplicate videos and the run summary carry no video-check fee.

Give the Actor a public video URL or ID. It fetches the description, obtains the duration when needed, and returns a plain-English verdict before the supporting fields. You do not need a YouTube login, account cookies or a YouTube API key. An Apify token is needed only when you call the Actor through the Apify API.

A passing result means the visible manual timestamp list meets the checks described below. It does not guarantee that YouTube displays chapters: channel feature access, creator settings and automatic chapter eligibility are separate.

### What you get

| Field | Meaning |
|---|---|
| `verdict` | The answer to read first, such as a passing manual chapter list or a missing manual list. |
| `status` | Stable machine-readable outcome for filters and automations. |
| `complete` | Whether the source and checks were sufficient for a complete video check. Only complete video checks carry the unit fee. |
| `title` | Public title observed for the requested video. |
| `videoUrl`, `videoId` | Canonical watch URL and eleven-character case-sensitive video ID. |
| `manualTimestampCount` | Number of recognized timestamp entries in the description. |
| `timestamps` | Original timestamp text, computed seconds, label, description line number and component-validity flag. |
| `durationSeconds` | Public source duration, or null when it could not be read. |
| `failedRules` | Specific rule codes for the detected problems. |
| `description` | Fetched public description text used for the check, including line breaks. |
| `descriptionPresent` | Distinguishes a source-provided empty description from an absent field. |
| `descriptionSource`, `durationSource` | Source field paths explaining where the description and duration came from. |
| `observedAt` | UTC observation timestamp. |
| `sourceRoute`, `sourceAttempts` | Signed-out source route and number of attempts for this video. |
| `chapterScope` | Always `manual_description_only`. |
| `displayEligibilityChecked` | Always false; private feature eligibility is outside this check. |
| `recordType`, `resultId` | Distinguish video-check rows from free input notes and provide a stable result key within the run. |

The dataset view puts the verdict, video, timestamp count, duration and completion state together. The key-value store contains a free `SUMMARY` record and a self-contained HTML `REPORT` that groups results by video. The output links take you directly to these records.

### Start in three steps

1. Keep the prefilled example or paste a public YouTube URL into `query`. A bare eleven-character video ID also works.
2. Set an Apify maximum charge if you want a spending limit, then start the run. The prefill checks one video rather than crawling a channel.
3. Open the Chapter checks dataset view, read `verdict`, and filter incomplete results separately. Use the report for a readable review or download JSON for all timestamp entries.

To check several videos, leave `query` empty and supply `videoUrls`. When calling the API, omit unused fields. If you keep a query and also supply a list, both are included and duplicate canonical IDs are checked once.

### Input reference

| Input | Type and default | How to use it |
|---|---|---|
| `query` | Optional string; one example is prefilled in the Console | Public watch, Shorts or live URL, video ID, or explicit channel handle such as `@3blue1brown`. |
| `videoUrls` | Optional array | Public video URLs or IDs. Tracking parameters and timestamp fragments do not create new videos. |
| `channels` | Optional array | Public channel handles or channel URLs. Channel mode is used only when you explicitly provide a channel. |
| `maxVideosPerChannel` | Integer, default 5, maximum 100 | Maximum eligible uploads selected from each supplied channel. |
| `maxVideos` | Integer, default 100, maximum 1,000 | Run-wide cap on unique videos after canonical deduplication. |
| `urls` | Optional array | Alias accepted for integrations that use a general URL list. |
| `startUrls` | Optional array | Request-list alias; each item may contain a `url` property. |

An omitted input uses the one-video example. Explicit empty input lists produce a free empty summary. An invalid video ID produces an input-note row before any video fetch. URLs from lookalike domains are rejected. Video IDs are case-sensitive; the Actor does not lowercase them.

Channel mode samples the available public upload listing, excluding identified upcoming, private, members-only and live entries. It does not crawl the full channel history. An unresolved channel produces a free note.

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=aircAruvnKk",
    "https://youtu.be/rfscVS0vtbw"
  ],
  "maxVideos": 2
}
```

### What the chapter checks mean

YouTube's published manual chapter guidance calls for a list of timestamps and titles beginning at zero, at least three entries in ascending order, and chapters at least ten seconds long. This Actor checks those conditions against the public description and the observed duration. It also checks valid minute/second components and whether any timestamp reaches or exceeds the video length.

Recognized entries begin a description line with a timestamp, optionally preceded by punctuation or an emoji. Common forms include `0:00 Introduction`, `(00:00) Introduction`, `00:00 - Introduction`, and `1:02:03 Introduction`. Labels may contain non-English text. The original label and timestamp are preserved. Total minutes and multi-hour times are handled without a fixed one-hour limit.

An unsupported list layout is not treated as proof that chapters are missing. For example, a list with the title before the time or full-width timestamp punctuation can return `ambiguous_format`. Mixed formats inside a chapter block also remain incomplete. An isolated prose timestamp outside a recognized block, such as a separate correction note, is not added as a chapter. These rules reduce false missing-list and false descending-order findings, but unusual free-form descriptions can still need manual review.

| Status | Meaning and unit fee |
|---|---|
| `valid_manual_chapters` | Recognized list passes all checks, including the final segment. Complete check. |
| `invalid_manual_chapters` | Description and required duration were available; one or more rules failed. Complete check. |
| `missing_manual_chapters` | Available description contains no recognized or ambiguous timestamp list. Complete check. |
| `unknown_description` | The description field was absent. Incomplete and free. |
| `unknown_duration` | A list was found but duration needed for final validation was unavailable. Incomplete and free. |
| `ambiguous_format` | Timestamp-like content needs manual interpretation. Incomplete and free. |
| `source_unavailable`, `video_unavailable` | Source could not support a complete public check. Incomplete and free. |
| `invalid_input`, `channel_unavailable` | Input or channel note. No video-check fee. |

Failed rules include `first_timestamp_not_zero`, `fewer_than_three_chapters`, `not_strictly_ascending`, `invalid_time_component`, `missing_chapter_title`, `chapter_under_ten_seconds`, `timestamp_at_or_beyond_duration`, and `final_chapter_under_ten_seconds`. A missing duration is never replaced with a historical cached seed. An explicitly empty description can support a missing-list result; an absent description cannot.

### Pricing and spending limits

| Complete checks | Standard price including default run start | Bronze price including default run start |
|---:|---:|---:|
| 1 | $0.01005 | $0.00955 |
| 10 | $0.10005 | $0.09505 |
| 100 | $1.00005 | $0.95005 |

The unit price is $0.01 on Free, $0.0095 on Bronze, $0.009 on Silver and $0.008 on Gold and higher tiers. The default start is $0.00005; Apify can multiply its start event for larger memory allocations. Keep the default memory unless your workload needs more. The displayed run maximum is authoritative for your plan and settings.

Each complete unique video result is saved before its fee is requested. A missing manual list is a useful complete check, as is a list with specific formatting failures. Source errors and incomplete findings do not consume video-check units. Apify's small run start still applies to runs that return only free notes. The Actor stops when another complete check cannot fit under your maximum charge.

Deduplication applies within one run, including equivalent watch, short-link, live and Shorts URLs. A new run is a fresh observation and may charge again. Restart recovery within the same run restores saved source evidence, final rows and acknowledged charges so completed work is not emitted or charged again.

### Use with the API

Use your own Apify token in an environment variable. Do not paste credentials into Actor input. The following request starts the same one-video workflow used by the prefill:

```bash
curl --request POST \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"query":"https://www.youtube.com/watch?v=aircAruvnKk"}' \
  'https://api.apify.com/v2/acts/moggedhq~youtube-video-description-chapter-checker/run-sync-get-dataset-items?format=json'
```

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("moggedhq/youtube-video-description-chapter-checker").call(
    run_input={"videoUrls": ["aircAruvnKk", "rfscVS0vtbw"], "maxVideos": 2}
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["verdict"], row.get("videoUrl"))
```

### Use with AI agents (MCP)

Connect your agent to `https://mcp.apify.com`. Describe the actual job: fetch public YouTube video descriptions and check their manual chapter timestamps against duration. Supply the video URLs and a maximum charge. Ask the agent to preserve incomplete results and report failed rule codes rather than silently dropping them.

Ask: “Check these public YouTube video descriptions for missing or invalid chapter timestamps. Return the verdict, timestamp labels, duration and failed rules for each video. Keep incomplete checks separate.” No separate model key is required.

### Limits and source observations

Public signed-out access varies by video and source conditions. Requests are serial and paced, with fresh datacenter retries and a bounded mobile residential fallback. There is no login, cookie import, CAPTCHA solving or paid upstream data API. Videos requiring private account access cannot be checked by this workflow.

This is a snapshot, not a permanent guarantee. Descriptions can change after observation. Automatic chapters, player-display confirmation, copyright restrictions, monetization, strikes and channel feature access are outside scope. Live broadcasts may lack a final duration.

### Tested output

A private cloud validation on 15 October 2026 returned **32 complete checks from 32 distinct public videos** in **189 seconds**. The set included all six chapter-positive discovery examples, Shorts, non-English descriptions, explicit empty descriptions and long courses. Ten returned rows were checked against the fetched text and source duration. This small test set is evidence of that run, not a promise that every future video will be available.

One real result from that run:

```json
{"verdict":"Chapters OK on 'Learn Python - Full Course for Beginners [Tutorial]': 35 chapters from 0:00, in order, each at least 10 seconds.","videoId":"rfscVS0vtbw","manualTimestampCount":35,"durationSeconds":16012,"complete":true,"sourceRoute":"desktop_datacenter"}
```

### FAQ

**Does a valid result guarantee chapters appear on YouTube?** No. It confirms the visible formatting and timing rules tested here. Settings and eligibility can still prevent display.

**Can a video without manual chapters have automatic chapters?** Yes. The missing-list verdict concerns the description only and does not assert that the player has no chapters.

**Why is an invalid list charged?** It is a complete check with specific problems you can correct. An unresolved source or ambiguous list is free.

**Why is an empty description different from a missing field?** An explicit empty source value supports a missing-list conclusion. An absent field supplies no evidence about the description, so the check remains incomplete.

**Can I supply a description directly?** No. This Actor fetches the public video and the duration required for validation. It is not a supplied-text calculator.

**Are repeated URLs charged repeatedly?** Equivalent IDs in one run are deduplicated. Running a new job checks a fresh snapshot and is a new billable observation when complete.

**Can I check Shorts or long courses?** Yes, using their public URL or ID. Short videos often have no manual list. The same stated timestamp rules are applied without inventing chapters.

**What should I do with an incomplete result?** Inspect the status and source fields. Try a later run for a transient source refusal, or review the description manually for an unsupported layout.

**What are the authoritative chapter rules?** See [YouTube's Video Chapters help](https://support.google.com/youtube/answer/9884579?hl=en). Use this tool only where your collection and use of public data are permitted.

# Actor input Schema

## `query` (type: `string`):

Public video URL, eleven-character video ID or @channel handle.

## `videoUrls` (type: `array`):

Public watch, live or Shorts URLs or video IDs.

## `channels` (type: `array`):

Public @handles or channel URLs. Checks up to maxVideosPerChannel eligible uploads. Example: @3blue1brown.

## `maxVideosPerChannel` (type: `integer`):

Only used for explicitly supplied channels. Upcoming, private and members-only entries are excluded.

## `maxVideos` (type: `integer`):

Run-wide limit after canonical deduplication. Default 100. Start with one video to check the result.

## `urls` (type: `array`):

Alternative to videoUrls. Public video URLs or IDs.

## `startUrls` (type: `array`):

Optional URL list for API integrations.

## Actor input object example

```json
{
  "query": "https://www.youtube.com/watch?v=aircAruvnKk",
  "maxVideosPerChannel": 5,
  "maxVideos": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `report` (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 = {
    "query": "https://www.youtube.com/watch?v=aircAruvnKk"
};

// Run the Actor and wait for it to finish
const run = await client.actor("moggedhq/youtube-video-description-chapter-checker").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 = { "query": "https://www.youtube.com/watch?v=aircAruvnKk" }

# Run the Actor and wait for it to finish
run = client.actor("moggedhq/youtube-video-description-chapter-checker").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "query": "https://www.youtube.com/watch?v=aircAruvnKk"
}' |
apify call moggedhq/youtube-video-description-chapter-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,moggedhq/youtube-video-description-chapter-checker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3LXeUIjDfBleydWZ6/builds/jqziZ2DhjxuXOQhke/openapi.json
