Meta

Overview

The Meta Core API mirrors Meta's Graph API 1:1 — ad accounts, campaigns, ad sets, ads, creatives, insights, Pages and granted permissions.

Base path/api/core/meta
Graph versionv25.0
Auththe calling organization's own stored Meta access token, resolved from its meta_ads integration. You never pass a Meta token yourself
Every requestrequires ?organizationId=<uuid>

Every query param except organizationId is forwarded to the Graph API verbatim. fields, limit, after, time_range, time_increment, breakdowns, filtering, level, action_attribution_windows — all keep Meta's own names and semantics. Analify renames nothing and defaults nothing, so Meta's own reference is the field-level source of truth.

Dry-running a create call

Every campaign/adset/ad/creative/audience/rule/experiment create endpoint already accepts Meta's own execution_options: ["validate_only"] field in the POST body — since these routes are pure pass-through, this has always worked. Meta validates the object (auth, field shape, budget/targeting rules) and returns success/failure without creating anything live. Use it before a scripted bulk-launch, or any time an agent is about to spend real budget on a config it hasn't run before.

API Endpoints

49 routes. Insights edges on Pages, Posts and Instagram require Meta's metric param; Instagram metrics differ by media type.

EndpointMeta Real EndpointDescription
GET /accountsGET /me/adaccountsAd accounts the token can reach
GET /accounts/{accountId}GET /{accountId}Account currency + timezone
POST /accounts/{accountId}POST /{accountId}Update ad-account settings (spend_cap, name, notifications_enabled)
GET /accounts/{accountId}/insightsGET /{accountId}/insightsAccount-level insights
GET /accounts/{accountId}/delivery-estimateGET /{accountId}/delivery_estimateAudience reach sizing before creating an ad set
GET /accounts/{accountId}/campaignsGET /{accountId}/campaignsCampaigns under an account
POST /accounts/{accountId}/campaignsPOST /{accountId}/campaignsCreate a campaign
GET /accounts/{accountId}/adsetsGET /{accountId}/adsetsAd sets under an account
POST /accounts/{accountId}/adsetsPOST /{accountId}/adsetsCreate an ad set
GET /accounts/{accountId}/adsGET /{accountId}/adsAds under an account
POST /accounts/{accountId}/adsPOST /{accountId}/adsCreate an ad
GET /accounts/{accountId}/creativesGET /{accountId}/adcreativesAd creatives under an account
POST /accounts/{accountId}/creativesPOST /{accountId}/adcreativesCreate an ad creative
POST /accounts/{accountId}/imagesPOST /{accountId}/adimagesUpload an ad image (bytes base64, or image_url — Analify fetches an already-hosted asset server-side)
POST /accounts/{accountId}/videosPOST /{accountId}/advideosUpload an ad video (hosted-file file_url)
GET /accounts/{accountId}/audiencesGET /{accountId}/customaudiencesList Custom Audiences / Lookalikes
POST /accounts/{accountId}/audiencesPOST /{accountId}/customaudiencesCreate a Custom Audience or Lookalike
GET /accounts/{accountId}/rulesGET /{accountId}/adrules_libraryList Automated Rules
POST /accounts/{accountId}/rulesPOST /{accountId}/adrules_libraryCreate an Automated Rule
GET /accounts/{accountId}/experimentsGET /{accountId}/ad_studiesList Split Tests
POST /accounts/{accountId}/experimentsPOST /{accountId}/ad_studiesCreate a Split Test
GET /accounts/{accountId}/activitiesGET /{accountId}/activitiesAccount change/activity log
GET /accounts/{accountId}/adspixelsGET /{accountId}/adspixelsPixels on the account
GET /accounts/{accountId}/dataset-metadataGET /{accountId}/dataset_metadataCAPI / offline datasets
GET /accounts/{accountId}/promote-pagesGET /{accountId}/promote_pagesPages this account may promote
GET /campaigns/{campaignId}GET /{campaignId}Get one campaign
POST /campaigns/{campaignId}POST /{campaignId}Update a campaign
DELETE /campaigns/{campaignId}DELETE /{campaignId}Delete a campaign permanently
GET /campaigns/{campaignId}/insightsGET /{campaignId}/insightsCampaign insights
GET /adsets/{adSetId}GET /{adSetId}Get one ad set
POST /adsets/{adSetId}POST /{adSetId}Update an ad set
DELETE /adsets/{adSetId}DELETE /{adSetId}Delete an ad set permanently
GET /adsets/{adSetId}/insightsGET /{adSetId}/insightsAd-set insights
GET /ads/{adId}GET /{adId}Get one ad
POST /ads/{adId}POST /{adId}Update an ad
DELETE /ads/{adId}DELETE /{adId}Delete an ad permanently
POST /ads/{adId}/copiesPOST /{adId}/copiesDuplicate an ad
GET /ads/{adId}/insightsGET /{adId}/insightsAd-level insights
GET /ads/{adId}/previewsGET /{adId}/previewsRendered ad preview
GET /creatives/{creativeId}GET /{creativeId}Get one ad creative
DELETE /creatives/{creativeId}DELETE /{creativeId}Delete an ad creative permanently
GET /audiences/{audienceId}GET /{audienceId}Get one audience
DELETE /audiences/{audienceId}DELETE /{audienceId}Delete an audience
POST /audiences/{audienceId}/usersPOST /{audienceId}/usersAdd hashed customer records to a Custom Audience
GET /rules/{ruleId}GET /{ruleId}Get one Automated Rule
POST /rules/{ruleId}POST /{ruleId}Update an Automated Rule (e.g. enable/disable)
DELETE /rules/{ruleId}DELETE /{ruleId}Delete an Automated Rule
GET /experiments/{studyId}GET /{studyId}Get one Split Test
POST /experiments/{studyId}POST /{studyId}Update a Split Test (e.g. stop it)
POST /pixels/{pixelId}/eventsPOST /{pixelId}/eventsConversions API — send server-side events
GET /objectsGET /?ids=Batch read many objects at once
GET /pagesGET /me/accountsFacebook Pages the token manages
GET /permissionsGET /me/permissionsScopes actually granted to the token
GET /businessesGET /me/businessesBusiness Managers reachable
GET /businesses/{businessId}/owned-product-catalogsGET /{businessId}/owned_product_catalogsCatalogs of one business
GET /owned-product-catalogsGET /me/owned_product_catalogsCatalogs reachable directly
GET /catalogs/{catalogId}/productsGET /{catalogId}/productsProducts as Meta holds them
GET /pages/{pageId}GET /{pageId}Page profile
GET /pages/{pageId}/insightsGET /{pageId}/insightsOrganic Page performance
GET /pages/{pageId}/feedGET /{pageId}/feedFull timeline
GET /pages/{pageId}/postsGET /{pageId}/postsPosts by the Page
GET /posts/{postId}/commentsGET /{postId}/commentsComment thread
GET /posts/{postId}/insightsGET /{postId}/insightsPer-post reach
GET /videos/{videoId}GET /{videoId}Video metadata
GET /instagram/{igUserId}GET /{igUserId}IG Business profile
GET /instagram/{igUserId}/mediaGET /{igUserId}/mediaPosts, reels, stories
GET /instagram/{igUserId}/insightsGET /{igUserId}/insightsIG account performance
GET /instagram/media/{mediaId}/insightsGET /{mediaId}/insightsPer-post IG performance

