logicspike/docs

Learning

Facebook Graph API — Complete Mastery Guide

Last Updated: 2026-03-28 Status: Active

This guide covers everything you need to master the Facebook Graph API — from foundational concepts to advanced publishing workflows for Facebook Pages and Instagram. It is written for developers building social media automation tools (like the LogicSpike Content Engine).


1. What is the Graph API?

The Facebook Graph API is the primary interface for reading and writing data to Meta's platforms (Facebook, Instagram, Messenger, WhatsApp). It models the entire social platform as a graph — a collection of objects (nodes) connected by relationships (edges).

1.1 Base URL

Every Graph API request hits this base:

https://graph.facebook.com/v25.0/

The v25.0 is the version identifier. Meta releases new versions roughly every 4 months. Each version is supported for approximately 2 years before deprecation.

IMPORTANT

Always pin your API calls to a specific version. Unversioned calls default to the oldest supported version — which may be deprecated soon.

1.2 The Three Building Blocks

The entire Graph API is built on three primitives:

Concept What It Is Example
Node An individual object in the graph A User (/me), a Page (/123456), a Photo (/789)
Edge A connection between two nodes A Page's feed (/123456/feed), a User's accounts (/me/accounts)
Field A property of a node name, id, created_time, message

1.3 Anatomy of a Request

Every Graph API call follows this pattern:

GET https://graph.facebook.com/v25.0/{node-id}?fields={field1},{field2}&access_token={token}

Breaking it down:

Part Purpose Example
v25.0 API version Always use a specific version
{node-id} The object you are querying me, 123456789, a Page ID
fields Explicitly request only the data you need name,id,fan_count
access_token Proves who you are and what you can do A User, Page, or App token

Example — Get your own profile name and ID:

curl -X GET "https://graph.facebook.com/v25.0/me?fields=id,name&access_token=EAABs..."

Response:

{
  "id": "10229876543210",
  "name": "Dipanshu Vishwakarma"
}

1.4 HTTP Methods

Method Purpose Example
GET Read data Fetch a Page's info, list posts
POST Create or update data Publish a post, upload a photo
DELETE Remove data Delete a post or comment

2. Access Tokens — The Key to Everything

Access tokens are the most critical concept in the Graph API. Every request requires one. They determine who is making the request and what they can access.

2.1 Types of Access Tokens

Token Type Who Uses It Lifespan How to Get It
User Access Token Acts on behalf of a user Short-lived: ~1 hour; Long-lived: ~60 days Facebook Login (OAuth 2.0)
Page Access Token Acts on behalf of a Facebook Page Inherits from user token; can be made "never-expiring" Exchange from User Token via /me/accounts
App Access Token Acts on behalf of your app itself Never expires (until secret is reset) GET /oauth/access_token?client_id={id}&client_secret={secret}&grant_type=client_credentials
System User Token Automated server-to-server actions Long-lived Created in Meta Business Manager

2.2 Getting a User Access Token (OAuth 2.0 Flow)

This is the standard flow when a user clicks "Connect Facebook" in your app.

Step 1 — Build the Authorization URL:

https://www.facebook.com/v25.0/dialog/oauth?
  client_id={YOUR_APP_ID}
  &redirect_uri={YOUR_CALLBACK_URL}
  &scope=pages_show_list,pages_manage_posts,instagram_basic,instagram_content_publish
  &state={CSRF_TOKEN}
Parameter Purpose
client_id Your Facebook App ID
redirect_uri Where Meta sends the user back (must match your app settings exactly)
scope Comma-separated list of permissions you are requesting
state A random string you generate to prevent CSRF attacks — verify it on callback

Step 2 — Exchange the Code for a Token:

curl -X GET "https://graph.facebook.com/v25.0/oauth/access_token?\
  client_id={APP_ID}&\
  redirect_uri={CALLBACK_URL}&\
  client_secret={APP_SECRET}&\
  code={CODE_FROM_STEP_1}"

Response:

{
  "access_token": "EAABs...short_lived_token",
  "token_type": "bearer",
  "expires_in": 3600
}

