Insights API, Limits & Best Practices

Facebook Insights API provides performance data from Facebook marketing campaigns. To protect system performance and stability, we have protective measures so we fairly distribute system resources among applications. All policies we describe below are subject to change.

Data Per Call Limits

We use data-per-call limits to prevent a query from retrieving too much data beyond what the system can handle. There are 2 types of data limits:

  1. By number of rows in response, and
  2. By number of data points required to compute the total, such as summary row.

These limits apply to both sync and async /insights calls, and we return an error:

error_code = 100,  CodeException (error subcode: 1487534)

Best Practices, Data Per Call Limits

  • Limit your query by limiting the date range or number of ad ids. You can also limit your query to metrics that are necessary, or break it down into multiple queries with each requesting a subset of metrics.
  • Avoid account-level queries that include high cardinality breakdowns such as action_target_id or product_id, and wider date ranges like lifetime.
  • Use /insights edge directly with lower level ad objects to retrieve granular data for that level. For example, first use the account-level query to fetch the list of lower-level object ids with level and filtering parameters. In this example, we fetch all campaigns that recorded some impressions:
curl -G \
-d 'access_token=<ACCESS_TOKEN>' \
-d 'level=campaign' \
-d 'filtering=[{field:"ad.impressions",operator:"GREATER_THAN",value:0}]' \
'https://graph.facebook.com/v2.7/act_<ACCOUNT_ID>/insights'

We can then use /<campaign_id>/insights with each returned value to query and batch the insights requests for these campaigns in a single call:

curl \
-F 'access_token=<ACCESS_TOKEN>' \
-F 'batch=[ \
  { \
    "method": "GET", \
    "relative_url": "v3.0/<CAMPAIGN_ID_1>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
  }, \
  { \
    "method": "GET", \
    "relative_url": "v3.0/<CAMPAIGN_ID_2>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
  }, \
  { \
    "method": "GET", \
    "relative_url": "v3.0/<CAMPAIGN_ID_3>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
  } \
]' \
'https://graph.facebook.com'
  • Use filtering parameter only to retrieve insights for ad objects with data. The field value specified in filtering uses DOT notation to denote the fields under the object. For example:
curl -G \
-d 'access_token=<ACCESS_TOKEN>' \
-d 'level=ad' \
-d 'filtering=[{field:"ad.impressions",operator:"GREATER_THAN",value:0},]' \
'https://graph.facebook.com/v3.0/act_<ACCOUNT_ID>/insights'
  • Use date_presets if possible. Custom date ranges are less efficient to run in our system.
  • Use batch requests for multiple sync calls and async to query for large volume of data to avoid timeouts.
  • Try sync calls first and then use async calls in cases where sync calls timeout
  • Insights refresh every 15 minutes and do not change after 28 days of being reported

Insights Call Load Limits

We use load limits for optimal reporting experience for all our partners. We measure API calls for their rate as well as the load they generate on our end. We allow a fixed load limit per application per second. When that limit is exceeded, application requests will fail. Observe the X-FB-Ads-Insights-Throttle HTTP header returned with every API response to know how close your app is to its limit as well as to estimate how heavy a particular query may be.

Once an app reaches its limit, the call will get an error response with error_code = 4, CodedException. It is always advisable to stay comfortably below your limit. However, in the event of an application hitting its allowed limits, only a certain percentage of requests will go through, depending on the query, and the rate. Rate limiting is applied to each app sending sync and async /insights calls combined. The 2 main parameters limits are counted against are by application, and by ad account.

Here's an example of the HTTP header communicating an application's accrued score in percent against set limits:

X-FB-Ads-Insights-Throttle: { "app_id_util_pct": 100, "acc_id_util_pct": 10 }

The header "X-FB-Ads-Insights-Throttle" is a JSON value containing these info:

  • app_id_util_pct The percentage of allocated capacity for the associated app_id has consumed.
  • acc_id_util_pct The percentage of allocated capacity for the associated ad account_id has consumed.

