Storing and Retrieving Ad Objects

Ad Object Status

Ad Campaign, Ad Set and Ads usually fall into one of following status types:

  • Live
  • Archived
  • Deleted

Live Objects

Live ad objects consists of following status:

  • ACTIVE
  • PAUSED (ADSET/CAMPAIGN)
  • PENDING_REVIEW
  • CREDIT_CARD_NEEDED
  • PREAPPROVED
  • DISABLED

Archived Objects

You can set the ad object status to ARCHIVED by setting the status field to ARCHIVED.

When an object status is set to ARCHIVED you can continue to query the details and stats based on the object id. However there is maximum limit on the number of objects you can archive.

So we recommend your tools to be aware of this decision making and change the status to DELETED when an object is no longer needed.

An ARCHIVED object has only two alterable fields: name and status. And the status can only be changed to DELETED.

Deleted Objects

You can set the ad object status to DELETED by either setting the status field to DELETED or sending an HTTP DELETE request to that object. Once an object status is set to be DELETED it cannot be set back to ARCHIVED.

If you keep the deleted object id, you can continue to retrieve the stats or object details by individually querying the object ID. However you cannot retrieve the deleted objects as a connection object from a non deleted node/object. e.g. <API_VERSION>/<AD_ID>/insights works for a deleted object but <API_VERSION>/act_<AD_ACCOUNT_ID>/insights?level=ad will not return stats for the deleted object.

However, note that if you delete an ad, it may will still track impressions, clicks, and actions for 28 days after the date of last delivery. One particular scenario to be aware of is if you have an ad set, two ads within it, and you delete one ad, the following two queries will not return the same results:

https://graph.facebook.com/&lt;API_VERSION>/<AD_SET_ID>/insights
https://graph.facebook.com/&lt;API_VERSION>/<AD_ID>/insights

The ad set will return stats for both the deleted and the non-deleted ads within it, even though when querying for the ads within the set you will only see one ad:

https://graph.facebook.com/<API_VERSION>/<AD_SET_ID>/ads

To avoid the above scenario the best practice is to delete ads 28 days after their last date of delivery to ensure stats will no longer change. Also you need to store the stats or ids of those objects in your own system before you delete them. You do not need to follow this suggestion if your application does not show the breakdown of stats, or you think it is OK for the sum of break down of stats not to match that of the parent object, due to some deleted child objects.

No field, except name, of a DELETED object can be changed.

How it works

How will the flow work?

  • You create ad objects, they go live and start delivering
  • When you call delete an object it will automatically be deleted.
  • When the limit for achived object is reached, you can no longer archive an object anymore.
  • You need to move these archived deleted objects to deleted state to reduce the limit.

Setting the status on ad objects works in top to down manner:

  • If you set the status to paused/archived/deleted for a campaign, all the objects below it will automatically inherit that status. Example, if the ad campaign was set for deletion, you cannot retrieve the ad sets or ads below that campaign without explicitly specifying the ids.
  • If you set the ad status to paused/archived/deleted, the ad set or ad campaign containing that ad will keep its original status and will be available for retrieval.

The following limits apply to ARCHIVED objects for given ad account:

  • 100k for Ad Campaigns
  • 100k for Ad Sets
  • 100k for Ads

The read API calls for archived edges need to specifically filter for the archived objects. Archived edges won't be returned by default. The read API calls for stats will include the stats of all its children objects, no matter they are active, archived, or deleted. No filter is needed for insights.

Comparisons of objects of different statuses

Objects of live statuses (ACTIVE, PAUSED, etc), ARCHIVED status, and DELETED status differs in multiple aspects. Here is a list of some of those differences.

Query Live `ARCHIVED` `DELETED`

Exists in database

Yes

Yes

Yes

Maximum number per ad account

With limits

50,000

No limit

Query as edges w/o filter

Yes

No

No

Query as edges with status filter

Yes for objects of status contained in the filter

Yes if status filter contains ARCHIVED.

