G-gen の高宮です。Google Calendar API と Google Meet REST API を組み合わせた実装時に、いくつかのエラーが発生しました。その内容と原因の考察を紹介します。

はじめに
以前の記事「API経由でGoogle Meetの成果物作成を有効化したカレンダー予定を作成する」では、Google Calendar API と Google Meet REST API を利用し、Google Meet の成果物作成を有効化したカレンダーの予定を作成する方法を紹介しました。
Google Meet の成果物(アーティファクト)作成を有効化したカレンダー予定を API で作成するために、以下の3つのアプローチを検証しました。
- Calendar API でイベントを作成する。次に、Meet REST API(
google-apps-meetライブラリ経由)でアーティファクト生成を有効化したスペースを作成する。次に、イベントとスペースを紐づける。 - Calendar API でイベントとスペースを作成する。次に、Meet REST API(
google-api-python-clientライブラリ経由)でスペースのアーティファクト生成を有効化する。 - Calendar API でイベントを作成する。次に、Meet REST API(
google-api-python-clientライブラリ経由)でアーティファクト生成を有効化したスペースを作成する。次に、イベントとスペースを紐づける。
それぞれのアプローチの実装方法の違いと動作結果は、以下の表の通りです。
| No. | 使用ライブラリ(Meet) | スペースの作成 | アーティファクト設定 | イベントとの紐づけ | 動作結果 |
|---|---|---|---|---|---|
| 1 | google-apps-meet ライブラリ |
Meet REST API | スペース作成時に指定 | Calendar API で作成したイベントを更新し、紐づけ | 会議スペース作成時にエラー |
| 2 | google-api-python-client ライブラリ |
Calendar API | Meet REST API で作成されたスペースの設定を更新 | Calendar API でイベント作成時に自動で紐づけ | Meet 会議の設定編集時にエラー |
| 3 | google-api-python-client ライブラリ |
Meet REST API | スペース作成時に指定 | Calendar API で作成したイベントを更新し、紐づけ | 期待した動作(以前の記事) |
当記事では、上記のうちエラーが発生したケース1とケース2について、エラーの詳細、原因、および回避策を解説します。
会議スペース作成時にエラー
実装
以下のコマンドを実行して、google-apps-meet ライブラリを追加でインストールします。
uv add google-apps-meet==0.2.0
2025年11月時点でベータ版で公開されている機能を使用して、main.py に以下のコードを実装します。
import os import datetime from google.auth.transport import requests from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.apps.meet_v2beta import SpacesServiceClient from google.apps.meet_v2beta.types import Space, SpaceConfig, CreateSpaceRequest from googleapiclient.discovery import build def authorize() -> Credentials: """Calendar API と Meet API を呼び出すために OAuth 2.0 認証を行い、Credentials オブジェクトを返す""" CLIENT_SECRET_FILE = "./client_secret.json" credentials = None if os.path.exists("token.json"): credentials = Credentials.from_authorized_user_file("token.json") if credentials is None: flow = InstalledAppFlow.from_client_secrets_file( CLIENT_SECRET_FILE, scopes=[ "https://www.googleapis.com/auth/calendar.events.owned", "https://www.googleapis.com/auth/meetings.space.created", ], ) flow.run_local_server(port=0) credentials = flow.credentials if credentials and credentials.expired: credentials.refresh(requests.Request()) if credentials is not None: with open("token.json", "w") as f: f.write(credentials.to_json()) return credentials USER_CREDENTIALS = authorize() def create_event(): """Calendar API を使用してイベントを作成する""" # サービスオブジェクトの構築 service = build("calendar", "v3", credentials=USER_CREDENTIALS) # 指定した時刻から1時間の予定を作成 now = datetime.datetime.now() startTime = now.isoformat() endTime = (now + datetime.timedelta(hours=1)).isoformat() event = { "summary": "Google Calendar Meet Test Event", "start": { "dateTime": startTime, "timeZone": "Asia/Tokyo", }, "end": { "dateTime": endTime, "timeZone": "Asia/Tokyo", }, } # 自身のカレンダーに予定を追加 event = service.events().insert(calendarId="primary", body=event).execute() return event def create_space() -> Space: """Meet API を使用して アーティファクト生成を有効化した Space リソースを作成する""" # クライアントを作成し、 Space を作成 client = SpacesServiceClient(credentials=USER_CREDENTIALS) request = CreateSpaceRequest() space = client.create_space(request=request) # artifact の自動生成を有効化 space.config = SpaceConfig( artifactConfig=SpaceConfig.ArtifactConfig( recordingConfig=SpaceConfig.ArtifactConfig.RecordingConfig( autoRecordingGeneration=SpaceConfig.ArtifactConfig.RecordingConfig.AutoRecordingGeneration.ON ), transcriptionConfig=SpaceConfig.ArtifactConfig.TranscriptionConfig( autoTranscriptionGeneration=SpaceConfig.ArtifactConfig.TranscriptionConfig.AutoTranscriptionGeneration.ON, ), smartNotesConfig=SpaceConfig.ArtifactConfig.SmartNotesConfig( autoSmartNotesGeneration=SpaceConfig.ArtifactConfig.SmartNotesConfig.AutoSmartNotesGeneration.ON, ), ), ) return space def update_event(eventId: str = None, meetUri: str = None): """Calendar API を使用して既存のイベントに Meet 情報を追加する""" # サービスオブジェクトの構築 service = build("calendar", "v3", credentials=USER_CREDENTIALS) # 予定の更新 event = service.events().get(calendarId="primary", eventId=eventId).execute() event["conferenceData"] = { "conferenceSolution": { "key": {"type": "hangoutsMeet"}, }, "entryPoints": [ { "entryPointType": "video", "uri": meetUri, } ], } updated_event = ( service.events() .update( calendarId="primary", eventId=event["id"], body=event, conferenceDataVersion=1, ) .execute() ) return updated_event def main(): """メイン処理""" # イベントと Space の作成 event = create_event() print(f"Google Calendar URL {event.get('htmlLink')}") space = create_space() if __name__ == "__main__": main()
以下の関数は以前の記事と同様です。
authorizecreate_eventupdate_event
以前の記事と異なるのは、create_space 関数における以下の処理の実装です。
- 認証情報を使用して、
SpacesServiceClientクラスのインスタンスを作成します。 CreateSpaceRequestクラスのインスタンスを引数に設定し、create_spaceメソッドを呼び出しスペースを作成するリクエストを送信します。- 処理
2.で作成したスペースに対して、以下のクラスを使用して Google Meet の成果物作成を有効化します。
| クラス名 | 説明 |
|---|---|
| SpaceConfig | 会議スペース全般の構成 |
| ArtifactConfig | 会議でサポートされている自動生成アーティファクトに関する構成 |
| RecordingConfig | 録画の構成 |
| TranscriptionConfig | 自動文字起こしの構成 |
| SmartNotesConfig | 自動スマートメモの構成 |
エラー
処理 2. の実行時(80行目)に、以下の例外が発生します。
例外が発生しました: MethodNotImplemented
501 Method not found.
grpc.channel.InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.UNIMPLEMENTED
details = "Method not found."
debug_error_string = "UNKNOWN:Error received from peer ipv4:{IPアドレス}:443 {grpc_message:"Method not found.", grpc_status:12}"
原因
エラーメッセージに、gRPC の標準的なステータスコードである StatusCode.UNIMPLEMENTED(エラーコード 12)が返却されており、「リクエストされた操作が実装されていない、または API によってサポートされていない」ことがわかります。
ベータ版の機能であるため、サーバー側で当該メソッドがまだ実装されていない、あるいは公開されていないことが原因と推測され、本番環境での使用には十分な注意が必要です。
回避策
処理 2. を google-api-python-client ライブラリ経由で Meet REST API を呼び出す実装に変更することで期待した動作になります。
Meet 会議の設定編集時にエラー
実装
main.py に以下のコードを実装します。本実装では、API の呼び出しは、google-api-python-client ライブラリを使用して行っています。
import os import datetime import uuid from google.auth.transport import requests from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build def authorize() -> Credentials: """Calendar API と Meet API を呼び出すために OAuth 2.0 認証を行い、Credentials オブジェクトを返す""" CLIENT_SECRET_FILE = "./client_secret.json" credentials = None if os.path.exists("token.json"): credentials = Credentials.from_authorized_user_file("token.json") if credentials is None: flow = InstalledAppFlow.from_client_secrets_file( CLIENT_SECRET_FILE, scopes=[ "https://www.googleapis.com/auth/calendar.events.owned", "https://www.googleapis.com/auth/meetings.space.created", "https://www.googleapis.com/auth/meetings.space.settings", ], ) flow.run_local_server(port=0) credentials = flow.credentials if credentials and credentials.expired: credentials.refresh(requests.Request()) if credentials is not None: with open("token.json", "w") as f: f.write(credentials.to_json()) return credentials USER_CREDENTIALS = authorize() def create_event(): """Calendar API を使用してイベントを作成する""" # サービスオブジェクトの構築 service = build("calendar", "v3", credentials=USER_CREDENTIALS) # 指定した時刻から1時間の予定を作成 now = datetime.datetime.now() startTime = now.isoformat() endTime = (now + datetime.timedelta(hours=1)).isoformat() event = { "summary": "Google Calendar Meet Test Event", "start": { "dateTime": startTime, "timeZone": "Asia/Tokyo", }, "end": { "dateTime": endTime, "timeZone": "Asia/Tokyo", }, "conferenceData": { "createRequest": { "requestId": str(uuid.uuid4()), "conferenceSolutionKey": {"type": "hangoutsMeet"}, } }, } # 自身のカレンダーに予定を追加 event = service.events().insert(calendarId="primary", body=event, conferenceDataVersion=1).execute() return event def update_space(meetUri: str = None): """Meet API を使用して 既存の Meet 会議をアーティファクト生成を有効化した Space リソースに更新する""" # google-api-python-client での実装 service = build("meet", "v2", credentials=USER_CREDENTIALS) # meetUri から Space 名を抽出 name = f"spaces/{meetUri.split('/')[-1]}" # 変更する Space リソースのボディ定義 space_body = { "config": { "artifactConfig": { "recordingConfig": { "autoRecordingGeneration": "ON", }, "transcriptionConfig": { "autoTranscriptionGeneration": "ON", }, "smartNotesConfig": { "autoSmartNotesGeneration": "ON", }, } } } # 更新する対象の定義 update_mask = "config.artifactConfig.recordingConfig,config.artifactConfig.transcriptionConfig,config.artifactConfig.smartNotesConfig" # Space リソースの更新 return service.spaces().patch(name=name, body=space_body, updateMask=update_mask).execute() def main(): """メイン処理""" # Meet 会議 を含んだイベントの作成 event = create_event() print(f"Google Calendar URL {event.get('htmlLink')}") meet_uri = event["conferenceData"]["entryPoints"][0]["uri"] print(f"Meet URL {meet_uri}") # Space の設定を更新 update_space(meetUri=meet_uri) if __name__ == "__main__": main()
以前の記事との相違点は以下です。
authorize関数のscopesで、https://www.googleapis.com/auth/meetings.space.settingsを指定しています。このスコープを指定することで、Google Meet 通話すべての設定の表示および編集が可能です。create_event関数でconferenceDataフィールドを使用して、Meet 会議の設定を追加します。insertメソッド呼び出し時の引数として、conferenceDataVersion=1を追加し、Meet 会議を含むイベントを作成します。
以前の記事と異なるのは、update_space 関数における以下の実装です。
- イベントに紐づく Meet 会議の URI を引数に取得し、
https://meet.google.com/{meetingCode}をspaces/{meetingCode}の形式に変換します。 - Google Meet の成果物作成を有効化する設定を JSON 形式で定義します。
- 更新するフィールド情報を文字列で定義します。JSON のネストはドット(
.)区切り、複数のフィールドを指定したい場合は、カンマ(,)区切りで表現します。 - 処理
1.、2.、3.で定義した変数を引数にpatchメソッドを実行することで、Meet 会議の設定を更新します。
エラー
処理 3. の実行時(110行目)に、以下の例外が発生します。
例外が発生しました: HttpError
<HttpError 403 when requesting https://meet.googleapis.com/v2/spaces/{meetingCode}?updateMask=config.artifactConfig.recordingConfig%2Cconfig.artifactConfig.transcriptionConfig%2Cconfig.artifactConfig.smartNotesConfig&alt=json returned "Permission denied on resource Space (or it might not exist)". Details: "Permission denied on resource Space (or it might not exist)">
原因
エラーメッセージに、HTTP のレスポンスステータスコードの 403 が返却されており、「リクエストした操作を行う権限がない」ことがわかります。
Meet REST API の仕様として、自身で作成したスペースに対する更新のみを許容しており、カレンダーなどの外部の操作で作成されたスペースを更新できないことが原因と推測されます。
- 参考 : REST API スコープについて
- 参考 : Google Meet API . spaces
回避策
以下の実装に変更することで、期待した動作になります。
create_event関数 では、イベント作成時に Meet 会議が作成されないように設定します。google-api-python-clientライブラリ経由で Meet REST API を呼び出し、アーティファクト生成が有効化されたスペースを作成します。- Calendar API を使用して、作成したイベントとスペースを紐づけします。
高宮 怜(記事一覧)
クラウドソリューション部クラウドエクスプローラ課
2025年6月より、G-genにジョイン。前職は四国のSIerで電力、製造業系のお客様に対して、PM/APエンジニアとして、要件定義から運用保守まで全工程を担当。現在はGoogle Cloudを学びながら、フルスタックエンジニアを目指してクラウドエンジニアとしてのスキルを習得中。
Follow @Ggen_RTakamiya