Best practices to work with rate limits

  • Sending burst queries at once are more likely to trigger our rate limiting. Try to spread more evenly your /insights queries by pacing them with wait time in your job.
  • Use the rate info in the HTTP response header to moderate your calls. Put in place a back-off mechanism to slow down or pause your /insights queries when you come close to hitting 100% utility on either your application, or on the specific ad account.
  • Ad insights data are reported using the ad account timezone. To retrieve insights data for the associated ad account daily, try to account for the time of day using the account timezone. This would help smooth queries out throughout the day.

Insights API Asynchronous Jobs

Fetch stats on many objects and apply filtering and sorting; we made the asynchronous workflow simpler:

  1. Send a POST request to <AD_OBJECT>/insights endpoint, which responds with the id of an Ad Report Run.
{
  "report_run_id": 6023920149050,
}
    

Do not store the report_run_id for long term use, it expires after 30 days.

  1. Ad Report Runs contain information about this asynchronous job, such as async_status. Poll this field until async_status is Job Completed and async_percent_completion is 100.
{
  "id": "6044775548468",
  "account_id": "1010035716096012",
  "time_ref": 1459788928,
  "time_completed": 1459788990,
  "async_status": "Job Completed",
  "async_percent_completion": 100
}
    
  1. Then you can query <AD_REPORT_RUN_ID>/insights edge to fetch the final result.
{
  "data": [
    {
      "impressions": "9708",
      "date_start": "2009-03-28",
      "date_stop": "2016-04-04"
    },
    {
      "impressions": "18841",
      "date_start": "2009-03-28",
      "date_stop": "2016-04-04"
    }
  ],
  "paging": {
    "cursors": {
      "before": "MAZDZD",
      "after": "MQZDZD"
    }
  }
}
    

This job gets all stats for the account and returns an asynchronous job ID:

use FacebookAds\Object\AdReportRun;
use FacebookAds\Object\Campaign;
use FacebookAds\Object\Values\AdsInsightsLevelValues;

$campaign = new Campaign(<CAMPAIGN_ID>);

$params = array(
  'level' => AdsInsightsLevelValues::CAMPAIGN,
);

/** @var AdReportRun $async_job */
$async_job = $campaign->getInsightsAsync(array(), $params);

$async_job->read();

while (!$async_job->isComplete()) {
  sleep(1);
  $async_job->read();
}

$async_job->getInsights();
from facebookads.adobjects.campaign import Campaign
from facebookads.adobjects.adsinsights import AdsInsights
from facebookads.adobjects.adreportrun import AdReportRun
import time

campaign = Campaign(<CAMPAIGN_ID>)
params = {
    'level': AdsInsights.Level.campaign,
}
async_job = campaign.get_insights(params=params, async=True)

async_job.remote_read()

while async_job[AdReportRun.Field.async_percent_completion] < 100:
    time.sleep(1)
    async_job.remote_read()

time.sleep(1)

print(async_job.get_result())
curl \
  -F 'level=campaign' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<CAMPAIGN_ID>/insights
curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/1000002
curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/1000003/insights

Async Job Status

StatusDescription

Job Not Started

Job has not started yet.

Job Started

Job has been started, but pending to run.

Job Running

Job has started running.

Job Completed

Job has successfully completed.

Job Failed

Job has failed. Consider to revisit your query and try again.

Job Skipped

Job has been expired and skipped. Please resubmit your job and try again.

Export Reports

We provide a convenience endpoint for exporting <AD_REPORT_RUN_ID> to a localized human-readable format.

Note: this endpoint is not part of our versioned Graph API and therefore does not conform to its breaking-change policy. Scripts and programs should not rely on the format of the result as it may change unexpectedly.

  curl -G \
  -d 'report_run_id=<AD_REPORT_RUN_ID>' \
  -d 'name=myreport' \
  -d 'format=xls' \
'https://www.facebook.com/ads/ads_insights/export_report/'
  
NameDescriptionType

name

Downloaded file name.

String

format

Format of file.

Enum{csv,xls}

report_run_id

ID of report to run.

Integer

access_token

Permissions granted by the logged-in user. Provide this to export reports for another user.

String