Youtube REST API Client

yt - Native Dart Client for YouTube APIs

Native Dart interface to multiple Google REST APIs, including:

Related Packages:

  • yt_cli — CLI tool for YouTube APIs

pub package License: MIT github build github issues

Quick Start

import 'package:yt/yt.dart';

void main() async {
  // Use an API key for read-only access
  final yt = Yt.withApiKey(apiKey: '[your youtube api key]');

  // Search for videos
  final results = await yt.search.list(q: 'flutter tutorial', maxResults: 5);
  for (final item in results.items) {
    print('${item.snippet?.title}');
  }
}

For write operations (uploading videos, managing broadcasts), use OAuth — see Configuration below.

Features

  • YouTube Data API — Channels, Playlists, Videos, Search, Comments, Subscriptions, Thumbnails, Captions, and more
  • YouTube Live Streaming API — LiveBroadcasts, LiveStreams, and LiveChat
  • YouTube Analytics API — Reports, Groups, and Group Items for channel analytics
  • Members & Memberships — Channel members, membership levels, and video abuse report reasons
  • Activities — Channel activity feeds including uploads, likes, favorites, subscriptions, and playlist additions
  • Multiple auth methods — API key, OAuth 2.0 with automatic token refresh, or custom token generators
  • Cross-platform — works on all Dart/Flutter platforms including web, mobile, and desktop
  • Dart-first — manually crafted (not auto-generated) for a focused, well-documented API surface

Getting Started

Add to your pubspec.yaml:

dependencies:
  yt: ^3.1.0

Configuration

⚠️ Breaking Changes in v3.1.0

Chat.send and EmojiFormatter removed. These convenience helpers were not part of any official YouTube API surface. Use Chat.insert directly with a liveChatMessages.insert request body:

await yt.chat.insert(body: {
  'snippet': {
    'type': 'textMessageEvent',
    'liveChatId': '<live chat id>',
    'textMessageDetails': {'messageText': 'Hello!'},
  },
});

⚠️ Breaking Changes in v3.0.1

1. OAuth library replaced: googleapis_authoauth2

All public OAuth APIs that previously used googleapis_auth types now use package:oauth2 types directly:

Before (googleapis_auth) After (oauth2)
Yt.withOAuth({ClientId? oAuthClientId}) Yt.withOAuth({oauth2.Client? oauthClient})
GoogleOAuthCredentials.toClientId() → ClientId GoogleOAuthCredentials.toAuthorizationCodeGrant() → oauth2.AuthorizationCodeGrant
OAuthCredentials.oAuthClientId (getter) OAuthCredentials.toAuthorizationCodeGrant()
OAuthAccessControl(ClientId?, AccessCredentials?) OAuthAccessControl([oauth2.Client?])

2. Token file format changed

Access tokens are now serialized via oauth2.Credentials.toJson() instead of googleapis_auth.AccessCredentials.toJson(). Existing token files written by yt < 3.0.0 (or yt_cli < 3.0.0) are no longer readable — delete the existing tokens file and re-run yt authorize.

3. OAuth credential filenames have changed

File Old Name New Name
Client secrets credentials.json client_secrets.json
Access tokens access_credentials.json access_tokens.json

Migration options:

  1. Rename your files on disk to match the new names in $HOME/.yt/
  2. Set environment variables to point at the old locations:
    export YT_CLIENT_SECRETS_FILE="$HOME/.yt/credentials.json"
    export YT_ACCESS_TOKENS_FILE="$HOME/.yt/access_credentials.json"
    

For full details, see the CHANGELOG.

YouTube API access requires either an API key (read-only) or OAuth 2.0 credentials.