Accounts

GET /api/core/meta/accounts

Every ad account the organization's token can reach. Use this to discover the {accountId} (Meta's act_<id> form) the routes below take.

curl "https://analify.scalyax.ai/api/core/meta/accounts?organizationId={organizationId}&fields=id,name,currency,account_status" \
  -H "Authorization: Bearer {anlfy_token}"

Which account is used? Routes that do not name an account in the path fall back to the ad account stored on the integration. The /accounts/{accountId}/* routes let you target any account the token can reach — pass the id in the path.

GET /api/core/meta/accounts/{accountId}/insights

The workhorse reporting endpoint. Takes Meta's own insights params — fields, level (account/campaign/adset/ad), time_range={"since":"YYYY-MM-DD","until":"YYYY-MM-DD"}, time_increment, breakdowns, action_attribution_windows, filtering.

curl -G "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/insights" \
  -H "Authorization: Bearer {anlfy_token}" \
  --data-urlencode "organizationId={organizationId}" \
  --data-urlencode "level=campaign" \
  --data-urlencode "fields=campaign_name,spend,impressions,actions,purchase_roas" \
  --data-urlencode 'time_range={"since":"2026-08-01","until":"2026-08-16"}'

purchase_roas here is Meta's own reported ROAS. It is not Analify's nROAS after RTO and, for a COD store with returns, the two differ materially. Use the Analytics API when you want the profit-true number; use Core API when you want exactly what Meta says.

GET /api/core/meta/accounts/{accountId}/delivery-estimate

Size an audience BEFORE creating the ad set that targets it. Pass targeting_spec (JSON-encoded, same shape as an ad set's targeting) and optimization_goal; response includes estimate_ready, estimate_mau_lower_bound, estimate_mau_upper_bound.

POST /api/core/meta/accounts/{accountId}

Update ad-account-level settings — Meta's own fields, forwarded verbatim.

curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"spend_cap": 50000, "notifications_enabled": true}'

Campaigns, ad sets and ads

Reads take fields; writes POST Meta's own field names in a JSON body.

# Read one campaign
curl "https://analify.scalyax.ai/api/core/meta/campaigns/23851234567890123?organizationId={organizationId}&fields=id,name,status,daily_budget,objective" \
  -H "Authorization: Bearer {anlfy_token}"

# Pause it — Meta's own `status` field, forwarded as-is
curl -X POST "https://analify.scalyax.ai/api/core/meta/campaigns/23851234567890123?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"status": "PAUSED"}'

# Delete it permanently — distinct from status: DELETED, which archives it for reporting
curl -X DELETE "https://analify.scalyax.ai/api/core/meta/campaigns/23851234567890123?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}"

Campaign, ad-set, ad and creative writes change a live ad account — budgets and delivery are affected immediately. They require an API token with the write scope. DELETE is available on campaigns, ad sets, ads and creatives.

Ad-set updates work the same way (POST /api/core/meta/adsets/{adSetId}) and accept Meta's ad-set fields, e.g. daily_budget, bid_amount, status, targeting. Ad updates (POST /api/core/meta/ads/{adId}) accept name, status, creative.

Creating campaigns, ad sets, ads and creatives

These routes launch new objects rather than editing existing ones — same rule as everywhere else in this API: Meta's own field names, forwarded verbatim, no reshaping. Add "execution_options": ["validate_only"] to any of these bodies to validate without creating anything live.

# Create a campaign
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/campaigns?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Traffic Campaign", "objective": "OUTCOME_TRAFFIC", "status": "PAUSED", "special_ad_categories": []}'

# Create an ad set under that campaign
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/adsets?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"campaign_id": "23851234567890123", "name": "Traffic AdSet", "billing_event": "LINK_CLICKS", "status": "PAUSED", "targeting": {"age_min": 18, "age_max": 65, "geo_locations": {"countries": ["EG"]}}}'

# Upload an image from local bytes (base64) OR from an already-hosted URL — pick whichever you have
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/images?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"bytes": "<base64-encoded-image-data>"}'

curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/images?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"image_url": "https://cdn.example.com/product-photo.jpg"}'

# Create a single-image creative using that hash
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/creatives?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Single Image Creative", "object_story_spec": {"page_id": "111", "link_data": {"link": "https://example.com", "message": "Ad text", "image_hash": "abc123"}}}'

# Create the ad from that creative
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/ads?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Test Ad", "adset_id": "23860001", "status": "PAUSED", "creative": {"creative_id": "23870001"}}'

# Duplicate an existing ad instead of building a new one
curl -X POST "https://analify.scalyax.ai/api/core/meta/ads/23870001/copies?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"status_option": "PAUSED", "rename_options": {"rename_strategy": "ONLY_TOP_LEVEL_RENAME"}}'

Image upload accepts two sources — bytes (base64, e.g. a file read locally or through an MCP client) or image_url (Analify fetches an already-hosted asset — a Shopify product photo, a rendered creative — server-side and forwards it to Meta; capped at Meta's 10MB ad-image limit). Video upload only supports the hosted-file variant — {"file_url": "https://..."} — not chunked/binary upload. Creatives also accept carousel (link_data.child_attachments), catalog/DPA (template_data + product_set_id), and Meta's GenAI creative features (degrees_of_freedom_spec — text generation, image uncrop, background generation) — see Meta's Ad Creative reference for the full field set; every field is forwarded as-is.

Audiences (Custom Audiences + Lookalikes)

Retargeting and prospecting — the same customaudiences endpoint creates both a Custom Audience (customer list, pixel/website) and a Lookalike, distinguished by subtype.

# Create a Custom Audience
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/audiences?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Website visitors 30d", "subtype": "WEBSITE", "customer_file_source": "USER_PROVIDED_ONLY"}'

# Add hashed customer records to it
curl -X POST "https://analify.scalyax.ai/api/core/meta/audiences/{audienceId}/users?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"payload": {"schema": ["EMAIL_SHA256"], "data": [["<sha256-hash>"]]}}'

# Create a Lookalike from an existing audience
curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/audiences?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "LAL 1% Egypt", "subtype": "LOOKALIKE", "origin_audience_id": "{audienceId}", "lookalike_spec": {"type": "similarity", "ratio": 0.01, "country": "EG"}}'
⚠️

Member data must be pre-hashed (SHA-256) by the caller. POST .../users never hashes or inspects the payload, and Meta rejects unhashed PII with its own validation error. This endpoint's body is never logged by Analify's tracing layer, but you are still responsible for hashing correctly before sending it. Removing members (DELETE .../users with a body) is not yet exposed.

Conversions API (server-side events)

POST /api/core/meta/pixels/{pixelId}/events sends server-side conversion events — the resilient counterpart to browser-side Pixel tracking after iOS14.5/ATT signal loss. Find a pixel id via GET /accounts/{accountId}/adspixels.

curl -X POST "https://analify.scalyax.ai/api/core/meta/pixels/{pixelId}/events?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"data": [{"event_name": "Purchase", "event_time": 1735689600, "action_source": "website", "user_data": {"em": ["<sha256-hash>"], "client_ip_address": "1.2.3.4"}, "custom_data": {"value": 199.99, "currency": "EGP"}}]}'

user_data may carry hashed PII (em/ph) alongside unhashed fields Meta's own spec allows (client_ip_address, client_user_agent, fbc, fbp) — same never-logged guarantee as Audiences above.

Automated Rules

Schedule-evaluated conditions — "if CPA > X for 3 days, pause" — that Meta acts on without polling.

curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/rules?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Pause high CPA campaigns", "evaluation_spec": {"evaluation_type": "SCHEDULE", "filters": [{"field": "cpa", "operator": "GREATER_THAN", "value": 5000}]}, "execution_spec": {"execution_type": "PAUSE"}, "schedule_spec": {"schedule_type": "DAILY"}}'

# Disable a rule
curl -X POST "https://analify.scalyax.ai/api/core/meta/rules/{ruleId}?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"status": "DISABLED"}'

Split Tests / Experiments

A controlled A/B test between cells of campaigns, instead of eyeballing insights across two campaigns run at different times.

curl -X POST "https://analify.scalyax.ai/api/core/meta/accounts/act_123456789/experiments?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
  -d '{"name": "Creative A/B", "type": "SPLIT_TEST", "cells": [{"name": "Video", "treatment_percentage": 50, "campaign_group_ids": ["{campaignId}"]}, {"name": "Image", "treatment_percentage": 50, "campaign_group_ids": ["{campaignId}"]}]}'

Batch reads

GET /api/core/meta/objects

Meta's batch object read — one call, many ids, one fields set. Cheaper than N single reads and counts as one Graph call against your rate budget.

curl "https://analify.scalyax.ai/api/core/meta/objects?organizationId={organizationId}&ids=23851111,23852222&fields=id,name,status" \
  -H "Authorization: Bearer {anlfy_token}"

Creatives and previews

GET /api/core/meta/creatives/{creativeId} returns the raw creative (object_story_spec, asset_feed_spec, image/video ids, …). GET /api/core/meta/ads/{adId}/previews returns Meta's rendered preview — pass Meta's ad_format (e.g. DESKTOP_FEED_STANDARD, INSTAGRAM_STORY).

Pages and permissions

GET /api/core/meta/permissions

The scopes Meta actually granted this token. Check this first when a call returns a 403 — a missing scope, not a bug in the route, is the usual cause.

curl "https://analify.scalyax.ai/api/core/meta/permissions?organizationId={organizationId}" \
  -H "Authorization: Bearer {anlfy_token}"

GET /api/core/meta/pages mirrors GET /me/accounts — the Facebook Pages the token manages, each with its own page access token and tasks.


Error Shape

Meta's Graph API collapses every failure into a body error code rather than an HTTP status, so Core API maps it back onto a real status:

ConditionHTTP status
Token invalid / expired401
Missing permission (scope)403
Object not found404
Rate limited429
Anything else400

Graph's own code and subcode are preserved under error.details:

{
  "success": false,
  "error": {
    "message": "(#100) Unsupported get request. Object with ID 'act_000' does not exist",
    "code": "META_API_ERROR",
    "details": { "code": 100, "subcode": 33 }
  }
}

Before any Graph call is attempted, credential problems surface as:

CodeStatusMeaning
META_NOT_CONNECTED400The organization has no connected Meta integration
META_RECONNECT_REQUIRED401The stored token is dead — the user must reconnect Meta
META_CREDENTIALS_ERROR400Credential resolution failed for another reason

| META_UNKNOWN_ERROR | 500 | A non-Graph failure inside the route |


Did this page help you?