Engineering

Facebook API explained (2026): Graph API, Pages API, access and how to post

There is no single Facebook API — there is the Graph API and a stack of products on top of it. Which one you need, how to get access and pass App Review, whether it costs anything, and working requests for posting text, photos, videos and Reels to a Facebook Page.

Search for “Facebook API” and you will not find one API. You will find the Graph API — Meta's HTTP interface to everything on Facebook — and a dozen products layered on top of it, each with its own permissions, review process and quirks. Most developers who type that query want one specific thing: to publish to a Facebook Page from their own code. This guide covers which part of the Facebook API you actually need, how to get access, whether it costs anything, and how to post text, photos, videos and Reels to a Page with working requests.

Everything here targets Graph API v24.0, the version we run in production at Outstand. Meta ships a new version roughly every quarter and supports each one for about two years, so pin your version in the URL and plan an upgrade at least once a year.

What is the Facebook API?

“The Facebook API” almost always means the Graph API. It models Facebook as a graph: nodes (a Page, a post, a photo, a comment), edges (a Page's /feed, a post's /comments) and fields you ask for explicitly. Every request goes to the same host:

bash
1curl -s "https://graph.facebook.com/v24.0/me?fields=id,name&access_token=$USER_TOKEN"

Three things decide what any given request can do: the access token you send (whose behalf you act on), the permissions that token was granted, and the access level your app has for each permission. Get those three right and the rest is ordinary REST.

You may also see it called the Meta Graph API. Same thing — the API predates the company rename, and Instagram's professional APIs now run on the same Graph infrastructure under the same Meta app.

The different Facebook APIs (and which one you need)

Meta splits functionality into products. They share the Graph API host and the app dashboard, but they have different permissions and review requirements. Picking the right one first saves weeks.

API

What it is for

Can it publish organic posts?

Pages API

Publishing, scheduling, reading comments and insights on Facebook Pages you manage

Yes — this is the one for posting

Marketing API

Creating and managing ads, campaigns, audiences and ad reporting

No (ads only)

Conversions API

Sending server-side web and offline events for ad attribution

No

Ad Library API

Searching public ads, mainly political and issue ads

No (read-only)

Instagram Platform

Publishing and messaging for Instagram professional accounts

Yes, on Instagram

Messenger Platform

Page inbox and automated conversations on Messenger

No (messages, not posts)

WhatsApp Cloud API

Business messaging on WhatsApp

No

If you are building a scheduler, a social media tool, an agency dashboard or an AI agent that posts, you want the Pages API. The rest of this guide is about that.

Two limits are worth knowing before you start, because they end a lot of projects early:

  • No personal profiles. Meta removed the ability for apps to publish to personal timelines in 2018. Programmatic posting works on Pages only.
  • No Groups. Meta deprecated the Groups API in 2024, so you can no longer publish to Groups through the Graph API either.

Is the Facebook API free?

Yes. Meta does not charge per request for the Graph API or the Pages API, and there is no paid tier that raises your limits. The costs are elsewhere:

  • Ad spend if you use the Marketing API — the API is free, the ads are not.
  • Per-message fees on the WhatsApp Business Platform, which is the one Meta API that is billed directly.
  • App Review and Business Verification, which cost nothing but take time and need a privacy policy, screencasts and a verified business.
  • Rate limits, which cap how much you can do per Page and per app regardless of what you are willing to pay.
  • Engineering time to handle tokens, media uploads, version upgrades and the error codes below. In practice this is the biggest line item.

If you are comparing this with other networks, we keep a running breakdown of Instagram API pricing and of what approval looks like on each platform.

How to get Facebook API access, step by step

1. Create a Meta developer app

Register at developers.facebook.com, create an app and pick a use case that includes managing Pages. Under Settings → Basic add a privacy policy URL, app icon and category — App Review will not start without them. Add your OAuth redirect URI under Facebook Login settings.

2. Request the right permissions

For publishing to Pages, the minimum set is:

Permission

Why you need it

pages_show_list

List the Pages the person manages, so they can pick one