Action Type Authentication Requirement Why
Reading Public Data API Key or OAuth 2.0 Accesses data anyone can see (e.g., public video titles, search results).
Reading Private Data OAuth 2.0 Required Accesses data specific to a user (e.g., a user's private videos or watch history).
Writing/Modifying Data OAuth 2.0 Required Performs actions on behalf of a user (e.g., uploading, deleting, or commenting).

API Key

Create an API key in the Google API Console, then:

final yt = Yt.withApiKey(apiKey: '[your api key]');

OAuth 2.0

OAuth 2.0 uses two files:

File Purpose Source
client_secrets.json OAuth client secret (input) Downloaded from Google Cloud Console
access_tokens.json Persisted access & refresh tokens (output) Created by the authorization flow

1. Create OAuth credentials in the Google Cloud Console, download the client-secret JSON, and save it to $HOME/.yt/client_secrets.json.

2. Run the authorization flow using the yt_cli tool. Pass --credentials-file to point at the client-secret JSON you saved in step 1, and --tokens-file to write the resulting tokens to the location the yt library expects:

Via Homebrew (macOS/Linux — fewer dependencies):

brew tap cdavis-code/yt
brew install yt
yt authorize \
  --credentials-file $HOME/.yt/client_secrets.json \
  --tokens-file $HOME/.yt/access_tokens.json

Via Dart pub (requires Dart SDK):

dart pub global activate yt_cli
yt authorize \
  --credentials-file $HOME/.yt/client_secrets.json \
  --tokens-file $HOME/.yt/access_tokens.json

This opens a browser, prompts for consent, and writes the tokens to $HOME/.yt/access_tokens.json.

Why both flags? yt_cli's default filenames (client_secret.json in the current directory for input, youtube_server_tokens.json for output) differ from the yt library's defaults (client_secrets.json and access_tokens.json in $HOME/.yt/). Passing both flags writes everything directly to the locations Yt.withOAuth() reads — no manual rename or copy needed. Alternatively, set the YT_CLIENT_SECRETS_FILE and YT_ACCESS_TOKENS_FILE environment variables (see below).

3. Use the credentials in your code:

final yt = await Yt.withOAuth();  // Uses default file locations

Customizing file locations

Both files can be redirected to arbitrary paths via environment variables (resolved from the runtime environment first, then a .env file in the current working directory):

Variable Default
YT_CLIENT_SECRETS_FILE $HOME/.yt/client_secrets.json
YT_ACCESS_TOKENS_FILE $HOME/.yt/access_tokens.json

Either variable may be set independently — unset variables keep the default location. Leading ~ in the resolved path is expanded against the user's home directory.

Usage

Data API

import 'package:yt/yt.dart';

final yt = Yt.withOAuth();

var playlistResponse = await yt.playlists.list(
    channelId: '[youtube channel id]', maxResults: 25);

playlistResponse.items
    .forEach((playlist) => print('${playlist.snippet?.title}'));

Upload a Video

final yt = await Yt.withOAuth();

final body = <String, dynamic>{
  'snippet': {
    'title': 'TEST title',
    'description': 'Test Description',
    'tags': ['tag1', 'tag2'],
    'categoryId': '22'
  },
  'status': {
    'privacyStatus': 'private',
    'embeddable': true,
    'license': 'youtube'
  }
};

final videoItem = await yt.videos.insert(
    body: body,
    videoFile: File('[path to a video to upload]'),
    notifySubscribers: false);

print(videoItem);

Live Streaming API

import 'package:yt/yt.dart';

final yt = await Yt.withOAuth();
final br = yt.broadcast;
final th = yt.thumbnails;

// Create a broadcast
final broadcastItem = await br.insert(body: {
  'snippet': {
    'title': 'TEST Broadcast',
    'description': 'Test',
    'scheduledStartTime':
        DateTime.now().add(Duration(hours: 2)).toUtc().toIso8601String()
  },
  'status': {'privacyStatus': 'private'},
  'contentDetails': {
    'monitorStream': {
      'enableMonitorStream': false,
      'broadcastStreamDelayMs': 10
    },
    'enableDvr': true,
    'enableContentEncryption': true,
    'enableEmbed': true,
    'recordFromStart': true,
    'startWithSlate': false
  }
}, part: 'snippet,status,contentDetails');

// Bind to a stream and upload thumbnail
await br.bind(
    broadcastId: broadcastItem.id,
    streamId: '[one of your valid stream ids]');

await th.set(
    videoId: broadcastItem.id,
    thumbnail: File('[path to an image to upload]'));

Download a LiveChat

final yt = await Yt.withOAuth();

var broadcastResponse = await yt.broadcast.list(broadcastStatus: 'active');

if (broadcastResponse.items.isNotEmpty) {
  await yt.chat.downloadHistory(
      liveBroadcastItem: broadcastResponse.items.first);
}

Analytics

final yt = await Yt.withOAuth();

// Query channel analytics
final report = await yt.analytics.query(
  ids: 'channel==MINE',
  startDate: '2026-01-01',
  endDate: '2026-01-31',
  metrics: 'views,estimatedMinutesWatched',
  dimensions: 'day',
);

for (final header in report.columnHeaders) {
  print('${header.name} (${header.dataType})');
}

// List analytics groups
final groups = await yt.analytics.groupsList(mine: true);
for (final group in groups.items) {
  print('${group.id}: ${group.snippet.title}');
}

Activities

final yt = Yt.withApiKey(apiKey: '[your api key]');

// List recent channel activities
final activities = await yt.activities.list(
  channelId: 'UC_x5XG1OV2P6uZZ5FSM9Ttw',
  maxResults: 10,
);

for (final activity in activities.items) {
  print('${activity.snippet?.type}: ${activity.snippet?.title}');
}
final yt = await Yt.withOAuth();

// List my recent activities
final myActivities = await yt.activities.list(
  mine: true,
  maxResults: 20,
);

for (final activity in myActivities.items) {
  print('${activity.snippet?.publishedAt}: ${activity.snippet?.title}');
}

Flutter Integration

This package has no Flutter dependencies and works on all platforms. To authenticate with a user's own YouTube account, implement a TokenGenerator:

import 'package:google_sign_in/google_sign_in.dart';
import 'package:yt/yt.dart';

class YtLoginGenerator implements TokenGenerator {
  final GoogleSignIn _googleSignIn = GoogleSignIn(
    scopes: ['https://www.googleapis.com/auth/youtube'],
  );

  @override
  Future<Token> generate() async {
    var _currentUser = await _googleSignIn.signInSilently();
    if (_currentUser == null) _currentUser = await _googleSignIn.signIn();
    final token = (await _currentUser!.authentication).accessToken;
    if (token == null) throw Exception();
    return Token(
        accessToken: token, expiresIn: 3599, scope: null, tokenType: '');
  }
}

Then in your Flutter app:

late final Yt yt;

void _init() async {
  yt = await Yt.withGenerator(YtLoginGenerator());
}

void _getPlaylists() async {
  setState(() {
    items.addAll(await yt.playlists.list(mine: true));
  });
}

See Usage within Flutter for google_sign_in setup requirements.

Documentation

Contributing

Any help from the open-source community is always welcome and needed:

  • Found an issue? Please fill a bug report with details.
  • Need a feature? Open a feature request with use cases.
  • Are you a developer? Fix a bug, implement a new feature, or improve tests — send a pull request.
  • Are you using and liking the project? Promote it, or let me know and I'll cross-link your project.

If you donate 1 hour of your time, you can contribute a lot, because others will do the same — just be part and start with your 1 hour.

License

MIT — see the LICENSE file for details.

Libraries

meta
app metadata
oauth
oauth related classes for io versus browser usage
yt
The YouTube Live Streaming API reference explains how to schedule live broadcasts and video streams on YouTube using the YouTube Live Streaming API.