Skip to main content
RapidDev - Software Development Agency
API AutomationsRedditOAuth 2.0

How to Automate Reddit Post Scheduling using the API

Reddit has no native scheduling API — build your own cron job that calls POST /api/submit at the target time with kind=self (text post) or kind=link (URL post). Requires OAuth2 with submit scope. Free tier handles 60 RPM. Critical gotcha: RATELIMIT and ALREADY_SUB errors are returned inside 200 OK responses, not as HTTP error codes — you must parse the JSON body.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read15-30 minutesRedditMay 2026RapidDev Engineering Team
TL;DR

Reddit has no native scheduling API — build your own cron job that calls POST /api/submit at the target time with kind=self (text post) or kind=link (URL post). Requires OAuth2 with submit scope. Free tier handles 60 RPM. Critical gotcha: RATELIMIT and ALREADY_SUB errors are returned inside 200 OK responses, not as HTTP error codes — you must parse the JSON body.

API Quick Reference

Auth

OAuth 2.0

Rate limit

60 requests/minute

Format

JSON

SDK

Available

Understanding the Reddit API

The Reddit Data API is a REST API providing access to Reddit content, communities, and posting capabilities. Authentication uses OAuth2, and all API requests must go to oauth.reddit.com (not www.reddit.com) — the wrong base URL drops you to 10 RPM unauthenticated.

For post scheduling, the key endpoint is POST /api/submit. Reddit does not provide a native schedule parameter — you build scheduling yourself using a cron job, task queue (Celery, BullMQ), or a scheduled serverless function that fires at the target time and calls submit.

The free tier at 60 RPM is completely sufficient for scheduling. Unlike X/Twitter (which charges $0.015-$0.20 per post) or TikTok (which requires a 2-6 week audit), Reddit posting is free with no tier-based pricing. The only paid tier is the commercial enterprise agreement at $12,000/month for very high volume.

Base URLhttps://oauth.reddit.com

Setting Up Reddit API Authentication

Reddit uses OAuth2 with different grant types for different app types. A script-type app (for your own bot) uses the password grant — simple but requires storing credentials. A web app uses authorization_code for multi-user scenarios. Access tokens expire after 1 hour; use duration=permanent with the web flow to get long-lived refresh tokens.

  1. 1Go to reddit.com/prefs/apps and click 'create another app'
  2. 2Select 'script' for a personal bot or 'web app' for a SaaS scheduler
  3. 3Set the redirect URI to http://localhost:8080 for script apps
  4. 4Note your client_id (below the app name) and client_secret
  5. 5POST to https://www.reddit.com/api/v1/access_token with Basic auth (client_id:client_secret)
  6. 6Include in request body: grant_type=password, username=your_reddit_username, password=your_password
  7. 7Set User-Agent: <platform>:<app-id>:<version> (by /u/<username>) — this format is mandatory
  8. 8Use the returned access_token as Authorization: Bearer <token> in all API calls to oauth.reddit.com
auth.py
1import requests
2import os
3
4def get_access_token():
5 CLIENT_ID = os.environ['REDDIT_CLIENT_ID']
6 CLIENT_SECRET = os.environ['REDDIT_CLIENT_SECRET']
7 USERNAME = os.environ['REDDIT_USERNAME']
8 PASSWORD = os.environ['REDDIT_PASSWORD']
9 USER_AGENT = f'python:com.example.scheduler:v1.0 (by /u/{USERNAME})'
10
11 r = requests.post(
12 'https://www.reddit.com/api/v1/access_token',
13 auth=requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET),
14 data={'grant_type': 'password', 'username': USERNAME, 'password': PASSWORD},
15 headers={'User-Agent': USER_AGENT}
16 )
17 r.raise_for_status()
18 return r.json()['access_token'], USER_AGENT