pages_manage_posts

Create, edit and delete posts on the Page

pages_read_engagement

Read Page content and engagement, including the post you just created

pages_read_user_content

Read comments and posts by other people on the Page (only if you need them)

pages_manage_engagement

Reply to and moderate comments (only if you need it)

read_insights

Read Page and post insights (only if you need analytics)

Ask for the smallest set that supports your feature. Every extra permission is another thing a reviewer has to see working in your screencast.

3. Pass App Review and Business Verification

While your app is in development you have Standard Access: everything works, but only for people who have a role on the app. To let anyone else connect their Page you need Advanced Access for each permission, which means App Review. Reviewers want a screencast per permission showing a real user logging in, granting it and seeing the feature that uses it. You will also need to complete Business Verification for your company.

The common rejection reasons are predictable: the screencast does not show the login flow, the test credentials do not work, the permission is requested but not visibly used, or the privacy policy URL is broken. Budget for at least one round trip.

4. Get a Page access token

The person logs in through Facebook Login and you receive a short-lived user access token. Exchange it server-side for a long-lived user token, then call /me/accounts to get a Page access token for each Page they manage. A Page token derived from a long-lived user token does not expire on a timer — it stops working only when the person changes their password, removes your app or loses their role on the Page.

bash
1# 1. Exchange the short-lived user token for a long-lived one
2curl -s "https://graph.facebook.com/v24.0/oauth/access_token?grant_type=fb_exchange_token&client_id=$APP_ID&client_secret=$APP_SECRET&fb_exchange_token=$SHORT_USER_TOKEN"
3
4# 2. List the person's Pages with their Page access tokens
5curl -s "https://graph.facebook.com/v24.0/me/accounts?fields=id,name,access_token&access_token=$LONG_USER_TOKEN"

We wrote up the full exchange, including the edge cases that silently invalidate tokens, in how to get a Facebook Page access token that does not expire. Store Page tokens encrypted, and never ship your app secret to a browser.

Posting to a Facebook Page with the Graph API

Once you have a Page ID and a Page access token, publishing is a handful of endpoints. Each one returns the new object's ID, which you should store so you can read engagement, reply to comments or delete the post later.

How to find your Facebook Page ID

The /me/accounts call above already returns the id of every Page the person manages, and that is the most reliable source. If you only have a Page's URL or username, resolve it with any valid token:

bash
1curl -s "https://graph.facebook.com/v24.0/outstandapp?fields=id,name&access_token=$PAGE_TOKEN"

Page admins can also see the ID in the Page's About → Page transparency section. Do not scrape it from the page HTML — the vanity URL can change and the numeric ID will not.

Text and link posts

Post to the Page's /feed edge. Add link to create a link post; Facebook scrapes the URL's Open Graph tags for the preview.

bash
1curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/feed" \
2  -d "message=We just shipped scheduled Reels support." \
3  -d "link=https://www.outstand.so/facebook" \
4  -d "access_token=$PAGE_TOKEN"
5# => {"id":"1234567890_9876543210"}

The same request from Node:

ts
1const res = await fetch(`https://graph.facebook.com/v24.0/${pageId}/feed`, {
2  method: "POST",
3  headers: { "Content-Type": "application/json" },
4  body: JSON.stringify({
5    message: "We just shipped scheduled Reels support.",
6    link: "https://www.outstand.so/facebook",
7    access_token: pageToken,
8  }),
9});
10const data = await res.json();
11if (data.error) throw new Error(`${data.error.code}: ${data.error.message}`);
12console.log("Post ID:", data.id);

Photos and multi-photo posts

A single photo goes to /photos, either by public url or as a multipart upload. For a post with several photos, upload each one unpublished, then attach the returned IDs to a /feed post:

