Back to Blog
TutorialMar 2026

How to Post to TikTok from Google Sheets: Automation Guide

Key Takeaways:

  • Transform Google Sheets into a TikTok content calendar with automated posting
  • Choose from 3 methods: n8n (no-code), Google Apps Script, or Python
  • Handle timezone conversions and approval workflows automatically
  • Scale from single posts to bulk scheduling with error handling
  • Integrate with Google Drive for video storage and team collaboration

Google Sheets is the perfect place to manage your TikTok content calendar. You can plan posts, track status, and collaborate with your team in a familiar interface. This guide shows you how to connect google sheets tiktok workflows that automatically post your scheduled content using TikTok API integrations.

You will learn multiple approaches for sheets to tiktok automation. From simple no-code tools to advanced scripting, you will find a method that fits your technical comfort level.

Related: For no-code automation using visual workflows, see our n8n TikTok guide. For programmatic control, check our Python TikTok scheduler.

What You Will Build

Complete Google Sheets to TikTok automation:

  • Content calendar that posts automatically
  • Approval workflows with status tracking
  • Bulk upload from spreadsheet rows
  • Scheduled posting with timezone support
  • Error handling and retry logic

Prerequisites

Before connecting sheets to tiktok, gather these items:

  • Google account with Sheets access
  • Postqued account with API key
  • TikTok account connected to Postqued
  • Sample video files stored in Google Drive or accessible URLs
  • Basic understanding of spreadsheet formulas (helpful but not required)

Setting Up Your Content Calendar Sheet

Create a Google Sheet with this structure:

ColumnHeaderDescription
ADateWhen to post (YYYY-MM-DD format)
BTimePost time (HH:MM format, 24-hour)
CTimezoneTimezone name (e.g., America/New_York)
DVideo URLLink to video file (Google Drive, Dropbox, or direct)
ECaptionPost caption with hashtags
FStatusReady, Scheduled, Posted, Failed
GPublish IDAuto-filled after posting
HPosted AtAuto-filled timestamp

Add data validation to the Status column with these options: Ready, Scheduled, Posted, Failed.

Method 1: n8n No-Code Automation

The easiest way to automate google sheets tiktok posting uses n8n visual workflows.

Step 1: Set Up n8n

Sign up for n8n Cloud or run self-hosted. The free cloud tier works for testing.

Create a new workflow named "TikTok Sheets Poster".

Step 2: Add Google Sheets Trigger

  1. Add "Google Sheets" trigger node
  2. Operation: Row Added or Modified
  3. Spreadsheet: Select your content calendar
  4. Sheet: Sheet1
  5. Filter: Status column equals "Ready"

This triggers whenever you mark a row as Ready.

Step 3: Add Postqued Credentials

  1. Click the key icon, create new credential
  2. Choose "Header Auth"
  3. Name: Postqued API
  4. Header Name: Authorization
  5. Value: Bearer pq_your_api_key_here

Step 4: Upload Video

Add HTTP Request node:

  • Method: POST
  • URL: https://api.postqued.com/v1/content/upload
  • Authentication: Postqued API
  • Body (JSON):
{
  "filename": "={{ $json.videoUrl.split('/').pop() }}",
  "contentType": "video/mp4",
  "fileSize": 5242880
}

Note: You may need to fetch the file first to determine actual size.

Step 5: Fetch Video File

If your video URL is external:

  • Add HTTP Request node before upload
  • Method: GET
  • URL: ={{ $json.videoUrl }}
  • Response Format: File

Step 6: Publish to TikTok

Add another HTTP Request node:

  • Method: POST
  • URL: https://api.postqued.com/v1/content/publish
  • Authentication: Postqued API
  • Headers: Idempotency-Key: ={{ $now.format('x') }}
  • Body (JSON):
{
  "contentIds": ["={{ $json.contentId }}"],
  "targets": [{
    "platform": "tiktok",
    "accountId": "your-tiktok-account-id",
    "intent": "draft",
    "caption": "={{ $('Google Sheets Trigger').item.json.caption }}",
    "dispatchAt": "={{ $('Google Sheets Trigger').item.json.date }}T{{ $('Google Sheets Trigger').item.json.time }}:00",
    "options": {
      "privacyLevel": "PUBLIC_TO_EVERYONE"
    }
  }]
}

Step 7: Update Sheet Status

