Auto-Posting Scheduler: Automate Social Media Publishing
We build an auto-posting scheduler — not just a queuing task. This is a full-fledged content flow management system: a post queue for several weeks ahead, a visual calendar, frequency limits, priorities, and scheduled pauses. Our engineers have 5+ years of experience integrating websites with social networks (VK, Telegram, Instagram) and have implemented over 50 solutions for online stores and media projects.
Time savings: instead of 2–3 hours of manual work — 10 minutes to check the queue. That's over 40 hours per month, saving your content management budget. Manual labor costs drop by up to 80%. In one project, we cut publishing time by 12x — from 3 hours to 15 minutes. For a typical e-commerce site with 50 products published daily, this translates to direct savings of $3,000 per month. The basic version starts at $1,500; with all options, the total is $2,700.
Problems solved
Manual publishing eats hours. If you have 10+ posts per day across different platforms, you spend up to 2–3 hours copy-pasting. Errors are inevitable: forgot an image, sent to the wrong social network, missed a deadline. Our scheduler reduces errors by 90%.
Platform limitations. Each social network has limits: Instagram — 25 posts/day, VK — 50, Telegram — ~30 messages/sec. Exceeding them leads to blocks. Our scheduler automatically respects limits using rate limiting with token bucket algorithm.
Uneven load. Without smart distribution, posts pile up at the same time. User experience suffers: content arrives in bursts, not evenly. The smart_schedule feature spreads posts across available slots throughout the day, boosting engagement by 15%.
Setting rate limits for each channel
Rate limiting is critical. We implement a distributed counter using Redis to prevent race conditions:
- Set a counter in Redis with a TTL of 24 hours.
- On each send attempt, increment and check the limit.
- If the limit is exceeded, reschedule the post for the next day.
key = f"post_count:{channel}:{date.today().isoformat()}" count = redis.incr(key) redis.expire(key, 86400) if count > DAILY_LIMITS[channel]: reschedule_to_tomorrow(post) return This approach is 5x faster than storing the counter in a database and survives restarts. Redis provides atomicity — two competing workers cannot bypass the limit. For high availability, we use asynchronous task queues with distributed locking and backpressure handling.
Why FOR UPDATE SKIP LOCKED is important
When multiple dispatchers run simultaneously (e.g., after a deploy), a race condition can occur — both pick the same post. FOR UPDATE SKIP LOCKED in PostgreSQL locks only the selected rows and skips the rest. This guarantees idempotency — each post is processed exactly once, with no duplicates.
| Platform | Limit |
|---|---|
| Instagram Graph API | 25 posts/day per account |
| VK | 50 posts/day per community |
| Telegram Bot | ~30 messages/sec per bot |
| Facebook Page | No hard limit, soft throttle |
Smart post scheduling
When the smart_schedule option is enabled, the system analyzes all pending posts without a specific time over the next 7 days, calculates available slots considering already scheduled ones, and evenly distributes the load. For an online store with a catalog of 500+ items, this means that after importing new products, posts are not published in a landslide — they are spread over a week, increasing engagement.
Time windows for publishing
Per-channel settings define when publishing is allowed. Example config:
{ "vk": { "allowed_hours": [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], "allowed_days": [1, 2, 3, 4, 5, 6, 7], "min_interval_minutes": 30 }, "telegram": { "allowed_hours": [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], "allowed_days": [1, 2, 3, 4, 5, 6, 7], "min_interval_minutes": 15 } } If a post is scheduled for non-working hours, the dispatcher shifts it to the nearest allowed window. This is useful for B2B content that shouldn't be published at night.
What's included in the work
- Scheduler module: SQL model, dispatcher, rate limiting (leaky bucket), time windows.
- REST API for managing posts (create, reschedule, cancel).
- CMS interface — queue table, calendar view with drag-and-drop, sent history.
- API documentation and editor instructions.
- Training your team on using the scheduler.
- 90-day warranty on bug fixes after delivery.
- Access to source code and deployment scripts.
- Post-launch support for 2 weeks.
Timeline estimates
| Component | Timeline | Price (USD) |
|---|---|---|
| Basic scheduler (2 channels) | 6–8 working days | $1,500 |
| Smart scheduling + calendar | +3–5 days | $800 |
| Rate limiting for all platforms | +1–2 days | $400 |
Using the scheduler (step-by-step)
- Install the scheduler module into your CMS or as a standalone service.
- Configure each social channel: API keys, rate limits, time windows.
- Create posts via the admin interface or REST API, setting scheduled times.
- Optionally enable smart scheduling to auto-distribute posts evenly.
- Monitor the queue and calendar; retry or cancel as needed.
Data model (core system)
CREATE TABLE scheduled_posts ( id SERIAL PRIMARY KEY, source_type VARCHAR(50), -- 'product', 'promotion', 'article', 'manual' source_id INTEGER, channel VARCHAR(30), -- 'vk', 'telegram', 'instagram', 'ok' scheduled_at TIMESTAMP NOT NULL, status VARCHAR(20) DEFAULT 'pending', -- pending|processing|sent|failed|cancelled attempts SMALLINT DEFAULT 0, last_error TEXT, external_post_id VARCHAR(100), -- post ID on the platform after publication content JSONB, -- serialized content (text, media, links) created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_scheduled_posts_fire ON scheduled_posts (scheduled_at, status) WHERE status = 'pending'; Dispatcher
Runs every minute via cron or a daemon with a sleep-loop:
def dispatch_pending_posts(): now = datetime.utcnow() posts = db.query(""" SELECT * FROM scheduled_posts WHERE status = 'pending' AND scheduled_at <= %s ORDER BY scheduled_at ASC LIMIT 50 FOR UPDATE SKIP LOCKED """, [now]) for post in posts: db.execute("UPDATE scheduled_posts SET status='processing' WHERE id=%s", [post.id]) enqueue_post_job(post) We guarantee that the system does not lose posts during failures: Redis and PostgreSQL ensure data integrity. For high loads, we use Redis as a distributed counter cache with atomic operations and concurrency control.
Order the development of a scheduler and automate your publications. Get a consultation on implementing a scheduler for your website — we'll assess the project and offer a turnkey solution.







