TQ

Meta Ads Manager Integration

Overview

Meta Ads Manager (Facebook Ads) integration syncs advertising data from Meta's Marketing API into Gridlink. Each client can have one or more linked Meta ad accounts, enabling centralized reporting across the portfolio.

Authentication

Meta Ads uses the Graph API with System User Access Tokens issued through Meta Business Manager.

Setup Steps

  • Go to developers.facebook.com — create a Business-type app
  • Add the Marketing API product to the app
  • In business.facebook.com → Settings → System Users, create a system user
  • Assign the app to the system user (Assets → Apps → select app → Admin role)
  • Assign ad account(s) to the system user (Assets → Ad Accounts)
  • Generate a System User Token with ads_read permission
  • In Gridlink, go to the client page → Digital Marketing → Meta Ads Accounts → Add Account
  • Enter the Ad Account ID (act_XXXXXXXXX), a label, and the access token
  • Click Test to verify connectivity
  • Token Lifecycle

  • System User tokens do not expire (unlike user tokens)
  • If a token is revoked in Business Manager, the test endpoint will return an error
  • Token issues are surfaced in sync logs and the Meta Ads status panel
  • Required Credentials

    CredentialWhere to FindStored In
    App IDdevelopers.facebook.com → App DashboardReference only
    App Secretdevelopers.facebook.com → Settings → BasicReference only
    Ad Account IDbusiness.facebook.com → Accounts → Ad Accountsmeta_ad_accounts.ad_account_id
    Access Tokenbusiness.facebook.com → System Users → Generate Tokenmeta_ad_accounts.access_token

    Sync Behavior

    Direction

    Read-only. Gridlink pulls advertising data from Meta but never writes back. Campaign management is done in Meta Ads Manager directly.

    Frequency

    Every 15 minutes via meta-sync.timer systemd timer.

    Lookback Window

  • Insights are synced for the last 7 days by default
  • First sync or manual sync can specify a longer window (e.g., --days 30)
  • Older data persists in the database; only the lookback window is refreshed
  • Rate Limits

    Meta Graph API has variable rate limits based on app tier. The sync engine:

  • Includes 0.5s delay between requests
  • Handles 429 (rate limit) responses with exponential backoff
  • Processes one account at a time
  • Objects Synced

    Meta ObjectMirror TableKey Fields
    Ad Accountsmeta_ad_accountsad_account_id, client_id, access_token, status
    Campaignsmeta_campaignsname, objective, status, effective_status, daily/lifetime budget, bid_strategy
    Ad Setsmeta_adsetsname, campaign_id, status, budget, targeting (JSON), optimization_goal, billing_event
    Adsmeta_adsname, adset_id, campaign_id, status, creative (body, title, link, image, thumbnail)
    Ad Insights (daily)meta_ad_insightsad/adset/campaign_id, date, impressions, clicks, CTR, CPC, CPM, spend, reach, frequency, conversions, video views (25/50/75/100%), actions (JSON)
    Account Insights (daily)meta_account_insightsaccount, date, impressions, clicks, CTR, CPC, CPM, spend, reach, frequency, conversions, actions (JSON)

    Data Model

    Hierarchy

    meta_ad_accounts (linked to client)
    

    └── meta_campaigns

    └── meta_adsets

    └── meta_ads

    └── meta_ad_insights (daily per ad)

    └── meta_account_insights (daily rollup)

    Key Relationships

  • meta_ad_accounts.client_idclients.id (which client owns this ad account)
  • meta_campaigns.meta_account_idmeta_ad_accounts.id
  • meta_adsets.campaign_idmeta_campaigns.campaign_id
  • meta_ads.adset_idmeta_adsets.adset_id
  • meta_ad_insights.ad_idmeta_ads.ad_id
  • Budgets

    Meta returns budgets in cents (e.g., 500000 = $5,000). The sync engine converts to dollars before storing.

    Actions & Conversions

    The actions field is a JSON array of action objects from Meta, e.g.:

    [
    

    {"action_type": "link_click", "value": "42"},

    {"action_type": "landing_page_view", "value": "38"},

    {"action_type": "lead", "value": "5"}

    ]

    The conversions field is a simplified integer count. For detailed breakdowns, query the actions JSON.

    Manual Sync

    # Sync all accounts
    

    python3 /opt/gridlink/backend/meta_sync.py

    Sync specific account (by meta_ad_accounts.id)

    python3 /opt/gridlink/backend/meta_sync.py --account 1

    Extended lookback (30 days of insights)

    python3 /opt/gridlink/backend/meta_sync.py --days 30

    Or via API:

    # Sync all
    

    curl -X POST http://localhost:5000/api/meta-ads/sync

    Sync specific account

    curl -X POST http://localhost:5000/api/meta-ads/sync -H 'Content-Type: application/json' -d '{"account_id": 1}'

    Extended lookback

    curl -X POST http://localhost:5000/api/meta-ads/sync -H 'Content-Type: application/json' -d '{"days": 30}'

    Sync Logs

    All sync operations are logged to sync_log with component_type = 'meta_ads'. Object types:

  • meta_campaigns
  • meta_adsets
  • meta_ads
  • meta_ad_insights
  • meta_account_insights
  • View recent sync history:

    curl http://localhost:5000/api/meta-ads/sync-status
    

    Troubleshooting

    IssueCauseFix
    "access token could not be decrypted"Token corrupted or truncatedRe-enter token in client settings
    "Permissions error"System user lacks ads_readRe-assign permissions in Business Manager
    Empty insightsNo ad spend in lookback windowExtend lookback with --days 30
    Rate limit errorsToo many API callsSync engine auto-retries with backoff

    Last updated: 5/28/2026