bash
1# Upload each photo without publishing it
2curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/photos" \
3  -d "url=https://example.com/photo-1.jpg" \
4  -d "published=false" \
5  -d "access_token=$PAGE_TOKEN"
6# => {"id":"111"}   (repeat for each photo)
7
8# Publish one post that references all of them
9curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/feed" \
10  -d "message=Three photos from the launch" \
11  -d 'attached_media[0]={"media_fbid":"111"}' \
12  -d 'attached_media[1]={"media_fbid":"112"}' \
13  -d 'attached_media[2]={"media_fbid":"113"}' \
14  -d "access_token=$PAGE_TOKEN"

Videos and Reels

Regular videos go to /videos with a file_url or a resumable upload. Reels have their own three-phase flow on the /video_reels edge: start a session, upload the bytes to Meta's upload host, then finish and publish.

  1. Start: POST /{page-id}/video_reels with upload_phase=start. You get back a video_id and an upload_url.
  2. Upload: POST to https://rupload.facebook.com/video-upload/v24.0/{video_id} with an Authorization: OAuth {page-token} header, plus either a file_url header for hosted files or the raw bytes with an offset and file_size.
  3. Finish: POST /{page-id}/video_reels with upload_phase=finish, the video_id, video_state=PUBLISHED and your description.
bash
1# 1. Start
2curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/video_reels" \
3  -d "upload_phase=start" -d "access_token=$PAGE_TOKEN"
4
5# 2. Upload from a hosted URL
6curl -s -X POST "https://rupload.facebook.com/video-upload/v24.0/$VIDEO_ID" \
7  -H "Authorization: OAuth $PAGE_TOKEN" \
8  -H "file_url: https://example.com/reel.mp4"
9
10# 3. Finish and publish
11curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/video_reels" \
12  -d "upload_phase=finish" -d "video_id=$VIDEO_ID" \
13  -d "video_state=PUBLISHED" \
14  -d "description=Behind the scenes of this week's build" \
15  -d "access_token=$PAGE_TOKEN"

Processing is asynchronous. Poll the video's status field before you tell your user it is live, and check Meta's current Reels specs for aspect ratio and duration — uploads that miss them fail at the processing step, not at upload.

Scheduling posts

To schedule natively, send published=false with a scheduled_publish_time as a Unix timestamp. Meta requires the time to be at least 10 minutes in the future and enforces a maximum window, so validate both before you call the API. Scheduled posts show up under the Page's /scheduled_posts edge until they go out.

bash
1curl -s -X POST "https://graph.facebook.com/v24.0/$PAGE_ID/feed" \
2  -d "message=Doors open at 9am tomorrow." \
3  -d "published=false" \
4  -d "scheduled_publish_time=1793696400" \
5  -d "access_token=$PAGE_TOKEN"

Many teams skip native scheduling and run their own queue instead. You get one scheduling model across every network, and you can retry or edit a post right up to publish time.

Rate limits, errors and gotchas

Page requests are governed by Business Use Case (BUC) rate limits, which scale with the Page's own activity, plus app-level limits. Every response includes headers telling you how close you are — read them and back off before Meta does it for you:

  • X-Business-Use-Case-Usage — per-Page usage as a percentage of the limit, with an estimated time to regain access once you are throttled.
  • X-App-Usage — app-wide call count, CPU time and total time as percentages.

The error codes you will meet most:

Code

Meaning

What to do

190

Access token invalid or expired

Mark the connection broken and ask the person to reconnect; do not retry

200 / 10

Permission missing or not granted

Check the token's granted scopes and your access level for that permission

100

Invalid parameter

Usually a malformed field, wrong ID type or unsupported media format

4 / 17 / 32 / 80001

App, user, Page or Pages BUC throttling

Back off using the usage headers; queue and retry later

368

Temporarily blocked for policy reasons

Stop posting from that Page and review content; retries make it worse

And the gotchas that show up in production:

  • Version drift. Every version has an end-of-life date. Meta sends deprecation notices to the app's developers, but they are easy to miss; track them in your calendar.
  • Silent token death. A password change or a removed Page role invalidates tokens with no webhook. Check tokens with /debug_token on a schedule instead of finding out on publish.
  • Mentions. Tagging another Page in the body of a post is not reliably supported through the API; mentioning it in a comment on your post is the dependable route.
  • Media URLs must be public. Meta fetches url and file_url from its own servers, so signed URLs that expire in seconds, or hosts that block Meta's crawler, fail with unhelpful errors.