Step 3 — Exchange for a Long-Lived Token:

curl -X GET "https://graph.facebook.com/v25.0/oauth/access_token?\
  grant_type=fb_exchange_token&\
  client_id={APP_ID}&\
  client_secret={APP_SECRET}&\
  fb_exchange_token={SHORT_LIVED_TOKEN}"

Response:

{
  "access_token": "EAABs...long_lived_token",
  "token_type": "bearer",
  "expires_in": 5184000
}

TIP

The long-lived token lasts ~60 days. Store it securely in your database (encrypted). Set up a cron job to refresh it before expiration.

2.3 Getting a Page Access Token

Once you have a User Access Token, you can fetch Page tokens for all Pages the user manages:

curl -X GET "https://graph.facebook.com/v25.0/me/accounts?\
  fields=id,name,access_token&\
  access_token={USER_ACCESS_TOKEN}"

Response:

{
  "data": [
    {
      "id": "108234567890123",
      "name": "My Business Page",
      "access_token": "EAABs...page_access_token"
    }
  ]
}

IMPORTANT

If you obtained the User Access Token as a long-lived token, the Page Access Token returned here is never-expiring. This is the token you save for automated posting.

2.4 Debugging Tokens

Use the Debug Token endpoint to inspect any token's metadata:

curl -X GET "https://graph.facebook.com/debug_token?\
  input_token={TOKEN_TO_INSPECT}&\
  access_token={APP_ID}|{APP_SECRET}"

Response (key fields):

{
  "data": {
    "app_id": "123456789",
    "type": "PAGE",
    "is_valid": true,
    "expires_at": 0,
    "scopes": ["pages_show_list", "pages_manage_posts"]
  }
}
Field Meaning
is_valid Whether the token is still active
expires_at 0 means never-expiring
scopes The permissions granted to this token
type USER, PAGE, or APP

3. Permissions (Scopes) — What You Can Access

Permissions control what data your app can read or write. They are requested during the OAuth flow via the scope parameter.

3.1 Permission Access Levels

Level Who Can Use It Requires App Review?
Standard Access Only users with a role on your app (Admin, Developer, Tester) No
Advanced Access Any Facebook user (general public) Yes

WARNING

In Development Mode, your app only has Standard Access. Only role-holders can use it. You must pass App Review and switch to Live Mode for public use.

3.2 Key Permissions for Content Publishing

Permission What It Grants Required For
pages_show_list List Pages the user manages Getting Page IDs
pages_read_engagement Read Page engagement metrics Reading Page info
pages_manage_posts Create, edit, delete posts on a Page Publishing to Facebook Pages
pages_manage_metadata Manage Page metadata Updating Page settings
instagram_basic Read Instagram profile info Getting IG User ID
instagram_content_publish Publish content to Instagram Posting images, videos, reels, carousels
instagram_manage_comments Read and manage IG comments Comment moderation
instagram_manage_insights Read IG analytics Performance tracking
business_management Manage Business Manager assets Advanced business operations
publish_video Publish videos to Pages Uploading videos and reels to Facebook

3.3 How the scope Parameter Works

When building your OAuth URL, you list the permissions you need:

scope=pages_show_list,pages_manage_posts,instagram_basic,instagram_content_publish

The user sees a consent screen listing each permission. They can grant or deny individual scopes. Always check which scopes were actually granted:

curl -X GET "https://graph.facebook.com/v25.0/me/permissions?\
  access_token={USER_TOKEN}"

Response:

{
  "data": [
    { "permission": "pages_show_list", "status": "granted" },
    { "permission": "pages_manage_posts", "status": "granted" },
    { "permission": "instagram_basic", "status": "declined" }
  ]
}

CAUTION

Never assume all scopes were granted. Always verify. If a required permission is declined, prompt the user to re-authorize with that specific scope.


4. Publishing to Facebook Pages

This section covers every type of content you can publish to a Facebook Page.

4.1 Publish a Text Post

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/feed" \
  -F "message=Hello from the Graph API! 🚀" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Response:

{
  "id": "108234567890123_456789012345678"
}