Add Google Sheets node:

  • Operation: Update Row
  • Spreadsheet: Your content calendar
  • Row Number: ={{ $('Google Sheets Trigger').item.json.row_number }}
  • Column: Status
  • Value: Scheduled

Step 8: Save and Activate

Click "Save" then toggle the workflow to "Active". Now when you type "Ready" in the Status column, the workflow triggers and schedules your post.

Method 2: Google Apps Script

For a fully integrated sheets to tiktok solution inside Google Sheets, use Apps Script.

Step 1: Open Apps Script

In your Google Sheet, click Extensions -> Apps Script.

Step 2: Add the Script

Paste this code:

const CONFIG = {
  POSTQUED_API_KEY: 'pq_your_api_key_here',
  TIKTOK_ACCOUNT_ID: 'your-account-id',
  API_BASE_URL: 'https://api.postqued.com'
};

/**
 * Main function to process ready posts
 */
function processTikTokPosts() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  
  // Find column indices
  const colIndex = {
    date: headers.indexOf('Date'),
    time: headers.indexOf('Time'),
    timezone: headers.indexOf('Timezone'),
    videoUrl: headers.indexOf('Video URL'),
    caption: headers.indexOf('Caption'),
    status: headers.indexOf('Status'),
    publishId: headers.indexOf('Publish ID'),
    postedAt: headers.indexOf('Posted At')
  };
  
  // Process each row
  for (let i = 1; i < data.length; i++) {
    const row = data[i];
    const status = row[colIndex.status];
    
    if (status === 'Ready') {
      try {
        const result = schedulePost(row, colIndex);
        
        // Update status to Scheduled
        sheet.getRange(i + 1, colIndex.status + 1).setValue('Scheduled');
        sheet.getRange(i + 1, colIndex.publishId + 1).setValue(result.id);
        sheet.getRange(i + 1, colIndex.postedAt + 1).setValue(new Date());
        
        Logger.log(`Scheduled row ${i + 1}: ${result.id}`);
        
        // Rate limiting - wait 6 seconds between posts
        Utilities.sleep(6000);
        
      } catch (error) {
        Logger.log(`Error on row ${i + 1}: ${error}`);
        sheet.getRange(i + 1, colIndex.status + 1).setValue('Failed');
      }
    }
  }
}

/**
 * Schedule a single post
 */
function schedulePost(row, colIndex) {
  const videoUrl = row[colIndex.videoUrl];
  const caption = row[colIndex.caption];
  const date = row[colIndex.date];
  const time = row[colIndex.time];
  const timezone = row[colIndex.timezone] || 'UTC';
  
  // Build schedule datetime
  const scheduleDate = new Date(date);
  const [hours, minutes] = time.split(':').map(Number);
  scheduleDate.setHours(hours, minutes, 0, 0);
  
  // Convert to UTC ISO string
  const dispatchAt = scheduleDate.toISOString();
  
  // Upload video first (simplified - assumes direct URL)
  const contentId = uploadVideo(videoUrl);
  
  // Schedule the post
  const publishRequest = {
    contentIds: [contentId],
    targets: [{
      platform: 'tiktok',
      accountId: CONFIG.TIKTOK_ACCOUNT_ID,
      intent: 'draft',
      caption: caption,
      dispatchAt: dispatchAt,
      options: {
        privacyLevel: 'PUBLIC_TO_EVERYONE',
        disableDuet: false,
        disableStitch: false,
        disableComment: false
      }
    }]
  };
  
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${CONFIG.POSTQUED_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': Utilities.getUuid()
    },
    payload: JSON.stringify(publishRequest)
  };
  
  const response = UrlFetchApp.fetch(
    `${CONFIG.API_BASE_URL}/v1/content/publish`,
    options
  );
  
  if (response.getResponseCode() !== 200) {
    throw new Error(`Publish failed: ${response.getContentText()}`);
  }
  
  return JSON.parse(response.getContentText());
}

/**
 * Upload video and return content ID
 */