Security notes

  • Store all credentials in environment variables — never hardcode CLIENT_ID, CLIENT_SECRET, or passwords
  • The User-Agent must follow <platform>:<app-id>:<version> (by /u/<username>) exactly — Reddit throttles non-compliant bots
  • Refresh tokens before the 1-hour expiry to avoid mid-job authentication failures
  • Use a dedicated bot account, not your personal Reddit account
  • For production schedulers, use the web app type with refresh tokens instead of hardcoded passwords

Key endpoints

POST/api/submit

Submits a new post to a subreddit. Use kind=self for text posts, kind=link for URL posts. Returns the new post's fullname and URL on success, or errors embedded in the 200 OK body.

ParameterTypeRequiredDescription
kindstringrequiredPost type: self (text), link (URL), image, video, videogif
srstringrequiredSubreddit name (without r/ prefix)
titlestringrequiredPost title, max 300 characters
textstringoptionalBody text for self posts (Markdown supported)
urlstringoptionalURL for link posts
flair_idstringoptionalFlair template ID from /r/{sr}/api/link_flair_v2
nsfwbooleanoptionalMark post as NSFW
spoilerbooleanoptionalMark post as spoiler

Request

json
1{"kind": "self", "sr": "python", "title": "Weekly discussion: best Python libraries 2026", "text": "What libraries have you discovered this week?", "nsfw": false, "spoiler": false}

Response

json
1{"json": {"errors": [], "data": {"url": "https://www.reddit.com/r/python/comments/abc123/weekly_discussion_best_python_libraries_2026/", "drafts_count": 0, "id": "abc123", "name": "t3_abc123"}}}
GET/api/needs_captcha

Checks whether a CAPTCHA is required before posting. Returns true if the account is new or has low karma. If true, programmatic posting from new accounts may be blocked.

Response

json
1false
POST/r/{subreddit}/api/flairselect

Applies a flair to a recently submitted post. Call immediately after submit if the subreddit requires flair for posts to remain visible.

ParameterTypeRequiredDescription
linkstringrequiredFullname of the post (t3_xxx)
flair_template_idstringoptionalUUID from the subreddit's flair list
textstringoptionalCustom flair text if allowed by subreddit

Request

json
1{"link": "t3_abc123", "flair_template_id": "uuid-here", "text": "Question"}

Response

json
1{}

Step-by-step automation

1

Authenticate and Build Headers

Why: All POST /api/submit calls require a valid user-context OAuth2 token — unauthenticated or app-only tokens cannot submit posts.

Authenticate using the password grant for script-type apps or the authorization_code flow for web apps. Build the headers dictionary you'll reuse for all API calls. Check token expiry before each post and refresh proactively at 50 minutes to avoid mid-schedule failures.

request.sh
1curl -X POST https://www.reddit.com/api/v1/access_token \
2 -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \
3 -H 'User-Agent: script:com.example.scheduler:v1.0 (by /u/yourusername)' \
4 -d 'grant_type=password&username=YOUR_USERNAME&password=YOUR_PASSWORD'

Pro tip: Store the token in a module-level variable with its expiry time. Never fetch a new token on every API call — that wastes quota and slows down your scheduler.

Expected result: A headers object with Authorization and User-Agent ready for all subsequent API calls.

2

Load Scheduled Posts from Your Queue

Why: Your scheduler needs a data store for the post queue — this could be a JSON file, SQLite database, or a spreadsheet. Load posts that are due within the next poll interval.

Design a simple post record with: subreddit, title, kind (self/link), text or url, scheduled_time (ISO 8601), and status (pending/posted/failed). Query records where scheduled_time <= now AND status == pending. Sort by scheduled_time to post in order.

request.sh
1# This step is data-layer logic, not an API call.
2# Example: read from a JSON file
3cat scheduled_posts.json | python3 -c "
4import json, sys
5from datetime import datetime, timezone
6posts = json.load(sys.stdin)
7now = datetime.now(timezone.utc).isoformat()
8due = [p for p in posts if p['scheduled_time'] <= now and p['status'] == 'pending']
9print(json.dumps(due, indent=2))
10"

Pro tip: Include a max_retries field in each post record. If a post fails with RATELIMIT, increment retries and re-schedule for 15 minutes later rather than dropping it.