The returned id is the Post ID — format is {page-id}_{post-id}.

4.2 Publish a Link Post

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/feed" \
  -F "message=Check out our latest blog post!" \
  -F "link=https://logicspike.com/blog/my-article" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Facebook automatically scrapes the link for an Open Graph preview (title, description, image).

4.3 Publish a Photo Post

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/photos" \
  -F "caption=Our new product launch! 📸" \
  -F "url=https://example.com/photo.jpg" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
Parameter Description
caption The text accompanying the photo
url A publicly accessible URL to the image
source Alternative: upload the file directly (multipart/form-data)
published true (default) to post immediately, false for draft
scheduled_publish_time Unix timestamp for scheduled posting (requires published=false)

4.4 Publish a Video

Video publishing uses the Resumable Upload API — a multi-step process:

Step 1 — Initialize the upload session:

curl -X POST "https://graph.facebook.com/v25.0/{app-id}/uploads" \
  -F "file_type=video/mp4" \
  -F "file_length=15728640" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Response:

{
  "id": "upload:MTphdHRhY2htZW50..."
}

Step 2 — Upload the video file:

curl -X POST "https://graph.facebook.com/v25.0/upload:{session_id}" \
  --header "Authorization: OAuth {PAGE_ACCESS_TOKEN}" \
  --header "file_offset: 0" \
  --data-binary @video.mp4

Step 3 — Publish to the Page:

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/videos" \
  -F "title=My Video Title" \
  -F "description=A great video description" \
  -F "fbuploader_video_file_chunk=upload:{session_id}" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

4.5 Publish a Reel (Facebook)

Reels use a dedicated endpoint with a 3-phase upload:

Phase 1 — Initialize:

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/video_reels" \
  -F "upload_phase=start" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Response:

{
  "video_id": "123456789",
  "upload_url": "https://rupload.facebook.com/video-upload/..."
}

Phase 2 — Upload the video file:

curl --location "$UPLOAD_URL" \
  --header "Authorization: OAuth {PAGE_ACCESS_TOKEN}" \
  --header "offset: 0" \
  --header "file_size: {FILE_SIZE_BYTES}" \
  --data-binary @reel_video.mp4

Phase 3 — Publish:

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/video_reels" \
  -F "upload_phase=finish" \
  -F "video_id=123456789" \
  -F "title=My First Reel" \
  -F "description=Check this out! #trending" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Reel Specifications:

Spec Requirement
Aspect Ratio 9:16 (recommended)
Resolution 1080x1920
Duration 3–90 seconds
Format .mp4 or .mov (H.264 codec)
Rate Limit 30 reels per 24-hour window

4.6 Scheduled Posts

Any post type supports scheduling by adding these parameters:

curl -X POST "https://graph.facebook.com/v25.0/{page-id}/feed" \
  -F "message=This post goes live tomorrow at 9 AM!" \
  -F "published=false" \
  -F "scheduled_publish_time=1711612800" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
Parameter Description
published Must be false for scheduled posts
scheduled_publish_time Unix timestamp (must be 10 min to 75 days in the future)

5. Publishing to Instagram

Instagram publishing uses a container-based workflow: you first create a media container, then publish it.

IMPORTANT

Instagram API publishing only works with Business or Creator accounts connected to a Facebook Page. Personal accounts cannot use the publishing API.

5.1 Getting the Instagram User ID

First, find the Instagram account linked to a Facebook Page:

curl -X GET "https://graph.facebook.com/v25.0/{page-id}?\
  fields=instagram_business_account&\
  access_token={PAGE_ACCESS_TOKEN}"

Response:

{
  "instagram_business_account": {
    "id": "17841400123456789"
  },
  "id": "108234567890123"
}

The instagram_business_account.id is your IG User ID — used in all Instagram API calls.

5.2 Publish a Single Image

Step 1 — Create the container:

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "image_url=https://example.com/photo.jpg" \
  -F "caption=Beautiful sunset! 🌅 #photography" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Response:

{
  "id": "17889455123456789"
}

