crontoolModern Cron Expression Builder

Social Media Post Scheduler Cron Job

Post to Twitter/X or LinkedIn at 10am and 3pm on weekdays — peak engagement times — from a queue of pre-written posts.

0 10,15 * * 1-5
At 10:00 AM and 3:00 PM, Monday through Friday
Open in builder →

How it works

The expression 0 10,15 * * 1-5 fires twice a day on weekdays: at 10:00 AM and 3:00 PM. The comma in the hour field (10,15) means "at hour 10 and hour 15". This targets the two peak engagement windows for developer and professional audiences.

The script reads from a queue — a CSV file, database table, or Airtable base — picks the next unposted item, posts it, and marks it as sent.

Queue-based post script (Python)

import os, csv, tweepy
from datetime import datetime

# Load queue from CSV: columns = [text, posted, posted_at]
QUEUE_FILE = "posts.csv"

def get_next_post():
    rows = []
    with open(QUEUE_FILE) as f:
        reader = csv.DictReader(f)
        rows = list(reader)

    for row in rows:
        if row["posted"] == "0":
            return row, rows
    return None, rows

def mark_posted(rows, post):
    for row in rows:
        if row["text"] == post["text"]:
            row["posted"] = "1"
            row["posted_at"] = datetime.utcnow().isoformat()
    with open(QUEUE_FILE, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=rows[0].keys())
        writer.writeheader()
        writer.writerows(rows)

post, rows = get_next_post()
if not post:
    print("Queue empty — nothing to post")
    exit(0)

# Post to Twitter/X
client = tweepy.Client(bearer_token=os.environ["TWITTER_BEARER_TOKEN"],
                       consumer_key=os.environ["TWITTER_API_KEY"],
                       consumer_secret=os.environ["TWITTER_API_SECRET"],
                       access_token=os.environ["TWITTER_ACCESS_TOKEN"],
                       access_token_secret=os.environ["TWITTER_ACCESS_SECRET"])

client.create_tweet(text=post["text"])
mark_posted(rows, post)
print(f"Posted: {post['text'][:60]}...")

Platform snippets

Standard crontab
0 10,15 * * 1-5    /usr/local/bin/social-post.py >> /var/log/social.log 2>&1
GitHub Actions
on:
  schedule:
    - cron: '0 10,15 * * 1-5'   # UTC

jobs:
  post:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install tweepy
      - run: python scripts/social_post.py
        env:
          TWITTER_BEARER_TOKEN: ${{ secrets.TWITTER_BEARER_TOKEN }}
          TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
          TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
          TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
          TWITTER_ACCESS_SECRET: ${{ secrets.TWITTER_ACCESS_SECRET }}
AWS EventBridge
cron(0 10,15 ? * MON-FRI *)
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: social-scheduler
spec:
  schedule: "0 10,15 * * 1-5"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: poster
            image: your-image:latest
            command: ["python", "social_post.py"]
            envFrom:
            - secretRef:
                name: twitter-credentials
          restartPolicy: OnFailure

Timezone note

10am and 3pm in crontab use your server's timezone. GitHub Actions and AWS EventBridge use UTC. If your audience is US Eastern (UTC-5), use 0 15,20 * * 1-5 to post at 10am and 3pm ET.

More guides

Morning brief Database backup Weekly metrics report Cache warm-up