Expected result: A list of post objects due for submission, ordered by scheduled time.

3

Submit Post via POST /api/submit

Why: The actual posting step — the API has no scheduled delivery, so you submit at the exact moment you want the post to appear.

POST to /api/submit with kind, sr (subreddit name), title, and either text (for self posts) or url (for link posts). Always check the JSON body for errors after getting a 200 response — Reddit embeds RATELIMIT and ALREADY_SUB errors in the success body. On ALREADY_SUB, the URL was already submitted to that subreddit; either use kind=self or a different URL.

request.sh
1curl -X POST https://oauth.reddit.com/api/submit \
2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
3 -H 'User-Agent: script:com.example.scheduler:v1.0 (by /u/yourusername)' \
4 -H 'Content-Type: application/x-www-form-urlencoded' \
5 -d 'kind=self&sr=python&title=Weekly+Discussion&text=Share+your+tips+this+week&nsfw=false'

Pro tip: Set resubmit=true to allow link re-submission. Without this, re-posting a URL that was previously deleted returns ALREADY_SUB. Reddit still shows 'already submitted' warnings to users, but the post goes through.

Expected result: On success: json.errors is empty and json.data contains id (the post ID like 'abc123') and url (the full Reddit post URL).

4

Apply Flair if Required and Log Result

Why: Many subreddits require flair on posts — posts without required flair may be auto-removed by AutoModerator within seconds of posting.

If the subreddit requires flair, call GET /r/{subreddit}/api/link_flair_v2 first to get available flair template IDs, then include flair_id in the submit call or follow up with POST /r/{subreddit}/api/flairselect. Finally, update your post queue record with the Reddit post ID, URL, and posted timestamp.

request.sh
1# Get available flair for a subreddit
2curl -X GET 'https://oauth.reddit.com/r/python/api/link_flair_v2' \
3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
4 -H 'User-Agent: script:com.example.scheduler:v1.0 (by /u/yourusername)'

Pro tip: Cache subreddit flair lists locally — they rarely change and fetching them on every post wastes quota. Refresh the cache once per day.

Expected result: Post is visible in the subreddit with the correct flair applied. Queue record is updated with status=posted and the Reddit post ID.

Complete working code

This complete scheduler polls a SQLite queue every minute, submits posts that are due, handles RATELIMIT and ALREADY_SUB errors gracefully, and updates the queue with results. It refreshes the OAuth token automatically and logs all activity.