Reading data: comments, insights and engagement

The same Page token reads back what you published. GET /{post-id}?fields=message,created_time,permalink_url confirms the post, GET /{post-id}/comments lists comments (with pages_read_user_content), and GET /{post-id}/insights returns post metrics such as impressions and reactions (with read_insights). Meta has been retiring older insights metrics version by version, so build your dashboards from the metrics list for the version you pin.

If you also manage Instagram, the same Meta app and much of the same token flow cover it — see our guides to the Instagram DM API and to every platform's posting endpoints.

Skip the plumbing: posting to Facebook with Outstand

Everything above is what we built so our customers do not have to. Outstand runs on its own approved Meta app (Managed Keys), so you can connect a Facebook Page through OAuth and publish without registering an app or going through App Review. Tokens are refreshed for you, the right upload flow is chosen per media type, and rate limits are handled by queueing and retrying instead of surfacing 429s.

A photo post to a Facebook Page, scheduled, is one request:

bash
1curl https://api.outstand.so/v1/posts/ \
2  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
3  -H "Content-Type: application/json" \
4  -d '{
5    "accounts": ["YOUR_FACEBOOK_ACCOUNT_ID"],
6    "containers": [
7      {
8        "content": "Open house this weekend!",
9        "media": [{ "url": "https://media.outstand.so/house.jpg", "filename": "house.jpg" }]
10      }
11    ],
12    "scheduledAt": "2026-11-03T09:00:00Z"
13  }'

Publishing a Reel is the same call with one flag:

json
1{
2  "accounts": ["YOUR_FACEBOOK_ACCOUNT_ID"],
3  "containers": [
4    {
5      "content": "Behind the scenes of this week's build",
6      "media": [{ "url": "https://media.outstand.so/reel.mp4", "filename": "reel.mp4" }]
7    }
8  ],
9  "facebook": { "publishAsReel": true }
10}

Add more connected account IDs to accounts and the same post fans out to Instagram, LinkedIn, X, Threads and the other networks we support, in one request. If you need your own branding on the connection screen, you can bring your own Meta app instead — the Facebook configuration docs list the exact scopes and callback URLs, and Create a post documents every field.

Building an agent rather than an app? The Facebook MCP server exposes the same publishing surface as tools, and we walked through it in posting to Facebook Pages from Claude.

FAQ

Does Facebook have a public API?

Yes. The Graph API is public, but almost everything useful needs an access token and, for production use with other people's Pages, App Review for each permission.

Can I post to a personal Facebook profile with the API?

No. Meta removed timeline publishing for apps in 2018. You can publish to Pages you manage.

Can I post to Facebook Groups with the API?

Not any more. The Groups API was deprecated in 2024.

What is the difference between the Graph API and the Marketing API?

The Graph API is the underlying interface. The Marketing API is the set of Graph endpoints and permissions for ads. Organic Page posts use the Pages API; paid campaigns use the Marketing API.

Is there a Facebook API key?

Not in the usual sense. You authenticate with OAuth access tokens. Your app has an App ID and App Secret, which you use server-side to exchange and debug tokens — never as a request credential from a client.

The short version

  • “Facebook API” means the Graph API; for posting you want the Pages API.
  • It is free to call. The cost is App Review, Business Verification, rate limits and maintenance.
  • Request pages_show_list, pages_manage_posts and pages_read_engagement, then exchange tokens until you hold a long-lived Page token.
  • Post with /feed, /photos, /videos and the three-phase /video_reels flow; schedule with published=false and scheduled_publish_time.
  • Read the usage headers, treat 190 as “reconnect” and 368 as “stop”.

If you would rather ship the feature than maintain the integration, Outstand's Facebook API gives you Pages publishing, scheduling, Reels, Stories and analytics through one endpoint — plus every other network your users care about. Get an API key and make your first post in a few minutes.