↑ To all notes Dmitry on January 9, 2024

Generate Google Meet link from shell

I wanted a shell command that:

  • creates a meeting
  • prints out its link
  • copies it to clipboard
  • opens it in the browser for correct account

It takes ~10 minutes to set up and is based on Google Meet API guide.


0. Prepare project directory

~ 1 minute

Create an empty NodeJS project in a directory of your choice (i put mine in ~/.config/scripts/):

mkdir -p ~/.config/scripts/auto-meet
cd ~/.config/scripts/auto-meet
npm init -y
npm i @google-apps/meet @google-cloud/local-auth

1. Create a GCP project

~ 1 minute

Go to Google Cloud Console and create a new project. Use your personal account as this project might be handy for other utils in the future.

2. Enable Google Meet API

< 1 minute

When project is created, go to Enabled APIs and Services and enable Google Meet REST API.

~ 5 minutes

When API is enabled, you’ll see invitation to create credentials, which will take you to Wizard.

In this wizard:

  • What data will you be accessing? User data
  • Scopes > Add or remove scopes > Search for mettings.space.created > Add
  • Application type > Desktop app
  • Name > Whatever you want

After that you’ll be presented with a button to download credentials. Save them as credentials.json in project dir.

Now go to OAuth consent screen and fill in the required fields. On 3rd step add all your relevant emails to the test users.

4. Create a script

~ 1 minute

Put following code into index.js in your project directory.
Based on Google’s own example script. All i’m really changing is what is being printed out in the end (clean meeting url).

// index.js
const fs = require("fs").promises;
const path = require("path");
const process = require("process");
const { authenticate } = require("@google-cloud/local-auth");
const { SpacesServiceClient } = require("@google-apps/meet").v2;
const { auth } = require("google-auth-library");

const SCOPES = ["https://www.googleapis.com/auth/meetings.space.created"];
const TOKEN_PATH = path.join(process.cwd(), "token.json");
const CREDENTIALS_PATH = path.join(process.cwd(), "credentials.json");

async function loadSavedCredentialsIfExist() {
  const content = await fs.readFile(TOKEN_PATH);
  const credentials = JSON.parse(content);
  return auth.fromJSON(credentials);

async function saveCredentials(client) {
  const content = await fs.readFile(CREDENTIALS_PATH);
  const keys = JSON.parse(content);
  const key = keys.installed || keys.web;
  const payload = JSON.stringify({
    type: "authorized_user",
    client_id: key.client_id,
    client_secret: key.client_secret,
    refresh_token: client.credentials.refresh_token,
  await fs.writeFile(TOKEN_PATH, payload);

async function authorize() {
  let client = await loadSavedCredentialsIfExist();
  if (client) return client;
  client = await authenticate({
    scopes: SCOPES,
    keyfilePath: CREDENTIALS_PATH,
  if (client.credentials) {
    await saveCredentials(client);
  return client;

async function createSpace(authClient) {
  const meetClient = new SpacesServiceClient({ authClient });
  const response = await meetClient.createSpace({});


5. Create a shell function

~ 1 minute

Add this to your .bashrc or .zshrc.
Make sure to replace ~/.config/scripts/auto-meet with your project directory!

function meet() {
  url=$(node ~/.config/scripts/auto-meet/index.ts)
  if [ -z "$url" ]; then
    echo "Failed to get the meeting url"
    return 1
  echo $url
  echo $url | pbcopy
  open $url
  return 0

Usage and remarks

First run

Source your shell config or just restart it. First time you run meet, you’ll be prompted in your browser to authenticate with your Google account. After that, the token will be saved in token.json and you won’t be asked again. If token expires or gets revoked, you’ll have to delete token.json and re-run the script.

Multiple Google accounts and ?authuser=

Let me rant quickly that ?authuser= is a mistake and i’d rather have to log in/out of console sometimes but be able to click links without switching to correct account afterwards. Eh.

If you have multiple Google accounts active on your browser, you can modify the function to something like this:

  echo $url
  echo $url | pbcopy
  open "$url?authuser=1"
  return 0

Or parametrize the authuser value to call it like meet 1 or meet 2:

open "$url?authuser=$1"

In that case, browser will open with correct ?authuser= parameter. But the copied link will be account-agnostic. Beware that the owner of the call is whoever produced the token, i.e. the account you authenticated with on the OAuth screen.

Dmitry Moto

Software engineer.
I write about web / game development, design, urbanism, philosophy and other things i love.