automate_reddit_post_scheduling.py
1import requests
2import sqlite3
3import time
4import os
5import logging
6from datetime import datetime, timedelta, timezone
7
8logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
9log = logging.getLogger('scheduler')
10
11USER_AGENT = f"python:com.example.scheduler:v1.0 (by /u/{os.environ['REDDIT_USERNAME']})"
12token_cache = {'token': None, 'expires_at': datetime.now(timezone.utc)}
13
14def get_headers():
15 if datetime.now(timezone.utc) >= token_cache['expires_at'] - timedelta(minutes=5):
16 r = requests.post(
17 'https://www.reddit.com/api/v1/access_token',
18 auth=requests.auth.HTTPBasicAuth(
19 os.environ['REDDIT_CLIENT_ID'], os.environ['REDDIT_CLIENT_SECRET']
20 ),
21 data={'grant_type': 'password',
22 'username': os.environ['REDDIT_USERNAME'],
23 'password': os.environ['REDDIT_PASSWORD']},
24 headers={'User-Agent': USER_AGENT}
25 )
26 r.raise_for_status()
27 data = r.json()
28 token_cache['token'] = data['access_token']
29 token_cache['expires_at'] = datetime.now(timezone.utc) + timedelta(seconds=data['expires_in'])
30 return {'Authorization': f"Bearer {token_cache['token']}", 'User-Agent': USER_AGENT}
31
32def get_due_posts(db):
33 now = datetime.now(timezone.utc).isoformat()
34 cur = db.execute(
35 "SELECT * FROM scheduled_posts WHERE scheduled_time <= ? AND status = 'pending' ORDER BY scheduled_time ASC",
36 (now,)
37 )
38 cur.row_factory = sqlite3.Row
39 return [dict(row) for row in cur.fetchall()]
40
41def submit_post(headers, post):
42 payload = {'kind': post['kind'], 'sr': post['subreddit'],
43 'title': post['title'], 'nsfw': False, 'resubmit': True}
44 if post['kind'] == 'self':
45 payload['text'] = post.get('text', '')
46 else:
47 payload['url'] = post['url']
48 if post.get('flair_id'):
49 payload['flair_id'] = post['flair_id']
50 r = requests.post('https://oauth.reddit.com/api/submit', headers=headers, data=payload)
51 r.raise_for_status()
52 result = r.json()
53 errors = result.get('json', {}).get('errors', [])
54 if errors:
55 raise Exception(f"{errors[0][0]}: {errors[0][1]}")
56 return result['json']['data']
57
58def run_scheduler(db_path='posts.db'):
59 db = sqlite3.connect(db_path)
60 log.info('Scheduler started')
61 while True:
62 try:
63 posts = get_due_posts(db)
64 for post in posts:
65 try:
66 headers = get_headers()
67 data = submit_post(headers, post)
68 db.execute(
69 "UPDATE scheduled_posts SET status='posted', reddit_post_id=? WHERE id=?",
70 (data['id'], post['id'])
71 )
72 db.commit()
73 log.info(f"Posted t3_{data['id']} to r/{post['subreddit']}: {data['url']}")
74 time.sleep(2)
75 except Exception as e:
76 log.error(f"Failed post {post['id']}: {e}")
77 db.execute(
78 "UPDATE scheduled_posts SET status='failed', error_message=? WHERE id=?",
79 (str(e), post['id'])
80 )
81 db.commit()
82 except Exception as e:
83 log.error(f'Scheduler loop error: {e}')
84 time.sleep(60)
85
86if __name__ == '__main__':
87 run_scheduler()

Error handling

200 (RATELIMIT in body)errors: [["RATELIMIT", "you are doing that too much. try again in 9 minutes.", "ratelimit"]]
Cause

Per-subreddit posting frequency limit. Posting too many times within a short window triggers this. New accounts with low karma hit it more often.

Fix

Parse json.errors on every submit response. Extract the wait time from the error message and reschedule the post for after that duration. Do not retry immediately.

Retry strategy

Reschedule the post for now + (extracted minutes + 2) minutes. Max 3 retries before marking as failed.

200 (ALREADY_SUB in body)errors: [["ALREADY_SUB", "that link has already been submitted", "url"]]
Cause

The URL you're trying to post as a link has already been submitted to that subreddit. Reddit prevents duplicate link posts within a subreddit.

Fix

Use kind=self instead and include the URL in the text body, or append a unique query parameter to the URL if you control it. Set resubmit=true to suppress this for re-submissions of your own deleted posts.

Retry strategy

No retry — change the post type or URL and resubmit.

403SUBREDDIT_NOTALLOWED or USER_REQUIRED
Cause

The account doesn't have permission to post in this subreddit — may require minimum karma, account age, or moderator approval for the bot.

Fix

Check the subreddit's posting requirements. Some require a minimum karma threshold or account age. Consider posting in a development/test subreddit first.

Retry strategy

No retry — fix the account permissions issue.

429Too Many Requests
Cause

Global OAuth rate limit exceeded (60 RPM). Too many API calls across all endpoints within the rate limit window.

Fix

Check X-Ratelimit-Remaining and X-Ratelimit-Reset headers. Reduce request frequency and add delays between API calls.

Retry strategy

Wait until X-Ratelimit-Reset (seconds from now), then retry with exponential backoff.

Rate Limits for Reddit API

