The Facebook Ads Reporting API is implemented through Meta's Ads Insights API, which belongs to the broader Meta Marketing API.
- Create a Meta developer app, connect the appropriate business assets, and obtain a token with ads_read permission.
- Send a request to act_ACCOUNT_ID/insights, then define the reporting level, fields, date range, attribution settings, and permitted breakdowns.
- Use cursor pagination for multiple response pages and asynchronous reporting when a large query cannot be processed efficiently in one request.
- Automate reporting with a secured server side script, controlled retries, response validation, and scheduled delivery to a database, spreadsheet, or dashboard.
The facebook ads reporting api becomes valuable when campaign reporting grows beyond a few manually exported spreadsheets. A single Meta ad account may contain thousands of campaigns, ad sets, ads, placement combinations, attribution results, and daily performance rows. Through the Meta Ads Insights API, those records can be retrieved systematically, filtered for specific reporting needs, and delivered to dashboards or databases without rebuilding the same Ads Manager report every week. This tutorial explains the complete workflow, from creating a developer app to automating a production ready reporting process, so keep reading until the final implementation checklist.
Facebook Ads Reporting API Tutorial at a Glance
| Reporting component | What you need to know |
|---|---|
| Actual API | The reporting endpoint is the Ads Insights API within the Meta Marketing API |
| Main endpoint | /{ad_account_id}/insights |
| Account ID format | act_ followed by the numeric ad account ID |
| Basic permission | ads_read for reading advertising insights |
| Authentication | A valid access token issued for an authorized Meta user or system user |
| Reporting levels | Account, campaign, ad set, or ad |
| Common metrics | Spend, impressions, reach, clicks, actions, conversions, and cost metrics |
| Large reports | Use cursor pagination and asynchronous reporting |
| Automation | Schedule a script, store raw responses, validate totals, and load the data into a reporting destination |
| Main security rule | Never place a production access token in public code, URLs, screenshots, or browser based applications |
What Is the Facebook Ads Reporting API?