Step 2 — Publish the container:

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media_publish" \
  -F "creation_id=17889455123456789" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
Parameter Description
image_url Must be a publicly accessible URL (Instagram's servers download it)
caption Post text (supports hashtags and mentions)
alt_text Accessibility description for the image
location_id Optional Facebook Place ID for location tagging

5.3 Publish a Video (Feed Post)

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "video_url=https://example.com/video.mp4" \
  -F "media_type=VIDEO" \
  -F "caption=Check out this video! 🎬" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Then publish with /media_publish as in Step 2 above.

NOTE

Video processing takes time. Check the container status before publishing:

GET /{container-id}?fields=status_code

Wait until status_code is FINISHED before calling /media_publish.

5.4 Publish a Reel (Instagram)

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "video_url=https://example.com/reel.mp4" \
  -F "media_type=REELS" \
  -F "caption=My first API reel! 🎥 #reels" \
  -F "cover_url=https://example.com/cover.jpg" \
  -F "share_to_feed=true" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
Parameter Description
media_type Must be REELS
video_url Publicly accessible video URL
cover_url Optional custom cover image
share_to_feed true to also show on the feed grid

Reel Specs:

Spec Requirement
Aspect Ratio 9:16
Duration 3–90 seconds
Format .mp4 (H.264 codec, AAC audio)
Max File Size 1 GB

Carousels require creating individual child containers first, then a parent container:

Step 1 — Create child containers (up to 10):

# Image child
curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "image_url=https://example.com/slide1.jpg" \
  -F "is_carousel_item=true" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
# Response: { "id": "CHILD_1_ID" }
 
# Video child
curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "video_url=https://example.com/slide2.mp4" \
  -F "media_type=VIDEO" \
  -F "is_carousel_item=true" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
# Response: { "id": "CHILD_2_ID" }

Step 2 — Create the parent carousel container:

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "media_type=CAROUSEL" \
  -F "caption=Swipe through our gallery! ➡️" \
  -F "children=CHILD_1_ID,CHILD_2_ID" \
  -F "access_token={PAGE_ACCESS_TOKEN}"
# Response: { "id": "CAROUSEL_CONTAINER_ID" }

Step 3 — Publish:

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media_publish" \
  -F "creation_id=CAROUSEL_CONTAINER_ID" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

TIP

Carousels count as a single post toward the 100-post daily rate limit.

5.6 Publish a Story

curl -X POST "https://graph.facebook.com/v25.0/{ig-user-id}/media" \
  -F "image_url=https://example.com/story.jpg" \
  -F "media_type=STORIES" \
  -F "access_token={PAGE_ACCESS_TOKEN}"

Then publish with /media_publish. Stories expire after 24 hours. Interactive stickers (polls, links) are not supported via API.

5.7 Container Status Codes

Always check container status before publishing, especially for videos:

curl -X GET "https://graph.facebook.com/v25.0/{container-id}?\
  fields=status_code,status&\
  access_token={PAGE_ACCESS_TOKEN}"
Status Code Meaning
FINISHED Ready to publish
IN_PROGRESS Still processing (wait and retry)
ERROR Something went wrong — check status for details
EXPIRED Container expired (24-hour TTL) — recreate it

6. Reading Data — Common Query Patterns

6.1 Field Expansion (Nested Queries)

Request nested data in a single call:

curl -X GET "https://graph.facebook.com/v25.0/{page-id}?\
  fields=name,fan_count,posts{message,created_time,likes.summary(true)}&\
  access_token={PAGE_ACCESS_TOKEN}"

This returns the Page info AND its recent posts with like counts — all in one request.

6.2 Pagination

Large result sets are paginated. The response includes a paging object:

{
  "data": [...],
  "paging": {
    "cursors": {
      "before": "QVFIu...",
      "after": "QVFIu..."
    },
    "next": "https://graph.facebook.com/v25.0/.../feed?after=QVFIu..."
  }
}
Pagination Type Parameters Best For
Cursor-based after, before Most endpoints (recommended)
Time-based since, until (Unix timestamps) Chronological data (posts, comments)
Offset-based offset, limit Legacy — avoid when possible

TIP

Always use the next URL from the response rather than manually constructing pagination URLs. When next is absent, you have reached the end.

6.3 The /me Endpoint

/me is an alias for the currently authenticated user or Page:

# With a User Token → returns user info
GET /v25.0/me?fields=id,name
 
# With a Page Token → returns Page info
GET /v25.0/me?fields=id,name,fan_count

7. Batch Requests

Send up to 50 requests in a single HTTP call to reduce latency:

curl -X POST "https://graph.facebook.com/v25.0/" \
  -F "access_token={TOKEN}" \
  -F 'batch=[
    {"method":"GET","relative_url":"me?fields=id,name"},
    {"method":"GET","relative_url":"{page-id}?fields=fan_count"},
    {"method":"POST","relative_url":"{page-id}/feed","body":"message=Hello!"}
  ]'