ScopeLimitWindow
Global (with OAuth)60 requestsper minute
Global (without OAuth)10 requestsper minute
Per-subreddit post frequencyVariable — enforced via RATELIMIT in response bodyRolling (minutes to hours)
retry-handler.ts
1import time
2import requests
3
4def submit_with_retry(headers, payload, max_retries=3):
5 for attempt in range(max_retries):
6 r = requests.post('https://oauth.reddit.com/api/submit', headers=headers, data=payload)
7 if r.status_code == 429:
8 reset = int(r.headers.get('X-Ratelimit-Reset', 60))
9 print(f'HTTP 429 — waiting {reset}s')
10 time.sleep(reset + 1)
11 continue
12 r.raise_for_status()
13 errors = r.json().get('json', {}).get('errors', [])
14 if errors and errors[0][0] == 'RATELIMIT':
15 wait = min(60 * (2 ** attempt), 600)
16 print(f'RATELIMIT — waiting {wait}s (attempt {attempt+1})')
17 time.sleep(wait)
18 continue
19 return r.json()
20 raise Exception('Max retries exceeded')
  • Always use OAuth2 — unauthenticated posting is not supported and drops to 10 RPM anyway
  • Parse json.errors in EVERY /api/submit response — HTTP 200 does not mean success on Reddit
  • Add 2+ second delays between consecutive posts to the same subreddit
  • Monitor X-Ratelimit-Remaining and pause when it approaches zero
  • Schedule posts at least 1 minute apart to stay well within Reddit's anti-spam detection

Security checklist

  • Store REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, USERNAME, and PASSWORD in environment variables
  • Use a dedicated bot account separate from any personal Reddit account
  • Set a descriptive User-Agent with your contact info as required by Reddit's API terms
  • Implement automatic token refresh before the 1-hour expiry to prevent mid-schedule failures
  • Log all post attempts with timestamps, subreddits, and outcomes for audit trail
  • Validate post content before submission to ensure it complies with target subreddit rules
  • Never store Reddit passwords or tokens in source control or config files

Automation use cases

Weekly Community Discussion Posts

beginner

Automatically post recurring weekly discussion threads to a community subreddit at consistent times without manual effort.

Multi-Subreddit Content Distribution

intermediate

Schedule the same content (with subreddit-appropriate titles) to be posted across multiple relevant subreddits with time delays between each.

News or Product Update Announcements

intermediate

Post new blog articles, product releases, or changelogs to relevant subreddits automatically when they're published in your CMS.

Drip Campaign for Product Launches

advanced

Schedule a series of posts building up to a product launch across multiple subreddits, with analytics tracking engagement per post.

No-code alternatives

Don't want to write code? These platforms can automate the same workflows visually.

Zapier

Free tier (5 Zaps); Starter from $19.99/month

Zapier's Reddit module supports creating new posts triggered by events in other apps (RSS feeds, Google Sheets rows, form submissions). Scheduling support requires pairing with Zapier's Schedule trigger.

Pros
  • + No code needed
  • + Many trigger options (RSS, Sheets, webhooks)
  • + Reliable delivery
Cons
  • - No native post scheduling with specific times
  • - Task-based pricing adds up at scale
  • - Limited error handling for Reddit-specific errors

Make

Free tier; Core from $9/month

Make's Reddit module can create posts with richer filtering and scheduling options than Zapier, including time-based scheduling with the Schedule module.

Pros
  • + Better scheduling support than Zapier
  • + Visual flow editor
  • + More flexible filtering
Cons
  • - Reddit module has limited operations
  • - Per-operation pricing
  • - No RATELIMIT body error handling

n8n

Free self-hosted; Cloud from €20/month

n8n's Reddit node supports submitting posts with a Cron trigger for scheduling. The HTTP Request node can be used for any Reddit API operation not covered natively.

Pros
  • + Self-hostable (free)
  • + Cron-based scheduling built-in
  • + Full HTTP flexibility for advanced cases
Cons
  • - More technical setup required
  • - Reddit node limited to basic operations
  • - Self-hosted maintenance burden