function uploadVideo(videoUrl) {
  // For Google Drive files, you need to handle authentication
  // For direct URLs, we need to download and upload
  
  // Simplified example - assumes video is already accessible
  // In production, implement actual file upload logic
  
  const fileName = videoUrl.split('/').pop() || 'video.mp4';
  
  const uploadRequest = {
    filename: fileName,
    contentType: 'video/mp4',
    fileSize: 5242880  // You should get actual file size
  };
  
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${CONFIG.POSTQUED_API_KEY}`,
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify(uploadRequest)
  };
  
  const response = UrlFetchApp.fetch(
    `${CONFIG.API_BASE_URL}/v1/content/upload`,
    options
  );
  
  const uploadData = JSON.parse(response.getContentText());
  
  // Download and upload file to presigned URL
  const videoBlob = UrlFetchApp.fetch(videoUrl).getBlob();
  
  UrlFetchApp.fetch(uploadData.upload.url, {
    method: 'PUT',
    headers: uploadData.upload.headers || {},
    payload: videoBlob
  });
  
  // Confirm upload
  UrlFetchApp.fetch(
    `${CONFIG.API_BASE_URL}/v1/content/upload/complete`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.POSTQUED_API_KEY}`,
        'Content-Type': 'application/json'
      },
      payload: JSON.stringify({
        contentId: uploadData.contentId,
        key: uploadData.key || '',
        filename: fileName,
        contentType: 'video/mp4',
        size: videoBlob.getBytes().length,
        width: 1080,
        height: 1920,
        durationMs: 15000
      })
    }
  );
  
  return uploadData.contentId;
}

/**
 * Add custom menu to sheet
 */
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('TikTok Automation')
    .addItem('Process Ready Posts', 'processTikTokPosts')
    .addItem('Check Post Status', 'checkAllStatuses')
    .addToUi();
}

/**
 * Check status of all scheduled posts
 */
function checkAllStatuses() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  
  const statusCol = headers.indexOf('Status');
  const publishIdCol = headers.indexOf('Publish ID');
  
  for (let i = 1; i < data.length; i++) {
    const status = data[i][statusCol];
    const publishId = data[i][publishIdCol];
    
    if (status === 'Scheduled' && publishId) {
      try {
        const currentStatus = checkPublishStatus(publishId);
        
        if (currentStatus === 'sent' || currentStatus === 'published') {
          sheet.getRange(i + 1, statusCol + 1).setValue('Posted');
        } else if (currentStatus === 'failed') {
          sheet.getRange(i + 1, statusCol + 1).setValue('Failed');
        }
      } catch (error) {
        Logger.log(`Error checking status for ${publishId}: ${error}`);
      }
    }
  }
}

/**
 * Check individual publish status
 */
function checkPublishStatus(publishId) {
  const response = UrlFetchApp.fetch(
    `${CONFIG.API_BASE_URL}/v1/content/publish/${publishId}`,
    {
      headers: {
        'Authorization': `Bearer ${CONFIG.POSTQUED_API_KEY}`
      }
    }
  );
  
  const data = JSON.parse(response.getContentText());
  return data.status;
}

Step 3: Configure the Script

Replace these values in the CONFIG section:

  • POSTQUED_API_KEY: Your actual API key
  • TIKTOK_ACCOUNT_ID: Your TikTok account ID from Postqued

Step 4: Authorize and Run

  1. Click the "Save" button (disk icon)
  2. Select function "processTikTokPosts"
  3. Click "Run"
  4. Grant permissions when prompted

The script will process all rows marked "Ready" and schedule them for posting.

Step 5: Add Custom Menu

The onOpen function automatically adds a menu to your sheet. Reload the spreadsheet to see the "TikTok Automation" menu with options to process posts and check statuses.

Method 3: Python with gspread

For advanced users who prefer Python, use the gspread library to read from Sheets and schedule posts.

Step 1: Install Dependencies

pip install gspread google-auth requests pytz

Step 2: Set Up Google Sheets API

  1. Go to Google Cloud Console
  2. Create a new project
  3. Enable Google Sheets API
  4. Create service account credentials
  5. Download the JSON key file
  6. Share your spreadsheet with the service account email

Step 3: Create the Python Script

import os
import json
import uuid
import requests
import pytz
import gspread
from datetime import datetime
from google.oauth2.service_account import Credentials

# Configuration
API_KEY = os.environ.get("POSTQUED_API_KEY")
TIKTOK_ACCOUNT_ID = os.environ.get("TIKTOK_ACCOUNT_ID")
API_BASE_URL = "https://api.postqued.com"

# Google Sheets setup
SCOPES = [
    'https://www.googleapis.com/auth/spreadsheets',
    'https://www.googleapis.com/auth/drive'
]

