Youtube REST API Client
Native Dart interface to multiple Google REST APIs, including:
Related Packages:
- yt_cli — CLI tool for YouTube APIs
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_auth → oauth2
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:
- Rename your files on disk to match the new names in
$HOME/.yt/ - 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.jsonin the current directory for input,youtube_server_tokens.jsonfor output) differ from theytlibrary's defaults (client_secrets.jsonandaccess_tokens.jsonin$HOME/.yt/). Passing both flags writes everything directly to the locationsYt.withOAuth()reads — no manual rename or copy needed. Alternatively, set theYT_CLIENT_SECRETS_FILEandYT_ACCESS_TOKENS_FILEenvironment 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
- API Reference — Full Dart API docs
- CHANGELOG — Release history and breaking changes
- YouTube Data API — Google's Data API docs
- YouTube Live Streaming API — Google's Live Streaming API docs
- YouTube Analytics API — Google's Analytics API docs
- Example — Command-line example
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.