Best practices

  • Parse json.errors in every /api/submit response — HTTP 200 does not guarantee the post was created
  • Use fullname-based pagination (after=t3_xxx) for listing posts, never page numbers
  • Store flair template IDs in your queue database rather than fetching them on every post
  • Schedule posts at different times per subreddit to avoid looking like spam across communities
  • Test with a private or development subreddit before going live with a production scheduler
  • Check subreddit posting rules and required flair before adding it to your scheduler queue
  • Implement a dry-run mode that logs what would be posted without actually calling the API
  • Monitor your post's karma in the first hour — low initial engagement may indicate it was caught by spam filters

Ask AI to help

Copy one of these prompts to get a personalized, working implementation.

ChatGPT / Claude Prompt

I'm building a Reddit post scheduler in Python using the OAuth2 API. I POST to https://oauth.reddit.com/api/submit and need to handle Reddit's unusual error pattern where RATELIMIT and ALREADY_SUB errors come back in the JSON body of a 200 OK response, not as HTTP error codes. Help me write a submit_post() function that: 1) properly checks json.errors after a 200 response, 2) raises specific exceptions for RATELIMIT (with the wait duration extracted from the error message) and ALREADY_SUB, 3) retries RATELIMIT after the indicated wait time, and 4) returns the post ID and URL on success.

Lovable / V0 Prompt

Build a Reddit post scheduling dashboard. Features needed: a form to add scheduled posts (subreddit, title, kind: text/link, body text or URL, target datetime, optional flair), a calendar/timeline view showing scheduled posts, a status board showing pending/posted/failed posts with their Reddit URLs and karma scores, and a settings panel for API credentials. Use a clean dark UI with Reddit's orange accent color. The backend should be a Node.js API that manages a SQLite queue and submits posts via the Reddit API.

Frequently asked questions

Is the Reddit API free for scheduling posts?

Yes — the free tier supports 60 requests per minute with OAuth2, which is more than enough for scheduling. You can post to multiple subreddits daily without any cost. Commercial use at high scale requires an enterprise agreement at $12,000/month, but personal schedulers and small tools are completely free.

Does Reddit have a native scheduling feature in the API?

No. Reddit's API has no scheduled_time or publish_at parameter on /api/submit. You build scheduling yourself: store posts with their target times, run a polling loop (or cron job) that checks every minute, and call /api/submit when a post becomes due.

What happens when I hit the rate limit?

Two separate rate limits apply: global HTTP 429 when you exceed 60 RPM overall, and a per-subreddit RATELIMIT embedded in a 200 OK response body (errors: [["RATELIMIT", "try again in N minutes", "ratelimit"]]). Parse json.errors on every response. The per-subreddit limit is the more common issue for schedulers posting to active communities.

Why is my post not appearing in the subreddit after a successful API response?

Most likely: 1) AutoModerator removed it for missing required flair — check if the subreddit requires flair and add flair_id to your submit call. 2) The account is too new or has insufficient karma for that subreddit. 3) The post was caught by Reddit's spam filter and is pending manual review by moderators.

Can I schedule posts using PRAW (Python Reddit API Wrapper)?

Yes. PRAW simplifies authentication and posting with subreddit.submit(title, selftext=...) for text posts and subreddit.submit(title, url=...) for links. Install with pip install praw. PRAW handles rate limiting automatically and raises praw.exceptions.RedditAPIException for errors, making it easier to catch RATELIMIT and ALREADY_SUB errors.

What is the maximum number of posts I can schedule per day?

Reddit doesn't publish a hard daily posting limit per account, but the per-subreddit RATELIMIT throttles frequent posters. In practice, posting more than 4-6 times per day to the same subreddit triggers throttling. Spread posts across different subreddits and use a minimum 2-hour gap between posts to the same community.

Can RapidDev build a custom Reddit scheduling tool for my business?

Yes. RapidDev has built 600+ apps including content scheduling and social media automation platforms. We can build a production-ready Reddit scheduler with a full management dashboard, multi-subreddit support, flair handling, and analytics. Contact us for a free consultation.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.