def get_sheet_data(spreadsheet_id, sheet_name="Sheet1"):
    """Read data from Google Sheets."""
    creds = Credentials.from_service_account_file(
        'credentials.json', scopes=SCOPES
    )
    client = gspread.authorize(creds)
    
    spreadsheet = client.open_by_key(spreadsheet_id)
    worksheet = spreadsheet.worksheet(sheet_name)
    
    return worksheet.get_all_records()

def upload_video(file_path_or_url: str) -> str:
    """Upload video and return content ID."""
    # Handle URL vs local file
    if file_path_or_url.startswith('http'):
        import urllib.request
        temp_path = "/tmp/temp_video.mp4"
        urllib.request.urlretrieve(file_path_or_url, temp_path)
        file_path = temp_path
    else:
        file_path = file_path_or_url
    
    import os as os_module
    file_size = os_module.path.getsize(file_path)
    file_name = os_module.path.basename(file_path)
    
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    
    # Get upload URL
    upload_res = requests.post(
        f"{API_BASE_URL}/v1/content/upload",
        headers=headers,
        json={
            "filename": file_name,
            "contentType": "video/mp4",
            "fileSize": file_size
        }
    )
    upload_res.raise_for_status()
    
    upload_data = upload_res.json()
    content_id = upload_data["contentId"]
    
    # Upload file
    with open(file_path, "rb") as f:
        requests.put(
            upload_data["upload"]["url"],
            data=f,
            headers=upload_data["upload"].get("headers", {})
        )
    
    # Confirm upload
    requests.post(
        f"{API_BASE_URL}/v1/content/upload/complete",
        headers=headers,
        json={
            "contentId": content_id,
            "key": upload_data.get("key", ""),
            "filename": file_name,
            "contentType": "video/mp4",
            "size": file_size,
            "width": 1080,
            "height": 1920,
            "durationMs": 15000
        }
    ).raise_for_status()
    
    return content_id

def schedule_from_sheet(spreadsheet_id: str):
    """Process ready posts from Google Sheet."""
    records = get_sheet_data(spreadsheet_id)
    
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    
    for i, row in enumerate(records, start=2):  # start=2 for row number (header is row 1)
        if row.get("Status") != "Ready":
            continue
        
        try:
            print(f"Processing row {i}: {row.get('Caption', '')[:50]}...")
            
            # Upload video
            content_id = upload_video(row["Video URL"])
            
            # Parse schedule time
            date_str = row["Date"]
            time_str = row["Time"]
            timezone_str = row.get("Timezone", "UTC")
            
            # Combine date and time
            if isinstance(date_str, str):
                schedule_dt = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M")
            else:
                schedule_dt = datetime.combine(date_str, datetime.strptime(time_str, "%H:%M").time())
            
            # Localize and convert to UTC
            tz = pytz.timezone(timezone_str)
            schedule_dt = tz.localize(schedule_dt)
            utc_time = schedule_dt.astimezone(pytz.UTC)
            
            # Schedule post
            idempotency_key = str(uuid.uuid4())
            
            publish_request = {
                "contentIds": [content_id],
                "targets": [{
                    "platform": "tiktok",
                    "accountId": TIKTOK_ACCOUNT_ID,
                    "intent": "draft",
                    "caption": row["Caption"],
                    "dispatchAt": utc_time.isoformat().replace("+00:00", "Z"),
                    "options": {
                        "privacyLevel": "PUBLIC_TO_EVERYONE",
                        "disableDuet": False,
                        "disableStitch": False,
                        "disableComment": False
                    }
                }]
            }
            
            response = requests.post(
                f"{API_BASE_URL}/v1/content/publish",
                headers={**headers, "Idempotency-Key": idempotency_key},
                json=publish_request
            )
            response.raise_for_status()
            
            result = response.json()
            print(f"  Scheduled! ID: {result['id']}")
            
            # Update sheet status (requires additional gspread code)
            # worksheet.update_cell(i, 6, "Scheduled")
            # worksheet.update_cell(i, 7, result['id'])
            
        except Exception as e:
            print(f"  Error: {e}")
            # worksheet.update_cell(i, 6, "Failed")

# Usage
if __name__ == "__main__":
    SPREADSHEET_ID = "your-spreadsheet-id-here"
    schedule_from_sheet(SPREADSHEET_ID)

Step 4: Run the Script

