URL: https://mkdocs.justinsforge.com/memory/general/reference_social_media_apis/
Social Media API Reference¶
Covers YouTube Data API v3, Instagram Graph API, and TikTok Content Posting API as they apply to JWVR (YouTube creator), Nova Design (Shopify/social brand), and Gus Outdoor Co (fishing brand). Written 2026-04-30.
Hard Rule: Draft Only, Never Auto-Post¶
Forge never publishes to any social platform automatically. Every workflow stops at a draft or staged state. Justin reviews and posts manually.
| Platform | Draft state | What Forge does | What Justin does |
|---|---|---|---|
| YouTube | Upload as privacyStatus=private, publishAt unset |
Upload file, set title/description/tags/thumbnail, leave private | Reviews in YouTube Studio, sets to Public or Scheduled |
Create media container (published=false), do NOT call media_publish |
Prepare caption, hashtags, media URL, create container | Opens Creator Studio, reviews container, taps Publish | |
| TikTok | Use video.upload scope (inbox mode), NOT video.publish |
Upload to TikTok draft inbox | Reviews in TikTok app, edits if needed, publishes |
No Forge script, n8n workflow, or cron job calls a publish/go-live endpoint on any platform. Any code that would do so must be blocked at the design level, not just commented out.
1. YouTube Data API v3¶
1.1 What can be automated¶
| Operation | API method | Quota cost |
|---|---|---|
| Upload video | videos.insert |
100 units |
| Update video metadata (title, description, tags, category) | videos.update |
50 units |
Schedule video (set status.publishAt + status.privacyStatus=private) |
videos.update or during insert |
50 units |
| Set thumbnail | thumbnails.set |
50 units |
| Set video privacy (public/private/unlisted) | videos.update |
50 units |
| Create/update/delete playlist | playlists.insert/update/delete |
50 units each |
| Add video to playlist | playlistItems.insert |
50 units |
| Fetch video metadata (title, views, likes, comments count, status) | videos.list |
1 unit |
| Fetch channel info | channels.list |
1 unit |
| Fetch playlist items | playlistItems.list |
1 unit |
| Search videos | search.list |
100 units |
| List, insert, update, delete comments | comments.* |
1 unit read / 50 units write |
| Upload captions | captions.insert |
400 units |
| Update captions | captions.update |
450 units |
| Set watermarks | watermarks.set/unset |
50 units each |
| Get video rating | videos.getRating |
1 unit |
Scheduling mechanics: Set status.privacyStatus=private and status.publishAt to an ISO 8601 future timestamp during videos.insert or videos.update. YouTube flips public at that time automatically. No processing delay at go-live. If publishAt is in the past the video publishes immediately.
Analytics: Channel and video performance data lives in a separate API: YouTube Analytics API v2 (not Data API v3). It provides views, watch time, revenue, audience retention, traffic sources, demographics. Access via youtubeAnalytics.reports.query. Also separate: YouTube Reporting API for bulk export jobs (CSV). Both are free, both require OAuth.
1.2 OAuth 2.0 scopes¶
All scopes resolve under https://www.googleapis.com/auth/:
| Scope (shorthand) | Full URI | Grants |
|---|---|---|
youtube |
...auth/youtube |
Full account management |
youtube.force-ssl |
...auth/youtube.force-ssl |
Read/write/delete videos, ratings, comments, captions (most operations) |
youtube.readonly |
...auth/youtube.readonly |
Read-only view of account |
youtube.upload |
...auth/youtube.upload |
Upload videos only |
youtubepartner |
...auth/youtubepartner |
Partner assets and associated content |
youtubepartner-channel-audit |
...auth/youtubepartner-channel-audit |
Private channel audit data (partner program only) |
Minimum viable scopes for JWVR pipeline:
- Upload + schedule: youtube.upload + youtube.force-ssl
- Analytics: add youtube.readonly for Data API metadata reads; enable YouTube Analytics API separately under same credentials
- Comment moderation: youtube.force-ssl
Auth flow: Standard OAuth 2.0 with refresh token. Server-side flow (authorization code + PKCE). Tokens stored in ~/.forge-secrets/youtube.env. Refresh tokens do not expire unless revoked or unused for 6 months.
1.3 Quota system¶
- Default: 10,000 units per day per Google Cloud project
- Resets at midnight Pacific Time
- Quota is per project, not per API key. Multiple keys in one project share the pool.
- Every request (including invalid ones) costs at least 1 unit.
search.listis the quota killer at 100 units. Avoid using it for known channel IDs; usevideos.listwith explicit IDs instead (1 unit).- Quota increase: submit the YouTube API Services Audit and Quota Extension Form. Approval requires demonstrated usage and policy compliance.
Budget math for a JWVR upload-and-schedule workflow (per video):
- 1x videos.insert = 100 units
- 1x thumbnails.set = 50 units
- 1x playlistItems.insert = 50 units
- 1x videos.update (description tweak) = 50 units
- Total per publish: ~250 units = ~40 publishes before quota exhaustion
At 2 uploads/week that is well within budget. Analytics polling (1 unit per videos.list call) adds negligible cost.
1.4 Rate limits¶
Beyond daily quota, YouTube applies a concurrent request cap ("Queries per minute per user") that cannot be increased via the standard form. In practice this only bites batch scrapers. For Forge's single-channel use case it is not a concern.
1.5 What CANNOT be done via API¶
These features are Studio-only or simply not exposed:
| Feature | Status |
|---|---|
| Community posts (create/read/delete) | Not available via any official API endpoint |
| Enable/disable monetization on a video | Not available. VideoMonetizationDetails is readable in some partner contexts but not settable |
| Set ad formats (skippable, bumper, etc.) | Not available |
| Super Thanks | Not available |
| Channel membership management (pricing, perks) | Member resource is readable; membership configuration not writable |
| Premiere scheduling | Not available. Upload + publishAt creates a scheduled video, not a Premiere with countdown |
| Live stream scheduling | Use YouTube Live Streaming API (separate API), not Data API v3 |
| YouTube Shorts-specific metadata | Shorts are uploaded as standard videos; the short format is inferred from aspect ratio and duration |
| End screens / Cards | Not available |
| Channel sections (homepage layout) | Not available |
2. Instagram Graph API¶
2.1 Account requirements¶
- Business or Creator account required. Personal accounts cannot use the Graph API for publishing or insights.
- Must be connected to a Facebook Page (Business Login path) or use Instagram Login (new Business Login API introduced July 2024).
- Basic Display API was shut down December 4, 2024. Any legacy integration using it is dead. There is no longer a separate "personal account read" API.
For Nova Design and Gus Outdoor Co: Both brands should operate Instagram Business accounts. This is a prerequisite for all automatable actions.
2.2 Content publishing capabilities¶
| Content type | Supported | Notes |
|---|---|---|
| Single image (JPEG) | Yes | JPEG only; PNG not supported |
| Single video | Yes | Up to 90 seconds; some accounts capped at 60s depending on rollout phase |
| Reels | Yes | media_type=REELS; portrait (9:16) recommended; up to 90s |
| Carousel (up to 10 items) | Yes | Mix of images and videos; counts as 1 post against rate limit |
| Stories (image or video) | Yes | Business accounts only |
| Text-only posts | No | Not supported |
| Shopping product tags | No | Not supported via API |
Media hosting requirement: Media files must be on publicly accessible HTTPS servers at the time of container creation. URLs cannot be local. Pattern: upload to Forge's rclone-mounted Google Drive public share or a CDN, then pass the URL to the API.
2.3 Scheduling¶
Native scheduling is supported. Workflow:
- POST to
/{ig-user-id}/mediawithpublished=falseandscheduled_publish_time=<unix_timestamp>. Window: 10 minutes to 30 days in the future. - The endpoint returns a container ID.
- At the scheduled time, Meta publishes automatically.
To publish immediately instead: POST to /{ig-user-id}/media_publish with the container ID (omit scheduled_publish_time).
2.4 Analytics endpoints¶
Account-level (GET /{ig-user-id}/insights):
- Reach, impressions, profile views, follower count, online followers by hour
- Period options: day, week, month, lifetime
- Data retained for up to 90 days
Post/media-level (GET /{ig-media-id}/insights):
- Reach, impressions, engagement (likes + comments + saves), video views (Reels only), saves, shares
- Period: lifetime only for media insights
Deprecated as of January 8, 2025 (Graph API v21+):
- video_views for non-Reels content
- email_contacts, website_clicks, phone_call_clicks, text_message_clicks (time series)
- profile_views (time series)
Check rate limit headroom: GET /{ig-user-id}/content_publishing_limit returns current 24-hour post count.
2.5 OAuth scopes and token types¶
For Instagram Business Login (new July 2024 path, simpler):
- instagram_business_basic
- instagram_business_content_publish
- instagram_business_manage_comments
- instagram_business_manage_messages
- instagram_business_manage_insights (requires Meta app review)
For Facebook Login for Business (older, more complex but deeper integration):
- instagram_basic
- instagram_content_publish
- instagram_manage_comments
- instagram_manage_insights
- pages_read_engagement
- pages_show_list
Token types: - Short-lived user access token: ~1 hour - Long-lived user access token: 60 days (exchange short-lived via server-side call with app secret) - System user access token: never expires (Meta Business Suite, recommended for production automations)
Recommended for Forge: System user access token stored in ~/.forge-secrets/instagram.env per brand. Requires Meta Business Manager setup. Token never expires, no refresh dance.
2.6 Rate limits¶
| Limit | Value |
|---|---|
| API requests | 200 requests per hour per Instagram account |
| Published posts | 100 API-published posts per 24-hour moving window (some sources show 25 for feed posts specifically; the conservative limit to plan against is 25 feed posts/day) |
| Container creation | Multiple containers can exist; only published posts count against the cap |
| Rate limit check | GET /{ig-user-id}/content_publishing_limit |
HTTP 429 returned when limits exceeded.
3. TikTok Content Posting API¶
3.1 Account and developer requirements¶
- Personal accounts are not eligible. You must be registered as a business or developer entity on TikTok for Developers.
- An unaudited API client can upload content, but all posts are restricted to private viewing. To publish publicly, the app must pass TikTok's audit.
- Audit process: Submit app for production review with a demo video, privacy policy URL, and data handling description. Timeline: 5-10 business days. Fastest approvals (3-5 days) go to clear use cases with complete documentation.
3.2 Direct Post vs. Upload-to-Inbox¶
| Mode | Behavior | Use case |
|---|---|---|
Direct Post (video.publish scope) |
Posts immediately (or queued to Forge cron for scheduled publish) | Marketing automation, scheduled brand posts |
Upload to Inbox (video.upload scope) |
Content lands in creator's TikTok draft inbox for manual review before publishing | Creator-review workflows, sensitive content |
TikTok's API does not support native future-timestamp scheduling. To schedule a post for a specific time, implement a cron job in Forge that triggers the Direct Post flow at the target time. This is the standard pattern.
3.3 Required OAuth scopes¶
| Scope | What it does | Special approval needed? |
|---|---|---|
user.info.basic |
Read avatar and display name | No |
user.info.stats |
Read follower count, likes, video count | No |
video.upload |
Upload to creator draft inbox | Requires production approval |
video.publish |
Direct post to profile | Requires production approval |
video.list |
Read public video metadata | No |
research.data.basic |
Access public data for research | Requires vetted researcher status |
Both video.upload and video.publish require app-level approval from TikTok before users can authorize them.
3.4 Video specifications¶
| Parameter | Requirement |
|---|---|
| Format | MP4 (H.264 recommended), WebM |
| Resolution | Minimum 540x960px; recommended 1080x1920px (9:16 portrait) |
| File size | 10GB max (128MB in sandbox) |
| Duration | Up to 10 minutes for established accounts; 60 seconds for new/low-trust accounts; max API-reported cap is 300 seconds |
| Audio | AAC stereo, 44.1-48 kHz |
| Caption | 2,200 characters max |
| Hashtags | Up to 30 per post |
3.5 Rate limits¶
| Endpoint type | Limit |
|---|---|
Post initialization (/v2/post/publish/video/init/) |
6 requests per minute per access_token |
Status check (/v2/post/publish/status/fetch/) |
30 requests per minute per access_token |
| General query endpoints (user info, video query, video list) | 600 requests per minute per token |
| Daily upload cap | ~15 posts per day per creator account (shared across all API clients using that account) |
HTTP 429 returned on breach. Headers X-RateLimit-Remaining and X-RateLimit-Reset are available for backoff logic.
3.6 Analytics¶
TikTok's native analytics (views, likes, comments, shares, follower growth) are accessible via the Research API and the Video Query API but require separate scope approvals. For day-to-day Forge use, video.list + user.info.stats provides enough signal for a post-performance tracker without the Research API approval hurdle.
4. Forge Integration Approach¶
4.1 YouTube (JWVR channel)¶
Recommended: Direct Python client, with n8n for trigger/notify only.
Rationale: YouTube OAuth uses server-side refresh tokens stored in ~/.forge-secrets/youtube.env. The Forge Python pattern (forge_google_calendar.py, forge_gdrive_*.py) already handles Google OAuth directly. Adding a forge_youtube_client.py follows the same pattern. n8n has a YouTube node but it does not expose the full videos.insert metadata surface needed for scheduling, thumbnail setting, and playlist assignment in one call.
Workflow design (draft-only):
Telegram coordinator bot
→ /stage-youtube skill or coordinator intent
→ forge_youtube_client.py
video.insert (upload as privacyStatus=private, no publishAt)
thumbnails.set
playlistItems.insert (to target playlist)
→ Telegram notify: "Draft ready in YouTube Studio: <title>"
→ analytics poller (cron, daily)
youtube_analytics_report.query (views, watch time, revenue)
→ write to Notion JWVR analytics DB
What Forge never does: sets status.privacyStatus=public or sets status.publishAt. Justin opens YouTube Studio, reviews the private upload, then schedules or publishes manually.
Auth storage: ~/.forge-secrets/youtube.env with keys YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET, YOUTUBE_REFRESH_TOKEN. Refresh token is per-channel, generated via one-time OAuth consent flow.
n8n role (optional): Trigger upload workflow from n8n schedule node; call scripts/n8n/call.sh youtube-publish <payload> webhook pattern to kick off Python script. Keeps scheduling centralized in n8n but execution in Python.
Quota strategy: One GCP project per Forge (not per brand). With JWVR's current volume (1-2 uploads/week), 10,000 units/day is ample. No quota increase request needed initially. Do not use search.list in any automated poller; use explicit video ID lookups.
4.2 Instagram (Nova Design + Gus Outdoor Co)¶
Recommended: n8n workflow for posting, Python client for analytics pulls.
Rationale: n8n has a mature Instagram node that handles OAuth token management, media container creation, and publish calls. The multi-step container-then-publish flow maps naturally to n8n's node chain. For analytics (which require custom date range queries), a lightweight Python client is more flexible than n8n expression gymnastics.
Staging workflow (n8n, draft-only):
n8n Webhook trigger (from coordinator bot)
→ HTTP Request: create media container (/{ig-user-id}/media)
params: image_url/video_url, caption (no scheduled_publish_time)
published=false
→ Wait for container processing (polling loop)
→ Telegram notify: "Instagram container ready for review: <container_id>"
[STOP. Do NOT call media_publish.]
Justin receives the Telegram notification and publishes from Creator Studio or the app. Tag each n8n workflow forge + stage. Name convention: stage-instagram-photo, stage-instagram-reel.
Note: Instagram media containers without a published call expire after 24 hours. Forge must create the container only when Justin is ready to review and publish within that window, not as a pre-schedule days in advance.
Analytics workflow (Python):
cron (weekly, Sunday)
→ forge_instagram_analytics.py
GET /{ig-user-id}/media → list recent post IDs
GET /{media-id}/insights for each
write summary to Notion brand analytics DB or daily log
Auth storage: One env file per brand. ~/.forge-secrets/instagram-nova.env and ~/.forge-secrets/instagram-gus.env. Both store INSTAGRAM_SYSTEM_USER_TOKEN (non-expiring system user token from Meta Business Manager) plus INSTAGRAM_USER_ID.
Media hosting: Upload source images/videos to Google Drive public share via forge_gdrive_write.py, get public URL, pass to Instagram container creation. Avoids any local server requirement.
Rate limit guard: Add a check against /{ig-user-id}/content_publishing_limit at the top of every n8n post workflow. If quota_usage >= 20, send Telegram warn and abort. This avoids hitting the 25/day cap silently.
4.3 TikTok (Nova Design + Gus Outdoor Co, future)¶
Recommended: Python client only. No n8n.
Rationale: TikTok's posting flow requires a multi-step chunked upload protocol with status polling. n8n's HTTP Request node can handle it but the error handling and retry logic for a 10-step upload chain is painful in n8n. A dedicated forge_tiktok_client.py wraps the full init-upload-check-publish cycle cleanly.
Staging workflow (draft-only):
Telegram coordinator
→ forge_tiktok_client.py
POST /v2/post/publish/video/init/ (chunk_size mode, upload_url)
PUT <upload_url> (chunked video upload)
GET /v2/post/publish/status/fetch/ (poll until SEND_TO_USER_INBOX status)
→ Telegram notify: "TikTok draft in your inbox: <title>"
Uses video.upload scope (inbox/draft mode), not video.publish. Content lands in Justin's TikTok draft inbox. He reviews in the TikTok app and publishes manually. Forge never calls the publish endpoint.
Auth storage: ~/.forge-secrets/tiktok.env with TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET, TIKTOK_ACCESS_TOKEN, TIKTOK_REFRESH_TOKEN. TikTok access tokens expire per their OAuth 2.0 spec; the client must handle refresh before each request.
Prerequisite: Developer account registration + production app approval for video.publish scope must happen before this is buildable. Estimated lead time: 1-2 weeks including review.
n8n role: Receive a Telegram or scheduler trigger, call a webhook that invokes forge_tiktok_client.py via the standard scripts/n8n/call.sh outbound pattern. Same pattern as other Forge Python clients triggered from n8n.
4.4 Summary recommendation table¶
| Platform | Forge action | Draft state | Auth pattern |
|---|---|---|---|
| YouTube (JWVR) | Python client: upload + metadata | Private upload in YouTube Studio | OAuth refresh token in ~/.forge-secrets/youtube.env |
| Instagram (Nova Design) | n8n: create container only | Unpublished container (24h window) | System user token in ~/.forge-secrets/instagram-nova.env |
| Instagram (Gus Outdoor Co) | n8n: create container only | Unpublished container (24h window) | System user token in ~/.forge-secrets/instagram-gus.env |
| TikTok (Nova/Gus) | Python client: upload to inbox | TikTok draft inbox | OAuth refresh token in ~/.forge-secrets/tiktok.env |
Forge stops before any publish/go-live API call on every platform. Justin publishes from the native app or studio.
4.5 Build sequence (recommended order)¶
- YouTube client - fewest approval hurdles, biggest JWVR impact. GCP project already exists (reuse for social APIs). OAuth consent screen setup, scope request, one-time token generation.
- Instagram posting (n8n) - Meta Business Manager setup, system user token generation, n8n workflow for one brand (Nova Design), clone for Gus Outdoor Co.
- Instagram analytics (Python) - bolt-on after posting is stable.
- TikTok - requires developer registration and 5-10 day audit wait. Start the approval process while building YouTube and Instagram, then implement once approved.
5. Quick Reference: Limits at a Glance¶
| Platform | Daily post limit | Requests/hour | Scheduling | Analytics |
|---|---|---|---|---|
| YouTube | No hard per-day limit; 10,000 quota units/day total | Not published separately; project quota governs | Native (publishAt timestamp) |
Separate API (YouTube Analytics API v2) |
| 25-100 feed posts / 24h | 200 requests/hour per account | Native (scheduled_publish_time UNIX timestamp, 10min-30 days out) |
Built into Graph API (/insights endpoints) |
|
| TikTok | ~15 posts/day per creator account | 6 init requests/min; 600 query requests/min | Not native; implement cron layer | Research API (requires approval); basic stats via user.info.stats |
[Claude Code]