Response: An array of individual responses:

[
  { "code": 200, "body": "{\"id\":\"123\",\"name\":\"Dipanshu\"}" },
  { "code": 200, "body": "{\"fan_count\":1500}" },
  { "code": 200, "body": "{\"id\":\"123_456\"}" }
]

WARNING

Each operation in a batch counts toward your rate limits individually. Batching saves network round-trips, not quota.


8. Webhooks — Real-Time Updates

Instead of polling the API, subscribe to real-time notifications:

8.1 How Webhooks Work

8.2 Verification Endpoint

Meta verifies your webhook with a GET request:

// Example: Hono/Express handler
app.get('/webhook', (c) => {
  const mode = c.req.query('hub.mode');
  const token = c.req.query('hub.verify_token');
  const challenge = c.req.query('hub.challenge');
 
  if (mode === 'subscribe' && token === 'MY_VERIFY_TOKEN') {
    return c.text(challenge, 200);
  }
  return c.text('Forbidden', 403);
});

8.3 Receiving Events

app.post('/webhook', async (c) => {
  const body = await c.req.json();
  // Respond immediately — process async
  c.executionCtx.waitUntil(processWebhookEvent(body));
  return c.text('OK', 200);
});

CAUTION

You must respond with 200 OK within 5 seconds. If Meta receives failures, your subscription is deactivated. Always offload heavy processing to a background queue.


9. Error Handling

9.1 Error Response Format

Every error returns this structure:

{
  "error": {
    "message": "Invalid OAuth access token.",
    "type": "OAuthException",
    "code": 190,
    "error_subcode": 463,
    "fbtrace_id": "AbC123dEf"
  }
}

9.2 Common Error Codes Reference

Code Subcode Meaning Action
1 Unknown error Retry with exponential backoff
2 Temporary service error Retry after 30 seconds
4 Application-level rate limit Wait, check x-app-usage header
10 Permission denied Check your app's approved permissions
17 User-level rate limit Wait, check x-business-use-case-usage header
100 Invalid parameter Validate request params
190 458 App not installed User removed your app — re-authorize
190 460 Password changed User changed password — re-authorize
190 463 Token expired Exchange refresh token or re-authorize
190 467 Invalid token Token is corrupted — re-authorize
200–299 Permissions error Request the missing scope
368 Temporarily blocked for policy violations Review content for policy compliance

9.3 Rate Limit Headers

Monitor these headers in every response:

// x-app-usage header (parsed)
{
  "call_count": 28,
  "total_cputime": 15,
  "total_time": 12
}
Field Meaning Throttle At
call_count % of calls used in current window 100%
total_cputime % of CPU time consumed 100%
total_time % of total time consumed 100%

Strategy: Implement exponential backoff when any value exceeds 80%.


10. App Review & Going Live

10.1 Development Mode vs Live Mode

Aspect Development Mode Live Mode
Who can use it Only app role-holders (Admin, Dev, Tester) Any Facebook user
Permissions All permissions work for role-holders Only approved permissions work
Content visibility Only visible to role-holders Public
App Review required No Yes (for Advanced Access)

10.2 The App Review Process