export POSTQUED_API_KEY="your-key"
export TIKTOK_ACCOUNT_ID="your-account-id"
python schedule_from_sheets.py

Handling Google Drive Video Files

If your videos are in Google Drive, you need to make them accessible:

Option 1: Public Links

  1. Right-click video in Google Drive
  2. Click "Share"
  3. Change to "Anyone with the link"
  4. Copy the link
  5. Use the direct download URL format: https://drive.google.com/uc?export=download&id=YOUR_FILE_ID

Option 2: Service Account Access

For private files, use Google Drive API with your service account:

from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials

def get_drive_file(file_id):
    """Download file from Google Drive using service account."""
    creds = Credentials.from_service_account_file(
        'credentials.json',
        scopes=['https://www.googleapis.com/auth/drive.readonly']
    )
    
    service = build('drive', 'v3', credentials=creds)
    
    request = service.files().get_media(fileId=file_id)
    file_content = request.execute()
    
    return file_content

Building an Approval Workflow

Add approval steps to your schedule tiktok from sheets workflow:

  1. Add an "Approval" column to your sheet
  2. Change your automation to only process rows where Status = "Ready" AND Approval = "Approved"
  3. Use Google Sheets data validation for the Approval column (Pending, Approved, Rejected)
  4. Add conditional formatting to highlight rows awaiting approval

For n8n workflows, add an "If" node that checks the Approval column before proceeding to publish.

Best Practices for Google Sheets TikTok Integration

Use consistent date formats. Stick to ISO format (YYYY-MM-DD) to avoid parsing errors across different locales.

Include timezone information. Always specify the timezone column. This prevents posts from going out at the wrong time.

Validate video URLs. Add a formula column that checks if the URL returns a 200 status before marking as Ready.

Implement rate limiting. Process no more than 10 posts per minute to avoid API limits. Add delays between requests.

Log everything. Track Publish IDs, timestamps, and error messages in your sheet for debugging.

Use data validation. Restrict the Status column to specific values. This prevents typos that break your automation.

Set up monitoring. Create a dashboard view that shows upcoming posts, failed posts, and posts needing approval.

Troubleshooting Sheets to TikTok Automation

"Ready" rows not processing

Check your trigger configuration. For n8n, verify the filter matches exactly. For Apps Script, check the Logs (View -> Logs).

Video upload fails

Ensure the video URL is publicly accessible or your service account has access. Check file size is under 500MB.

Posts scheduled for wrong time

Verify your timezone column uses correct names like "America/New_York" not "EST". Check daylight saving time handling.

Authentication errors

For Apps Script, re-run authorization. For Python, verify your credentials.json file path. For n8n, check your credential is selected.

Quota exceeded errors

Google Sheets API has quotas (500 requests per 100 seconds). Add delays between requests or batch operations.

Videos not appearing on TikTok

Check your account ID is correct. Ensure you are using "intent": "draft" (requires manual approval in TikTok). Posts using "publish" require special TikTok approval.

Example Content Calendar Templates

Template 1: Weekly Posting Schedule

DateTimeTimezoneVideo URLCaptionStatus
2026-03-1709:00America/New_York[drive link]Monday motivation! #mondayReady
2026-03-1812:00America/New_York[drive link]Tutorial Tuesday #tutorialReady
2026-03-1915:00America/New_York[drive link]Behind the scenes #btsReady

Template 2: Campaign Tracker

Add columns for Campaign, Objective, Target Audience, and Performance metrics.

Template 3: Team Collaboration

Add columns for Assigned To, Reviewed By, and Notes for team coordination.

Next Steps for Your Google Sheets TikTok Automation

You now have three methods to automate google sheets tiktok posting:

  1. n8n for visual, no-code workflows and automation workflows
  2. Google Apps Script for native Sheets integration with TikTok API
  3. Python with gspread for advanced customization and TikTok automation

Choose the method that fits your technical skills and workflow needs. Start with a simple schedule, then add approval workflows, error handling, and performance tracking.

For more no-code tools and automation workflows, see our guides on:

Keywords covered: google sheets tiktok, sheets to tiktok, schedule tiktok from sheets, google sheets social media automation, spreadsheet to tiktok, TikTok API, automation workflows, no-code tools

Ready to automate your TikTok posting from Google Sheets? Get your Postqued API key and start building your content calendar today.


Need help with your integration? Check our API documentation or contact support.