The phrase Facebook Ads Reporting API is widely used by marketers and developers, but it is not the formal name of a separate Meta product. The reporting functionality comes from the Ads Insights API, which operates inside the broader Meta Marketing API, formerly known as the Facebook Marketing API. Meta describes the Insights API as the interface for retrieving advertising performance statistics with configurable parameters, fields, breakdowns, and attribution options.[1]
At a practical level, the API lets an authorized application ask Meta for structured reporting data. A request can return results for an entire ad account or divide them across campaigns, ad sets, individual ads, dates, devices, placements, countries, ages, and other supported dimensions.
The basic reporting path follows this structure:
https://graph.facebook.com/v25.0/act_AD_ACCOUNT_ID/insights
A typical request also includes:
fields=campaign_name,spend,impressions,clicks
date_preset=last_30d
level=campaign
access_token=YOUR_ACCESS_TOKEN
The Graph API is an HTTP based interface, which means the request can be sent through a browser based API explorer during testing, through command line tools such as cURL, or through application code written in Python, PHP, JavaScript, Java, or another language capable of making HTTPS requests.[2]
Facebook Ads Reporting API vs Meta Ads Insights API
The terminology becomes confusing because marketers often use Facebook Ads API, Meta Ads API, Facebook Ads Insights API, and Meta Ads Reporting API interchangeably. These names describe related concepts, but they do not always refer to the same technical scope. Understanding the hierarchy helps prevent incorrect endpoint and permission choices.
The Meta Marketing API is the broader advertising platform interface. It supports operations such as reading campaign structures, creating or updating advertising objects, managing audiences, retrieving creatives, and requesting performance insights.
The Ads Insights API is the reporting part of that platform. It is the API used when the primary goal is to retrieve spend, impressions, reach, clicks, actions, conversion values, and other advertising performance metrics.
The term Facebook Ads Reporting API is therefore a practical search phrase rather than a separate product. In this tutorial, it refers specifically to the Insights endpoint exposed through the Meta Marketing API.
A useful mental model is:
Meta Graph API
Meta Marketing API
Ads Insights API
The distinction also matters for permissions. A reporting integration may only need ads_read, while an application that creates or modifies campaigns may require ads_management and a broader review process.[3]
When Should You Use the API Instead of Ads Manager?
Ads Manager remains the simplest reporting interface for occasional analysis. It provides configurable columns, filters, exports, breakdowns, charts, and comparison tools without requiring a developer app. The API becomes useful when reporting must be repeated reliably or integrated with other business systems.
The decision should be based on operational needs rather than the belief that API data is automatically more accurate. Ads Manager and the API depend on Meta’s reporting systems, so the API mainly changes how the data is selected, transported, stored, and reused.
Use the Facebook Ads Reporting API when you need to:
- Combine data from several Meta ad accounts in one dashboard.
- Refresh reports automatically every day or every hour.
- Store historical snapshots in a warehouse.
- Join advertising costs with CRM, sales, inventory, or revenue data.
- Standardize reporting across clients or business units.
- Retrieve campaign, ad set, and ad results through a repeatable process.
- Create a reporting product for authorized users.
- Preserve raw responses for auditing and reconciliation.
Ads Manager may remain the better option when you only need a monthly report from one account, the report can be exported manually, or nobody on the team can securely maintain the integration. API automation removes repetitive work, but it also introduces responsibility for tokens, version upgrades, error handling, data validation, and privacy.
What You Need Before Getting Started
A successful Facebook Marketing API setup depends on more than writing a request URL. Meta must be able to identify the application, the authorized person or system user, the business assets involved, and the permissions granted to that identity. Missing one of these relationships commonly produces authorization errors even when the access token itself looks valid.
The exact setup may vary according to whether the integration is used only by your own business or offered to external businesses. Meta applies access tiers, App Review requirements, business verification requirements, and testing requirements according to the app’s use case and intended users.[4]
Prepare the following items:
- A personal Facebook account that can access Meta for Developers.
- A Meta Business Portfolio, previously called Business Manager, when business assets or system users are involved.
- Access to the required ad account.
- A Meta developer app configured for the Marketing API use case.
- A valid access token.
- The
ads_readpermission for read only advertising reports. - The numeric ad account ID.
- A secure server or local development environment for the reporting script.
- A destination for the data, such as a database, spreadsheet, data warehouse, or dashboard.
For a small internal test, an app administrator may be able to test the integration with assets they already control. A public product that reads data for other businesses generally requires a more formal configuration, suitable access, App Review, and possibly business verification.
How to Create a Meta Developer App
The developer app is the identity of your integration inside Meta’s platform. It controls settings such as products, permissions, redirect URLs, access tiers, review status, and business association. Creating the app does not automatically grant access to every ad account.
Meta’s interface changes periodically, so button names and navigation labels may differ slightly when you follow these steps. The underlying goal remains the same, create an app and configure the Marketing API use case rather than selecting an unrelated consumer product.
Step 1, Open Meta for Developers
Visit Meta for Developers and sign in with the Facebook account that will manage the application. Complete developer registration when prompted.
Step 2, Create the application
Open the application dashboard and choose the option to create an app. Enter a clear application name and a monitored contact email address.
Step 3, Select the Marketing API use case
Choose a use case that supports managing or accessing advertising data. Meta’s current setup flow may describe this as a Marketing API use case rather than requiring you to select an older app type label.
Step 4, Connect the correct business
Associate the app with the appropriate Meta business when required. This relationship is especially important for production applications, system users, business verification, and access to assets owned by the business.
Step 5, Review the application settings
Confirm the application ID, application secret management, privacy policy information, data deletion instructions, allowed domains, redirect URLs, and use case configuration. Never expose the application secret in frontend code or a public repository.
Meta’s official Marketing API use case workflow separates testing, verification, and App Review requirements. An app intended only for your own controlled assets may have a simpler path than an integration that asks external businesses to authorize access.[4]
How to Get an Access Token and Required Permissions
An access token proves that the request was authorized by an identity with permission to access the requested resource. It is not simply an API password that unlocks all Meta data. The token carries scopes, belongs to an app and identity, and only works when that identity can access the requested ad account.
For reporting, the central permission is generally ads_read. Meta states that ads_read allows an app to access the Ads Insights API for ad accounts owned by, or made accessible to, the authorized user.[3]
Generate a token for development testing
For an initial test, Graph API Explorer can generate a user access token. Select the correct app, request ads_read, approve the authorization dialog, and test the token only against advertising assets that the user is permitted to access.
Graph API Explorer is useful for learning, but a token copied manually from the tool is not a complete production authentication strategy. Tokens can expire, be invalidated, lose permissions, or stop working when access to an asset changes.
Choose the appropriate production token flow
A production implementation may use a user access token or a system user access token, depending on the use case. An internal business integration often benefits from a system user because it represents a controlled business identity rather than depending on an employee’s interactive session.
The correct approach depends on:
- Who owns the app.
- Who owns the ad account.
- Whether external businesses authorize the application.
- Whether the report runs without a person being logged in.
- Whether Meta requires Advanced Access or App Review for the intended use.
- Whether business verification applies.
Verify the token before writing automation
Use Meta’s token debugging tools or an authorized debug request to inspect:
- The application associated with the token.
- The user or system user represented by the token.
- The granted permissions.
- The expiration status.
- Whether the token is currently valid.
A common mistake is to confirm that a token contains ads_read but overlook whether the token’s identity can actually access the target ad account. Permission scope and asset access are separate conditions, and both must be valid.
How to Find Your Facebook Ad Account ID
The reporting endpoint expects an ad account node. Meta represents that node with the prefix act_ followed by the numeric account ID. The advertising interface may display the number with separators for readability, but API requests should use the raw numeric value.
For example, an interface might display an account as 123 456 789 012 345. The API path normally uses act_123456789012345.
You can find the ID through several methods.
Find it inside Ads Manager
Open the relevant account in Ads Manager and check the account selector, account settings, or browser URL. Confirm that the number belongs to the intended account rather than a campaign or business portfolio.
Retrieve accessible accounts through the API
An authorized user can request the ad accounts available to that identity:
curl -G \
"https://graph.facebook.com/v25.0/me/adaccounts" \
--data-urlencode "fields=id,name,account_status,currency,timezone_name" \
--data-urlencode "access_token=YOUR_ACCESS_TOKEN"
A simplified response may resemble:
{
"data": [
{
"id": "act_123456789012345",
"name": "Main Advertising Account",
"account_status": 1,
"currency": "USD",
"timezone_name": "America/New_York"
}
]
}
Do not select an account solely by its name. Names can be duplicated or changed, while the account ID remains the more reliable reporting identifier.
Make Your First Ads Insights API Request
The first request should stay deliberately small. Requesting dozens of metrics, several breakdowns, years of history, and ad level granularity at once makes troubleshooting unnecessarily difficult. A controlled query confirms authentication, asset access, endpoint syntax, and basic response handling.
Start with account level data for a short date range. Once that works, add campaign names, lower reporting levels, action metrics, and breakdowns gradually.
A basic cURL request is:
curl -G \
"https://graph.facebook.com/v25.0/act_123456789012345/insights" \
--data-urlencode "fields=account_id,account_name,spend,impressions,clicks" \
--data-urlencode "level=account" \
--data-urlencode "date_preset=last_30d" \
--data-urlencode "access_token=YOUR_ACCESS_TOKEN"
A response may contain:
{
"data": [
{
"account_id": "123456789012345",
"account_name": "Main Advertising Account",
"spend": "2450.73",
"impressions": "890432",
"clicks": "12491",
"date_start": "2026-06-12",
"date_stop": "2026-07-11"
}
]
}
The precise fields returned depend on the request, API version, account activity, and metric availability. Numeric values are frequently represented as strings, so production code should explicitly convert them to suitable numeric types before performing calculations.
Avoid passing production tokens in URLs that may be stored in browser history, proxy logs, analytics systems, or screenshots. Server side requests should preferably send credentials through secure request handling and prevent token values from appearing in application logs.
Choose the Right Reporting Level
The level parameter determines the unit represented by each row. Choosing the wrong level can make a technically valid response unusable for the intended analysis. It also affects the number of rows and the computational cost of the request.
The commonly used reporting levels are:
Account level
Use level=account for a consolidated total across the selected ad account and time period. This is useful for executive summaries, billing checks, and initial API testing.
Campaign level
Use level=campaign to compare objectives, initiatives, geographic strategies, or campaign groups. Include identifiers as well as names because names can change.
Useful fields include:
campaign_id,campaign_name,spend,impressions,reach,clicks
Ad set level
Use level=adset to analyze audience, budget, schedule, placement, optimization, and bidding group performance. This is often the most useful operational level for paid media teams.
Ad level
Use level=ad to evaluate individual creative delivery and outcomes. Ad level reports create more rows and should be requested with carefully selected fields and date ranges.
A reporting pipeline should store IDs at every available level. Names are valuable for people, while IDs are safer for joins, deduplication, and historical tracking.
Select Metrics, Date Ranges and Breakdowns
The usefulness of a Meta ad account insights request depends on how its parameters interact. Fields define the values returned, the reporting level defines row granularity, the date configuration defines the measurement period, and breakdowns divide rows by supported dimensions. Attribution settings can further affect reported action results.
Meta’s Ads Insights documentation highlights parameters, fields, and breakdowns as central components for building specific reports.[1]
Select stable identification fields
Begin with fields that explain what each row represents:
account_id
account_name
campaign_id
campaign_name
adset_id
adset_name
ad_id
ad_name
date_start
date_stop
Only request identifiers that make sense for the selected level.
Select delivery and cost metrics
Frequently used metrics include:
spend
impressions
reach
frequency
clicks
inline_link_clicks
cpm
cpc
ctr
Do not assume that every click field measures the same behavior. clicks, inline_link_clicks, outbound click related metrics, and action records can represent different user interactions.
Request action and value fields carefully
Conversion results are commonly returned through array based fields such as:
actions
action_values
cost_per_action_type
purchase_roas
A single actions field can contain multiple objects:
[
{
"action_type": "link_click",
"value": "842"
},
{
"action_type": "purchase",
"value": "37"
}
]
Your data model must flatten or preserve these arrays consistently. Do not assume the position of a particular action type inside the array because the order and presence of action records should not be treated as a fixed schema.
Choose an explicit date range
For predictable production jobs, use time_range with since and until dates:
time_range={"since":"2026-07-01","until":"2026-07-07"}
In cURL, URL encode the JSON value:
--data-urlencode 'time_range={"since":"2026-07-01","until":"2026-07-07"}'
Date presets are convenient for testing:
today
yesterday
last_7d
last_30d
this_month
last_month
Explicit dates are safer for backfills, repeatable jobs, and reconciliation because the same request continues to describe the same reporting window.
Use time increment for daily rows
Add:
time_increment=1
This divides the requested range into daily reporting rows. A larger value can group the range into multi day increments, while supported period style values may produce monthly or lifetime reporting depending on the request.
Add breakdowns only when required
Examples of breakdown dimensions include:
age
gender
country
region
publisher_platform
platform_position
device_platform
impression_device
A request might use:
breakdowns=publisher_platform,platform_position
Not every breakdown can be combined with every other breakdown, field, or attribution configuration. Unsupported combinations may fail, return partial results, or produce a different shape than expected. Meta also notes that some Insights metrics and breakdown values are estimated, in development, or both.[5]
Treat attribution settings as part of the report definition
Two reports should not be compared merely because they request the same field names. Attribution windows, action report time, account settings, and platform reporting changes can affect conversion totals.
Store the complete query configuration with the extracted data, including:
- API version.
- Requested fields.
- Reporting level.
- Date range.
- Time increment.
- Breakdowns.
- Attribution related parameters.
- Extraction timestamp.
- Account currency and timezone.
This metadata turns a collection of numbers into an auditable report.
How to Handle Pagination
Meta does not guarantee that every matching row will arrive in one response. Large result sets are divided into pages, and the response may contain a paging object with cursors and a URL for the next page. Ignoring pagination can silently produce incomplete reports.
Meta’s Graph API documentation describes cursor based pagination as the preferred method when it is supported.[6]
A paginated response can resemble:
{
"data": [
{
"campaign_id": "111",
"spend": "125.00"
}
],
"paging": {
"cursors": {
"before": "CURSOR_VALUE",
"after": "CURSOR_VALUE"
},
"next": "https://graph.facebook.com/v25.0/..."
}
}
Your code should:
- Process the current
dataarray. - Check whether
paging.nextexists. - Request the next page.
- Continue until no next page remains.
- Protect the loop from repeated URLs or unexpected infinite pagination.
- Record how many pages and rows were retrieved.
- Validate the final result against expected totals where possible.
Do not construct the next page manually when Meta already returns a valid next URL. Cursor values are opaque and should not be interpreted as row numbers.
When to Use Asynchronous Reporting
A synchronous Insights request waits for Meta to calculate and return the report in the same request cycle. This is convenient for small queries, but a detailed report across long date ranges, many ads, and several breakdowns may require more processing than a direct request can reliably handle.
Asynchronous reporting separates report creation from result retrieval. The application starts a report job, receives a report run identifier, checks its status, and downloads the results after the job completes.
Start an asynchronous report
Send a POST request to the Insights endpoint with async=true:
curl -X POST \
"https://graph.facebook.com/v25.0/act_123456789012345/insights" \
--data-urlencode "fields=campaign_id,campaign_name,adset_id,adset_name,ad_id,ad_name,spend,impressions,clicks" \
--data-urlencode "level=ad" \
--data-urlencode 'time_range={"since":"2026-06-01","until":"2026-06-30"}' \
--data-urlencode "time_increment=1" \
--data-urlencode "async=true" \
--data-urlencode "access_token=YOUR_ACCESS_TOKEN"
The response should include a report run identifier.
Check the report status
Request the report run object and inspect fields such as its processing status and completion percentage. Do not poll continuously. Use a controlled interval and increase the waiting period when processing takes longer.
Download the completed report
After the job reaches a completed state, request the report run’s insights results and follow pagination until every page has been retrieved.
Asynchronous reporting is appropriate when synchronous requests time out, large extracts are repeatedly required, or ad level history produces a substantial row count. It does not remove rate limits, permission rules, pagination requirements, or the need for validation.
Facebook Ads Reporting API Example in Python
A useful Python example should do more than make one request. It should keep credentials outside the source file, request explicit fields and dates, handle pagination, check HTTP errors, and return normalized rows. These practices make the example easier to extend into an automated report.
Install the Requests library when it is not already available:
python -m pip install requests
Create an environment variable for the token:
export META_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
On Windows PowerShell, use:
$env:META_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
The following example retrieves campaign level insights:
import json
import os
import time
from decimal import Decimal, InvalidOperation
from typing import Any
import requests
GRAPH_API_VERSION = "v25.0"
AD_ACCOUNT_ID = "act_123456789012345"
GRAPH_HOST = "https://graph." + "facebook.com"
INSIGHTS_PATH = (
f"/{GRAPH_API_VERSION}/"
f"{AD_ACCOUNT_ID}/insights"
)
BASE_URL = GRAPH_HOST + INSIGHTS_PATH
TOKEN_ENV_NAME = "META_" + "ACCESS_TOKEN"
TOKEN_PARAMETER = "access" + "_token"
ACCESS_TOKEN = os.getenv(TOKEN_ENV_NAME)
if not ACCESS_TOKEN:
raise RuntimeError(
f"{TOKEN_ENV_NAME} is missing from the environment."
)
def fetch_all_pages(
url: str,
params: dict[str, Any],
timeout: int = 60,
max_pages: int = 1000,
max_retries: int = 5,
) -> list[dict[str, Any]]:
"""
Retrieve every available page from the reporting endpoint.
The function follows the next-page URL returned by the API,
retries temporary failures, and stops if pagination becomes
repetitive or exceeds the configured maximum.
"""
rows: list[dict[str, Any]] = []
next_url: str | None = url
next_params: dict[str, Any] | None = params
visited_urls: set[str] = set()
page_count = 0
with requests.Session() as session:
while next_url:
page_count += 1
if page_count > max_pages:
raise RuntimeError(
"Pagination stopped because max_pages was reached."
)
if next_url in visited_urls:
raise RuntimeError(
"Pagination stopped because a URL was repeated."
)
visited_urls.add(next_url)
response: requests.Response | None = None
for attempt in range(1, max_retries + 1):
try:
response = session.request(
method="GET",
url=next_url,
params=next_params,
timeout=timeout,
)
if response.status_code == 429:
wait_seconds = min(
60,
5 * (2 ** (attempt - 1)),
)
time.sleep(wait_seconds)
continue
if 500 <= response.status_code < 600: wait_seconds = min( 60, 2 ** attempt, ) time.sleep(wait_seconds) continue response.raise_for_status() break except requests.RequestException as exc: if attempt == max_retries: raise RuntimeError( "The reporting request failed after " f"{max_retries} attempts." ) from exc wait_seconds = min( 60, 2 ** attempt, ) time.sleep(wait_seconds) if response is None: raise RuntimeError( "No response was received from the reporting service." ) try: payload = response.json() except ValueError as exc: raise RuntimeError( "The reporting service returned invalid JSON." ) from exc if "error" in payload: safe_error = payload["error"] raise RuntimeError( "The reporting service returned an error:\n" + json.dumps( safe_error, indent=2, ensure_ascii=False, ) ) page_rows = payload.get("data", []) if not isinstance(page_rows, list): raise RuntimeError( "The response data field was not a list." ) rows.extend(page_rows) paging = payload.get("paging", {}) if not isinstance(paging, dict): paging = {} next_url = paging.get("next") next_params = None return rows def to_decimal(value: str | int | float | None) -> Decimal:
"""
Convert an API value into Decimal without using floating-point
arithmetic for currency totals.
"""
if value in (None, ""):
return Decimal("0")
try:
return Decimal(str(value))
except (InvalidOperation, ValueError, TypeError):
return Decimal("0")
requested_fields = [
"account_id",
"campaign_id",
"campaign_name",
"spend",
"impressions",
"reach",
"clicks",
"cpm",
"cpc",
"ctr",
"actions",
]
reporting_period = {
"since": "2026-07-01",
"until": "2026-07-07",
}
params: dict[str, Any] = {
TOKEN_PARAMETER: ACCESS_TOKEN,
"fields": ",".join(requested_fields),
"level": "campaign",
"time_range": json.dumps(reporting_period),
"time_increment": 1,
"limit": 500,
}
report_rows = fetch_all_pages(
url=BASE_URL,
params=params,
)
total_spend = sum(
(
to_decimal(row.get("spend"))
for row in report_rows
),
start=Decimal("0"),
)
print(f"Rows retrieved: {len(report_rows)}")
print(f"Total spend: {total_spend}")
output_path = "meta_campaign_insights.json"
with open(
output_path,
"w",
encoding="utf-8",
) as output_file:
json.dump(
report_rows,
output_file,
indent=2,
ensure_ascii=False,
)
print(f"Report saved to: {output_path}")
This example is intentionally conservative. A production implementation should classify retryable and permanent errors, inspect Meta usage headers, use exponential backoff with jitter, emit structured logs, protect secrets with a managed secret store, and write results through an idempotent loading process.
How to Automate the Report
Automation is not complete merely because a script runs on a schedule. A reliable reporting system must retrieve the correct window, avoid duplicate records, detect partial extraction, preserve enough metadata to reproduce the report, and notify someone when the process fails.
The safest approach is to separate extraction, storage, transformation, and presentation. This keeps the raw API response available when a dashboard formula or business definition later changes.
Design an incremental extraction schedule
Daily reporting jobs can retrieve yesterday and a short rolling lookback period. The rolling window helps capture delayed attribution changes and reporting adjustments.
For example:
- Retrieve yesterday for the routine daily report.
- Reprocess the previous seven or fourteen days to capture later conversions.
- Replace or upsert rows using a stable composite key.
- Run a less frequent historical reconciliation over a longer period.
The appropriate lookback depends on the business, attribution configuration, conversion delay, and reporting purpose.
Define a stable row key
A row key may include:
account_id
campaign_id
adset_id
ad_id
date_start
date_stop
breakdown values
attribution configuration
The exact key depends on the reporting level and breakdowns. Without a stable key, repeated extracts may create duplicate records.
Use a scheduler
Common scheduling options include:
- Cron on a secured server.
- A managed cloud scheduler.
- A workflow orchestration platform.
- A serverless scheduled function.
- An internal data pipeline service.
Google Apps Script can work for lightweight spreadsheet reporting, but larger extracts, long execution times, complex pagination, and secure credential management may be better handled by a server side service.
Validate the result before publishing it
At minimum, log and validate:
- Requested account.
- Reporting period.
- API version.
- Requested fields and breakdowns.
- Number of pages.
- Number of rows.
- Total spend.
- Extraction start and finish time.
- Warning and error responses.
- Destination write status.
A job that completes without raising an exception can still produce an empty, partial, or incorrectly scoped report. Business level checks are therefore as important as HTTP status checks.
Common Errors and How to Fix Them
Most Meta Ads API failures fall into a few broad categories, authentication, authorization, invalid parameters, unsupported field combinations, rate limits, unavailable objects, or expired API behavior. Reading the full error object is more useful than reacting only to the HTTP status code.
Store the error code, subcode when present, message, request context, API version, and a correlation identifier when Meta provides one. Remove token values before logging.
Invalid or expired access token
The token may have expired, been revoked, lost required permissions, or become invalid after a password or security change.
Check the token with Meta’s debugging tools, confirm its expiration, and verify that the associated identity still has access to the ad account.
Missing ads_read permission
The application may have generated a token without the correct scope, or the scope may not be available at the required access level for the intended users.
Request only the permissions genuinely required by the use case. Complete App Review, business verification, or access tier requirements when Meta requires them.
Unsupported request
This can happen when the object ID is incorrect, the identity cannot access the object, the API version no longer supports the request, or a field is unavailable at the requested level.
Reduce the request to a minimal endpoint, verify the account ID, remove optional fields, and add parameters back gradually.
Invalid field or breakdown combination
A field may not support the chosen level, breakdown, attribution configuration, or API version.
Test the query with identification fields and one metric. Add breakdowns individually rather than attempting several new dimensions at once.
Too many requests
Meta applies rate limits to protect platform stability. The Marketing API exposes usage information through response headers, including headers associated with ad account usage, business use cases, and Ads Insights throttling.[7]
Respond by:
- Reading the relevant usage headers.
- Reducing unnecessary requests.
- Caching reusable results.
- Consolidating compatible queries.
- Using asynchronous jobs for large reports.
- Adding exponential backoff and jitter.
- Avoiding immediate repeated retries.
- Distributing workloads across appropriate time windows without attempting to bypass platform limits.
Empty results
An empty data array does not always indicate a failed request. The account may have had no delivery during the selected dates, the chosen level may contain no matching objects, or the filters may exclude every row.
Confirm the date range, account timezone, delivery history, reporting level, filters, and account selection.
Why API Numbers May Differ from Ads Manager
A difference between an API report and Ads Manager does not automatically prove that either result is wrong. The comparison may use different attribution settings, timezones, date boundaries, action definitions, filters, breakdowns, conversion timestamps, or data freshness. Even a field with the same visible name may be configured differently.
Meta also identifies some Insights metrics as estimated or in development. Breakdown values can also be estimated, which means they should not always be treated as exact accounting records.[5]
Compare the following settings before investigating a discrepancy:
- Ad account ID.
- Account timezone.
- Account currency.
- Start and end dates.
- Reporting level.
- Time increment.
- Included campaigns or delivery statuses.
- Attribution window.
- Action report time.
- Breakdown dimensions.
- Conversion event definition.
- API version.
- Extraction timestamp.
- Whether Ads Manager includes modeled, estimated, or delayed results.
- Whether either report applies custom filters.
A daily API report should also be compared with a daily Ads Manager export rather than a lifetime total. Daily rows expose the date on which differences begin and make attribution or timezone issues easier to identify.
The reporting system should permit recent dates to be updated. Conversion metrics may change after the initial extraction as additional attributed actions are processed.
Security and Token Management Best Practices
Access tokens can expose sensitive advertising information, including spend, campaign strategy, account structure, and performance data. A token with broader permissions may also enable management actions. It should therefore receive the same operational protection as a database password or cloud service credential.
Security is not only about preventing public disclosure. It also includes limiting who can use the token, reducing its scope, rotating it safely, and being able to revoke access when an employee, contractor, or integration no longer requires it.
Apply these practices:
- Store tokens in environment variables or a managed secret store.
- Never commit tokens to Git.
- Never insert production tokens into public frontend code.
- Never include tokens in tutorials, screenshots, support tickets, or analytics URLs.
- Grant the minimum required permissions.
- Separate development and production credentials.
- Restrict access to the secret at the infrastructure level.
- Redact tokens from logs and exceptions.
- Monitor token validity and permission changes.
- Document ownership and revocation procedures.
- Remove asset access from former team members.
- Rotate credentials after suspected exposure.
- Keep the application secret outside client side code.
- Review Meta’s platform terms and data handling obligations.
- Maintain an inventory of applications, tokens, system users, and connected ad accounts.
For an agency or software platform, tenant isolation is essential. One customer’s token, data, cache, or report output must never become accessible to another customer.
Is the Facebook Ads Reporting API Worth Using?
The Facebook Ads Reporting API is worth using when reporting is repetitive, multi account, integrated with business data, or expected to operate without manual exports. It provides flexible access to Meta ad account insights, but its real value comes from the surrounding system, secure authentication, carefully defined queries, pagination, scheduled extraction, validation, and maintainable storage.
The strongest implementation begins with one small verified request. It then adds explicit dates, stable identifiers, required metrics, supported breakdowns, pagination, asynchronous processing, retries, monitoring, and reconciliation in controlled stages.
The API is not a shortcut around reporting discipline. It makes reporting logic executable, which means every definition and mistake can also be repeated automatically. Build the first report slowly, save its full configuration, compare it with Ads Manager, and only then schedule it at scale.
Have you already tried to pull Facebook Ads data through the Meta Ads Insights API, or are you still blocked by app setup, permissions, access tokens, or report discrepancies? Leave a comment with the step you are working on, and share the exact error without exposing your token so the issue can be discussed safely.
References
- Meta for Developers, Ads Insights API
- Meta for Developers, Graph API Overview
- Meta for Developers, Permissions Reference
- Meta for Developers, Marketing API Use Cases
- Meta for Developers, Ad Account Insights Reference
- Meta for Developers, Paginated Results
- Meta for Developers, Marketing API Rate Limiting
- Meta for Developers, Insights Limits and Best Practices
- Meta for Developers, Marketing API Authorization
- Meta for Developers, Graph API Versions
Pertanyaan yang Sering Diajukan
Ready to apply this to your business?
Let's Talk Strategy →