What you must provide for each permission:

  1. A written explanation of how your app uses the data
  2. A screen recording (screencast) demonstrating the feature
  3. At least one successful API call made within 30 days of submission
  4. A functional prototype (not just mockups)

10.3 Business Verification

Required before you can get Advanced Access:

  1. Go to Meta Business Manager → Security Center
  2. Upload business documents (registration, tax ID)
  3. Verify your contact info (phone or email)
  4. Meta reviews and approves (usually 2–5 business days)

IMPORTANT

Document names and addresses must exactly match your official registration. Mismatches are the #1 rejection reason.


11. Developer Tools & Debugging

11.1 Graph API Explorer

The most important tool for learning and debugging:

URL: https://developers.facebook.com/tools/explorer

What you can do:

  • Generate test access tokens with specific permissions
  • Execute any Graph API request interactively
  • Inspect response structure before writing code
  • Test edge cases and error scenarios

11.2 Access Token Debugger

URL: https://developers.facebook.com/tools/debug/accesstoken

Paste any token to see: type, expiration, scopes, associated app, and user.

11.3 Useful curl Debugging Tips

Add &debug=all to any request for extra debug info:

GET /v25.0/me?fields=id&debug=all&access_token={TOKEN}

12. Quick Reference — Complete Endpoint Map

12.1 Facebook Page Endpoints

Action Method Endpoint
Get Page info GET /{page-id}?fields=name,fan_count
List managed Pages GET /me/accounts
Publish text post POST /{page-id}/feed
Publish photo POST /{page-id}/photos
Publish video POST /{page-id}/videos
Publish reel POST /{page-id}/video_reels
Get Page feed GET /{page-id}/feed
Delete a post DELETE /{post-id}
Get post insights GET /{post-id}/insights

12.2 Instagram Endpoints

Action Method Endpoint
Get IG User ID GET /{page-id}?fields=instagram_business_account
Create media container POST /{ig-user-id}/media
Publish container POST /{ig-user-id}/media_publish
Check container status GET /{container-id}?fields=status_code
Get user media GET /{ig-user-id}/media
Get media insights GET /{media-id}/insights
Get user insights GET /{ig-user-id}/insights
Delete media DELETE /{media-id}

12.3 Token Endpoints

Action Method Endpoint
OAuth dialog GET facebook.com/v25.0/dialog/oauth
Exchange code for token GET /oauth/access_token
Extend token GET /oauth/access_token?grant_type=fb_exchange_token
Debug token GET /debug_token?input_token={token}
Check permissions GET /me/permissions

13. Best Practices Checklist

  • Always specify API version in URLs (v25.0)
  • Request only the fields you need (reduces response size and improves speed)
  • Use long-lived tokens for server-side integrations
  • Store Page Access Tokens (never-expiring) securely in your database
  • Implement exponential backoff for rate-limited requests
  • Monitor x-app-usage headers on every response
  • Check container status before publishing Instagram videos
  • Verify granted permissions after OAuth — never assume all scopes were approved
  • Use the Graph API Explorer to prototype before coding
  • Subscribe to the Meta changelog for deprecation notices
  • Always respond to webhooks within 5 seconds
  • Host media on public URLs with proper SSL for Instagram publishing

14. Captions, Hashtags & Interactive Features

14.1 Hashtags in Captions

Hashtags work on both platforms — just include them directly in the caption or message text:

# Facebook
POST /{page-id}/feed
  message=Launching today! 🚀 #startup #saas
 
# Instagram
POST /{ig-user-id}/media
  caption=Beautiful vibes ☀️ #photography #nature
Rule Facebook Instagram
Hashtags in caption ✅ Just type #tag ✅ Just type #tag
Max hashtags No hard limit (avoid spam) 5 per post (new 2026 rule)
Clickable/searchable
Mentions (@username)
Emojis
Line breaks Use \n in the string Use \n in the string
Max caption length ~63,206 characters 2,200 characters

WARNING

Instagram recently reduced the max hashtags to 5 per post. For best engagement, Instagram recommends 3–5 relevant hashtags. Exceeding the limit causes the post to fail.

14.2 Caption with Line Breaks Example