No if status filter does not contain DELETED, and error if it does.

Query by its own ID

Yes

Yes

Yes

Stats aggregated in /<PARENT_OBJECT_ID>/insights

Yes

Yes

Yes

Stats included in the result list of /<PARENT_OBJECT_ID>/insights?level=<OBJECT_LEVEL>

Yes

No

No

Stats included in the result list of /<PARENT_OBJECT_ID>/insights with delivery_info filtering

Yes for objects of status contained in the filter

Yes for objects of status contained in the filter

No

Insights shown with /<OBJECT_ID>/insights

Yes

Yes

Yes

Status can be changed to

Any valid status

DELETED

Cannot change

Examples

To set an ad to be archived:

use FacebookAds\Object\Ad;

$ad = new Ad(<AD_ID>);
$ad->archive();
from facebookads.adobjects.ad import Ad

ad = Ad(ad_id)
ad.remote_archive()
new Ad(<AD_ID>, context).update()
  .setStatus(Ad.EnumStatus.VALUE_ARCHIVED)
  .execute();
curl \
  -F 'status=ARCHIVED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<AD_ID>

To delete an ad:

use FacebookAds\Object\Ad;

$ad = new Ad(<AD_ID>);
$ad->deleteSelf();
from facebookads.adobjects.ad import Ad

ad = Ad(<AD_ID>)
ad.remote_delete()
new Ad(<AD_ID>, context).update()
  .setStatus(Ad.EnumStatus.VALUE_DELETED)
  .execute();
curl -X DELETE \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<AD_ID>/

To retrieve live sub-objects of a live object, for example, all live ads of an ad campaign, not including ARCHIVED or DELETED ads:

use FacebookAds\Object\Campaign;
use FacebookAds\Object\Fields\AdFields;

$campaign = new Campaign(<CAMPAIGN_ID>);
$ads = $campaign->getAds(array(
  AdFields::NAME,
));

foreach ($ads as $ad) {
  echo $ad->{AdFields::NAME}.PHP_EOL;
}
from facebookads.adobjects.campaign import Campaign
from facebookads.adobjects.ad import Ad

campaign = Campaign(<CAMPAIGN_ID>)
ads = campaign.get_ads(fields=[Ad.Field.name])

for ad in ads:
    print(ad[Ad.Field.name])
APINodeList<Ad> ads = new Campaign(<CAMPAIGN_ID>, context).getAds()
  .requestNameField()
  .execute();
curl -G \
  -d 'fields=name' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<CAMPAIGN_ID>/ads

To retrieve ARCHIVED sub-objects of a live object, for example, all ARCHIVED ads of an ad set, requires the status filter:

use FacebookAds\Object\Campaign;
use FacebookAds\Object\Fields\AdFields;
use FacebookAds\Object\Ad;

$campaign = new Campaign(<CAMPAIGN_ID>);
$params = array(
  AdFields::EFFECTIVE_STATUS => array(
    Ad::STATUS_ARCHIVED,
  ),
);
$ads = $campaign->getAds(
  array(AdFields::NAME),
  $params);

foreach ($ads as $ad) {
  echo $ad->{AdFields::NAME}.PHP_EOL;
}
from facebookads.adobjects.campaign import Campaign
from facebookads.adobjects.ad import Ad

campaign = Campaign(<CAMPAIGN_ID>)
params = {
    Ad.Field.effective_status: [Ad.Status.archived],
}
ads = campaign.get_ads(
    fields=[Ad.Field.name],
    params=params,
)

for ad in ads:
    print(ad[Ad.Field.name])
APINodeList<Ad> ads = new Campaign(<CAMPAIGN_ID>, context).getAds()
  .setEffectiveStatus("[\"ARCHIVED\"]")
  .requestNameField()
  .execute();
curl -G \
  -d 'effective_status=["ARCHIVED"]' \
  -d 'fields=name' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<CAMPAIGN_ID>/ads