const caption = [
  "Launching LogicSpike v2.0 today! 🚀",
  "",
  "New features:",
  "✅ AI content scheduling",
  "✅ Multi-platform publishing",
  "",
  "#startup #saas #ai"
].join("\n");

14.3 Polls, Prompts & Interactive Features

Interactive elements (polls, quizzes, question stickers, AI caption prompts) are in-app-only features and are not available through the Graph API:

Feature In Instagram App Via Graph API
Polls (Stories/Feed/Reels)
AI caption prompts/suggestions
Question stickers
Quiz stickers
Countdown stickers
Music sticker
Link sticker
Location tag ✅ (location_id param)
User tags ✅ (user_tags param)
Hashtags in caption ✅ (just text)
Mentions in caption ✅ (just text)

Workaround: Use caption-based engagement instead — add questions like "What do you think? Comment below! 👇"


15. FAQ — Common Doubts & Edge Cases

Q1: Can I post an image with music?

❌ Not directly. The Graph API has no music_id or audio_url parameter. Meta's music library is in-app-only (licensed content).

Workaround: Pre-render the image + audio into a video file using FFmpeg, then post as a Reel:

ffmpeg -loop 1 -i photo.jpg -i music.mp3 \
  -c:v libx264 -tune stillimage \
  -c:a aac -b:a 128k \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:-1:-1:color=black" \
  -shortest -pix_fmt yuv420p \
  -t 15 output_reel.mp4

Then upload output_reel.mp4 as a Reel via the standard /media/media_publish flow.


Q2: Can I add the "Made with AI" label via API?

Not directly. There is no ai_generated=true parameter. Meta detects AI content through file metadata:

Detection Method How It Works
C2PA metadata Embedded by Adobe Firefly, DALL-E 3, Microsoft tools
IPTC DigitalSourceType Standard EXIF/XMP metadata field
Manual toggle In-app only — not available via API

Workaround: Embed the metadata yourself before uploading:

exiftool -IPTC:DigitalSourceType="trainedAlgorithmicMedia" image.jpg

Instagram reads this metadata and auto-applies the "AI generated" label.


Q3: What happens with text-only posts?

Platform Text-Only Post Supported? What Users See
Facebook ✅ Works natively Standard text post (short text may get colored gradient background)
Instagram ❌ Not supported Every post requires media — API returns an error without image_url or video_url

Workaround for Instagram: Render text as a styled image (1080x1080) using server-side SVG/image generation, then post as an image:

const svg = `
  <svg width="1080" height="1080" xmlns="http://www.w3.org/2000/svg">
    <rect width="100%" height="100%" fill="#1a1a2e"/>
    <text x="540" y="540" text-anchor="middle"
          font-family="Inter" font-size="48" fill="#fff"
          dominant-baseline="middle">${userText}</text>
  </svg>`;
const image = await sharp(Buffer.from(svg)).png().toBuffer();

Q4: Can I create collaborative posts via API?

✅ Partially. Meta added Collaboration API endpoints in 2025:

# Create a collab post (invite a collaborator)
POST /{ig-user-id}/media
  image_url=https://cdn.example.com/photo.jpg
  caption=Amazing collab! 🤝
  collaborators=['other_ig_username']
  access_token={PAGE_TOKEN}
 
# View incoming collab requests
GET /{ig-user-id}/collab_requests?fields=id,status,media
 
# Accept/Decline
POST /{collab-request-id}/accept
POST /{collab-request-id}/decline
Limitation Detail
Max collaborators via API 1 (app supports up to 3)
Other user must accept Post shows on their profile only after acceptance
Supported content Feed posts, Reels, Carousels — not Stories
Permissions needed instagram_content_publish + instagram_manage_comments

Q5: Can I edit or update a post after publishing?

Platform Edit Caption Replace Media Delete
Facebook POST /{post-id} with new message ❌ Must delete & repost DELETE /{post-id}
Instagram ❌ Not supported via API ❌ Not supported DELETE /{media-id}

NOTE

Instagram captions cannot be edited via API. The user must edit in the Instagram app, or you must delete and repost.


Q6: Can I get analytics/insights for published posts?

✅ Yes, both platforms support it:

# Facebook Post Insights
GET /{post-id}/insights?metric=post_impressions,post_engaged_users
 
# Instagram Media Insights
GET /{media-id}/insights?metric=impressions,reach,likes,comments,shares,saved
Metric Facebook Instagram
Impressions
Reach
Likes
Comments count
Shares
Saves
Clicks
Video views ✅ (Reels)

Q7: What happens when a token expires mid-schedule?

Token Type Behavior
Page Access Token (from long-lived user token) Never expires — use this for all scheduled posts
User token expires Doesn't matter if you already saved the Page token
User changes password / revokes app Page token becomes invalid → error code 190

Strategy for LogicSpike:

Save never-expiring Page Token → Use for all scheduled posts
→ If error 190 occurs → Mark connection as "disconnected"
→ Notify user to reconnect

Q8: Can I comment on posts or reply to comments via API?

✅ Yes:

# Comment on a Facebook post
POST /{post-id}/comments?message=Thanks!&access_token={TOKEN}
 
# Reply to an Instagram comment
POST /{comment-id}/replies?message=Thank you! 🙏&access_token={TOKEN}
 
# Read comments
GET /{post-id}/comments?fields=from,message,created_time

Q9: Can I add Instagram Stories to Highlights?

❌ No. The API can publish Stories but cannot add them to Highlights. Stories auto-expire after 24 hours. Highlights are an in-app-only feature.


Q10: What are the media URL requirements for Instagram?

Instagram servers download the media from your URL during container creation:

Requirement Detail
Publicly accessible No auth headers, no quickly-expiring signed URLs
HTTPS Must be https://
Correct content-type Server must return proper Content-Type header
Available for ~5 minutes Instagram needs time to fetch and process

For LogicSpike: Use R2/CDN public URLs. If using signed URLs, set expiry to at least 30 minutes.


Q11: Can I schedule posts natively through the API?

Platform Native Scheduling? How
Facebook ✅ Yes published=false + scheduled_publish_time={unix_timestamp}
Instagram ❌ No Build your own scheduler (cron job that calls publish API at scheduled time)

Q12: What are the daily publishing rate limits?

Platform Limit
Facebook Page posts 25 posts per Page per hour
Facebook Reels 30 per 24 hours
Instagram (all types) 100 posts per account per 24 hours (carousels count as 1)
API calls total ~200 calls per user per hour (varies by app tier)

Q13: Can I post to a Facebook Group?

❌ Effectively no. Meta removed Group publishing API access in 2024. You can only post to Groups if your app is the group's own installed app — heavily restricted. Focus on Pages only.


Q14: Can I cross-post to both Facebook and Instagram in one API call?

❌ No. They are separate APIs with separate endpoints:

Facebook: POST /{page-id}/feed
Instagram: POST /{ig-user-id}/media → POST /{ig-user-id}/media_publish

Your server must make separate API calls for each platform — this is what every scheduling tool does.


Q15: Can I tag people or locations in posts?

Feature Facebook Instagram
Tag users in photo tags param user_tags param (x,y coordinates required)
Tag location place param (Facebook Place ID) location_id param (Facebook Place ID)
@mention in caption ✅ Just type @pagename ✅ Just type @username

Instagram user tags require coordinates:

POST /{ig-user-id}/media
  image_url=https://...
  user_tags=[{"username":"friend","x":0.5,"y":0.5}]

Q16: What media formats and sizes are supported?

Spec FB Photo FB Video IG Image IG Video/Reel
Formats JPG, PNG, GIF, BMP, TIFF MP4, MOV JPG, PNG MP4, MOV
Max file size 10 MB 10 GB 8 MB 1 GB
Aspect ratio Any Any 4:5 to 1.91:1 9:16 (Reels), 4:5 to 1.91:1 (Feed)
Resolution Up to 2048px Up to 4K 1080px wide recommended 1080x1920 (Reels)
Duration Up to 240 min 3–90 sec (Reels), up to 60 min (Feed)

Learning