Store Traffic Objective

To help your business respond to coronavirus (COVID-19), the store traffic objective is running with some temporary changes. Learn more.

The store visits optimization is currently only available to whitelisted partners. Please contact your Facebook representative for access.

The Store Traffic objective allows you to create Facebook ad campaigns to increase foot traffic and boost sales at your physical stores. You can choose the Store Traffic objective for your campaign if you have a business with multiple stores and you've added your store locations to Facebook.

The store traffic objective helps advertisers achieve the goal of driving customers into physical stores.

Learn more about the Store Traffic Objective, Ads Help Center.

Placements

Available for Facebook desktop and mobile (News Feed, Stories, and Marketplace) and Instagram (Feed and Stories).

Delivery Optimization

These optimization options are available for the store traffic objective:

  • Reach Optimization - Available to all advertisers with access to this API. It optimizes for daily unique reach and shows Impressions as the default metric in Ads Manager reporting.

  • Store Visits Optimization - Available to eligible advertisers. Optimize your ads for the lowest cost per attributed store visit. Increases delivery of your ads to people who are most likely to visit a store. Store visits is the default metric in Ads Manager reports, if available.

Create Dynamic Ads

To create dynamic ads for this objective, your page must use Facebook Locations.

Requirements

  • Campaigns must have objective set to STORE_VISITS.
  • Campaigns must have promoted_object set to the corresponding <PARENT_PAGE_ID>.
  • Ad set promoted_object and targeting must contain a place_page_set_id of a <PAGE_SET_ID>
  • Ad set optimization_goal must be set to STORE_VISITS or REACH
  • Ad set billing_event should be IMPRESSIONS

To create dynamic ads with this objective:

  1. Create a PageSet
  2. Create a Campaign
  3. Create an Ad Set
  4. Create an Ad Creative
  5. Create an Ad

Step 1. Create a PageSet

Facebook uses PageSet to target ads and uses it as the promoted object in your ad.

To create a PageSet:

  1. Collect store locations, which are Facebook pages for each store location and locations for your primary business. Your primary business is known as a main page.
  2. Create a Locations JSON to represent all of your locations.
  3. Create the PageSet with the Locations JSON.

Collect all Store Locations

To create a PageSet, get all locations from the main Facebook page.

<PARENT_PAGE_ID> is the page ID for the main page for all your store locations. It retrieves all store pages and locations for a main page and returns the longitude and latitude for each location:

curl -X GET \ -d 'fields="location{latitude,longitude},is_permanently_closed"' \ -d 'limit=30000' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v7.0/{page-id}/locations
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const Page = bizSdk.Page; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<ID>'; const api = bizSdk.FacebookAdsApi.init(access_token); const showDebugingInfo = true; // Setting this to true shows more debugging info. if (showDebugingInfo) { api.setDebug(true); } const logApiCallResult = (apiCallName, data) => { console.log(apiCallName); if (showDebugingInfo) { console.log('Data:' + JSON.stringify(data)); } }; let fields, params; fields = [ 'location{latitude', 'longitude}', 'is_permanently_closed', ]; params = { 'limit' : '30000', }; const locationss = (new Page(id)).getLocations( fields, params ); logApiCallResult('locationss api call complete.', locationss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\Page; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( 'location{latitude', 'longitude}', 'is_permanently_closed', ); $params = array( 'limit' => '30000', ); echo json_encode((new Page($id))->getLocations( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.page import Page from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAdsApi.init(access_token=access_token) fields = [ 'location{latitude', 'longitude}', 'is_permanently_closed', ] params = { 'limit': '30000', } print Page(id).get_locations( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; import java.util.Arrays; public class SAMPLE_CODE_EXAMPLE { public static void main (String args[]) throws APIException { String access_token = \"<ACCESS_TOKEN>\"; String app_secret = \"<APP_SECRET>\"; String app_id = \"<APP_ID>\"; String id = \"<ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new Page(id, context).getLocations() .setParam(\"limit\", \"30000\") .requestField(\"location{latitude\") .requestField(\"longitude}\") .requestIsPermanentlyClosedField() .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end page = FacebookAds::Page.get(id) locationss = page.locations({ fields: { 'location{latitude''longitude}''is_permanently_closed' }, limit: '30000', })

Sample output:

{
    "data": [
        {
            "location": {
                "latitude": 29.173384,
                "longitude": 48.098807
            },
            "is_permanently_closed": false,
            "id": "1788030244802584"
        },
        {
            "location": {
                "latitude": 29.303635,
                "longitude": 47.937725
            },
            "is_permanently_closed": false,
            "id": "261533444245300"
        },
        {
            "location": {
                "latitude": 29.302303,
                "longitude": 47.933178
            },
            "is_permanently_closed": false,
            "id": "179435399132774"
        },
        {
            "location": {
                "latitude": 29.302591,
                "longitude": 47.931801
            },
            "is_permanently_closed": false,
            "id": "1790317704582144"
        }
    ],
    "paging": {
        "cursors": {
            "before": "MTc4ODAzMDI0NDgwMjU4NAZDZD",
            "after": "MTA4MTU4NjU5NjA5MDA4"
        }
    }
}

Create a Locations JSON

Iterate through each entry in the returned results and verify each location is open for business by checking the is_permanently_closed field.

Get the estimated radius with two GET requests. Alternatively, you can make a batch API call to generate the values below.

Individual Requests

You should make this request using each store page's latitude and longitude from the JSON results. This returns the estimated radius for each location.

curl -X GET \ -d 'type="adradiussuggestion"' \ -d 'latitude=51.5152253' \ -d 'longitude=-0.1423029' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v7.0/search/
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const Page = bizSdk.Page; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<ID>'; const api = bizSdk.FacebookAdsApi.init(access_token); const showDebugingInfo = true; // Setting this to true shows more debugging info. if (showDebugingInfo) { api.setDebug(true); } const logApiCallResult = (apiCallName, data) => { console.log(apiCallName); if (showDebugingInfo) { console.log('Data:' + JSON.stringify(data)); } }; let fields, params; fields = [ ]; params = { 'type' : 'adradiussuggestion', 'latitude' : '51.5152253', 'longitude' : '-0.1423029', }; const sample_code = (new Page(id)).get( fields, params ); logApiCallResult('sample_code api call complete.', sample_code);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\Page; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'type' => 'adradiussuggestion', 'latitude' => '51.5152253', 'longitude' => '-0.1423029', ); echo json_encode((new Page($id))->getSelf( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.page import Page from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'type': 'adradiussuggestion', 'latitude': '51.5152253', 'longitude': '-0.1423029', } print Page(id).get( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; import java.util.Arrays; public class SAMPLE_CODE_EXAMPLE { public static void main (String args[]) throws APIException { String access_token = \"<ACCESS_TOKEN>\"; String app_secret = \"<APP_SECRET>\"; String app_id = \"<APP_ID>\"; String id = \"<ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new Page(id, context).get() .setParam(\"type\", \"adradiussuggestion\") .setParam(\"latitude\", \"51.5152253\") .setParam(\"longitude\", \"-0.1423029\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end page = FacebookAds::Page.get(id ,'')

Batch Requests

You can also batch multiple requests into a single request:

curl \
  -F "access_token=<ACCESS_TOKEN>" \
  -F "include_headers=false" \
  -F "batch=[
    {
      \"method\": \"GET\",
      \"relative_url\": \"/<API_VERSION>/search?type=adradiussuggestion&amp;latitude=29.173384&amp;longitude=48.098807\"
    },
    {
      \"method\": \"GET\",
      \"relative_url\": \"/<API_VERSION>/search?type=adradiussuggestion&amp;latitude=29.303635&amp;longitude=47.937725\"
    }
  ]" \
  "https://graph.facebook.com"
Final Locations JSON

Use the radius and distance_unit in results from the previous calls and the <CHILD_LOCATION_ID> as the page_id, create this JSON; this is the final locations JSON:

[
    {
      "page_id": 1788030244802584,
      "radius": 1,
      "distance_unit": "mile"
    },
    {
      "page_id": 261533444245300,
      "radius": 1,
      "distance_unit": "mile"
    }
]

Create the Pageset with the Locations JSON

The PageSet endpoint is currently only available to whitelisted Partners. Please contact your Facebook representative for access.

You can now create a PageSet with the information in your Locations JSON.

The maximum number of locations in a PageSet is 10000.

Asynchronous Requests

You can make an asynchronous request to create your PageSet. This enables you to create large PageSets with more than 1,000 locations without experiencing timeouts. We recommend you use asynchronous requests whenever you create a Pageset with over 50 locations.

To do that, make your request to https://graph.facebook.com/VERSION/ad_place_page_set_async_request:

curl -X POST \
  -d 'name=test_2' \
  -d 'parent_page=ID_1' \
  -d 'pages=[{\'page_id':ID_2}]' \
  -d 'metadata={"audience":{"size":5000}}' \
  -d 'access_token=<ACCESS_TOKEN>' \
  "https://graph.facebook.com/<API_VERSION>/AD_ACCOUNT_ID/ad_place_page_sets_async/"

You can use /ad_place_page_sets for synchronous requests; however, you should use asynchronous requests for more than 50 locations.

The format for parameters are the same as those you use for synchronous requests.

Inside your PageSet, you can use metadata. This field specifies to use a fixed radius per location for your ads delivery, or to reach a certain audience size. If you select the latter, Facebook automatically calculates a radius per location to reach that number of people.

In this example, the metadata option is set to a desired audience size (described below). See metadata for Radiuses. This returns an ad_place_page_set_async_request ID:

{
  "id": "405738580111111"
}      

Later, you can query that ID with ads_read permission to get the PageSet ID:

curl -i -X GET \
 "https://graph.facebook.com/<API_VERSION>/405738580111111?access_token=ACCESS_TOKEN"

Response:

    {
  "id": "405738580111111", 
  "place_page_set": {
    "id": "555555791481678",
    "name": "test_2"
  },
  "progress": 1
}

Where progress is from 0.0 to 1 and 1 means we completed your request and created a PageSet.

metadata for Radiuses

The metadata field tells Facebook you want to use a fixed radius for your locations or you want Facebook to automatically calculate radiuses per location based on a given audience size. For example, to specify a fixed radius using a synchronous request:

curl -X POST \
  -d 'name=test_2' \
  -d 'parent_page=238219010666666' \
  -d 'pages=[{\'page_id':405936056444444}]' \
  -d 'metadata={"fixed_radius":{"value":5,"distance_unit":"mile"}}' \
  -d 'access_token=ACCESS_TOKEN' \
  "https://graph.facebook.com/<API_VERSION>/AD_ACCOUNT_UD/ad_place_page_sets/"

This means you want Facebook to deliver your ad to people within a 5-mile radius of all locations in your PageSet.

Response:

 {
  "id": "1618547271777777"
}

metadata should be either the fixed or audience JSON objects.

If you use fixed, also provide distance_unit and value:

{
  'fixed_radius': {
     'distance_unit': <distance unit>
     'value': <distance>
  }
}

If you use audience, provide this JSON:

 {
  'audience': {
     'size': <audience size>
     'max_radius': { // optional
       'distance_unit': <distance unit>
       'value': <distance>
     }
  }
}

Best Practices for metadata

  • You must also provide locations; however, do not specify a radius in it. Alternately, if you use the locations parameter and provide radiuses, you should not also provide it for metadata.
  • distance must be mile or kilometer, and must be between 0.7 to 50 for mile, or 1 to 80 for kilometer.
  • size in audience is the number of people in the radius as long as the radius is from 1 to 80 kilometers in length. If you provide max_radius, in the actual radius, we calculate varies between 1 and max_radius.
  • If you specify audience for metadata, you must make your request with the asynchronous endpoint: ad_account_ID/ad_place_page_set_async.

Synchronous Requests

You can still use synchronous requests to create a PageSet:

curl -X POST \
  -d "name=My Grand Pageset" \
  -d "parent_page=<PARENT_PAGE_ID>" \
  -d "pages=<LOCATIONS_JSON_STRUCTURE>" \
  -d "access_token=<ACCESS_TOKEN>" \
 "https://graph.facebook.com/<API_VERSION>/act_<AD_ACCOUNT_ID>/ad_place_page_sets/"

This returns a PageSet ID that you use later:

{
  "id": <PAGE_SET_ID>
}

If the number of pages is too large for a cURL call, you can create a text file with LOCATIONS_JSON_STRUCTURE and pass it to the pages attribute with this flag: -F "pages=&lt;locations_json_structure.txt".


Step 2: Create a Campaign

Create a campaign with objective set to STORE_VISITS and your main page ID as promoted object.

curl -X POST \ -F 'name="Store Traffic Campaign"' \ -F 'objective="STORE_VISITS"' \ -F 'promoted_object={ "page_id": "<PAGE_ID>" }' \ -F 'status="PAUSED"' \ -F 'special_ad_categories=[]' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v7.0/act_<AD_ACCOUNT_ID>/campaigns
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdAccount = bizSdk.AdAccount; const Campaign = bizSdk.Campaign; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_ACCOUNT_ID>'; const api = bizSdk.FacebookAdsApi.init(access_token); const showDebugingInfo = true; // Setting this to true shows more debugging info. if (showDebugingInfo) { api.setDebug(true); } const logApiCallResult = (apiCallName, data) => { console.log(apiCallName); if (showDebugingInfo) { console.log('Data:' + JSON.stringify(data)); } }; let fields, params; fields = [ ]; params = { 'name' : 'Store Traffic Campaign', 'objective' : 'STORE_VISITS', 'promoted_object' : {'page_id':'<pageID>'}, 'status' : 'PAUSED', 'special_ad_categories' : [], }; const campaigns = (new AdAccount(id)).createCampaign( fields, params ); logApiCallResult('campaigns api call complete.', campaigns);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdAccount; use FacebookAds\Object\Campaign; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_ACCOUNT_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'name' => 'Store Traffic Campaign', 'objective' => 'STORE_VISITS', 'promoted_object' => array('page_id' => '<pageID>'), 'status' => 'PAUSED', 'special_ad_categories' => array(), ); echo json_encode((new AdAccount($id))->createCampaign( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adaccount import AdAccount from facebook_business.adobjects.campaign import Campaign from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_ACCOUNT_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'name': 'Store Traffic Campaign', 'objective': 'STORE_VISITS', 'promoted_object': {'page_id':'<pageID>'}, 'status': 'PAUSED', 'special_ad_categories': [], } print AdAccount(id).create_campaign( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; import java.util.Arrays; public class SAMPLE_CODE_EXAMPLE { public static void main (String args[]) throws APIException { String access_token = \"<ACCESS_TOKEN>\"; String app_secret = \"<APP_SECRET>\"; String app_id = \"<APP_ID>\"; String id = \"<AD_ACCOUNT_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdAccount(id, context).createCampaign() .setName(\"Store Traffic Campaign\") .setObjective(Campaign.EnumObjective.VALUE_STORE_VISITS) .setPromotedObject(\"{\\"page_id\\":\\"<pageID>\\"}\") .setStatus(Campaign.EnumStatus.VALUE_PAUSED) .setParam(\"special_ad_categories\", \"[]\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_ACCOUNT_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_account = FacebookAds::AdAccount.get(id) campaigns = ad_account.campaigns.create({ name: 'Store Traffic Campaign', objective: 'STORE_VISITS', promoted_object: {'page_id':'<pageID>'}, status: 'PAUSED', special_ad_categories: [], })

See Ad Campaign, Reference.

Step 3: Create An Ad Set

Create an ad set to contain your ad. See Reference, Ad Set, Reference, Targeting specs and Reference, Page locations.

At delivery time, Facebook invalidates any ads targeting locations more than 50 miles away from the nearest page location, known as local page.

curl \
  -F 'name=Store Visits Ad Set' \
  -F 'promoted_object={"place_page_set_id":"<PAGE_SET_ID>"}' \
  -F 'optimization_goal=STORE_VISITS' \
  -F 'billing_event=IMPRESSIONS' \
  -F 'is_autobid=true' \
  -F 'daily_budget=1000' \
  -F 'campaign_id=<CAMPAIGN_ID>' \
  -F "targeting={
    'place_page_set_ids': ['<PAGE_SET_ID>'],
    'device_platforms': ['mobile','desktop'],
    'facebook_positions': ['feed']
   }" \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/act_<AD_ACCOUNT_ID>/adsets

Geographical Targeting

You can also target by geo_locations in store visits campaigns.

For this objective, you can only use geo_locations or place_page_set_ids in ad set targeting.

We support all types of geo_location targeting in Advanced Targeting and Placement, including targeting by countries, cities, and zip codes. You can also select location_types, such as recent, home, or travel_in.

You should still provide place_page_set_id in the promoted_object. This page set has to be a page set without any explicit set of locations. See Creating the Pageset with the Locations JSON to create this page set. However, in this case, do not pass the parameter pages.

First create a page set you later provide in a promoted object:

curl -X POST \
  -d "name=My geo targeting page set" \
  -d "parent_page=PARENT_PAGE_ID" \
  -d "access_token=ACCESS_TOKEN" \
  "https://graph.facebook.com/<API_VERSION>/act_AD_ACCOUNT_ID/ad_place_page_sets/"

You do not need to provide the pages parameter as you normally do.

You can create an ad set with the store traffic objective, which targets geo_locations:

curl \
  -F 'name=Store Traffic Ad Set' \
  -F 'promoted_object={"place_page_set_id":"<PAGE_SET_ID>"}' \
  -F 'optimization_goal=STORE_VISITS' \
  -F 'billing_event=IMPRESSIONS' \
  -F 'is_autobid=true' \
  -F 'daily_budget=1000' \
  -F 'campaign_id=CAMPAIGN_ID' \
  -F "targeting={
    'geo_locations': {"countries":["US"],"location_types": ["home"]}, 
    'device_platforms': ['mobile','desktop'],
    'facebook_positions': ['feed']
  }" \
  -F 'access_token=ACCESS_TOKEN' \
  https://graph.facebook.com/<API_VERSION>/act_AD_ACCOUNT_ID/adsets

We automatically deliver ads for the store which is closest to the person viewing your ad.


Step 4: Provide an Ad Creative

You can dynamically insert creative based on someone's location. Customize your creative using a set of template placeholders, and Facebook replaces the placeholders in your ads at runtime with data from the nearest page location.

Available placeholders:

  • {{page.hours.today}}
  • {{page.location.city}}
  • {{page.location.region}}
  • {{page.location.postcode}}
  • {{page.location.street_address}}
  • {{page.name}}
  • {{page.phone_number}}

The dynamic_ad_voice field allows you to control the voice of your ad:

  • If dynamic_ad_voice is set to DYNAMIC: Page name and profile picture in your ad post come from the nearest page location.
  • If dynamic_ad_voice is set to STORY_OWNER: Page name and profile picture in your ad post come from the main page location.

Call To Actions

You can also dynamically ad call-to-action buttons (CTAs) based on someone's location:

  • When using GET_DIRECTIONS or CALL_NOW, the CTA value field is not required. Users will automatically be directed to nearest location or prompted to call the nearest location phone number.
  • MESSAGE_PAGE is allowed only if dynamic_ad_voice is set to STORY_OWNER. Messages will be delivered to the main page.
  • Optional field. If not specified for individual ads, we display a Like Page button.

For details, see Reference, Ad Creative

dynamic_ad_voice type in call_to_action

DYNAMIC

CALL_NOW


GET_DIRECTIONS

STORY_OWNER

CALL_NOW


GET_DIRECTIONS


LEARN_MORE


MESSAGE_PAGE


ORDER_NOW


SHOP_NOW

Examples

Provide an ad creative using dynamic page name and city:

curl \
  -F 'dynamic_ad_voice=DYNAMIC' \
  -F 'object_story_spec={ 
    "page_id": "<PARENT_PAGE_ID>", 
    "template_data": { 
      "description": "Ad Description", 
      "link": "<URL>", 
      "message": "Ad Message for {{page.location.city}}", 
      "name": "{{page.name}}", 
      "picture": "<IMAGE_URL>" 
    } 
  }' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/act_<AD_ACCOUNT_ID>/adcreatives

Ad Creative with Map Card

To use a map card, add a place_data structure in a attachment in the child_attachments field for your Ad Creative.

In this example, the map, with a Facebook store locator link, is the second item in the child_attachments array. You must provide have at least one item in addition to the map card:

curl \
  -F 'dynamic_ad_voice=DYNAMIC' \
  -F 'object_story_spec={ 
    "page_id": "<PARENT_PAGE_ID>", 
    "template_data": { 
      "description": "Ad Description", 
      "link": "<URL>", 
      "message": "Ad Message for {{page.location.city}}", 
      "name": "{{page.name}}", 
      "child_attachments":[
        {
          "description": "Come visit us!",
          "link": "http://yourweburl.com",
          "name": "{{page.location.street_address}} - {{page.location.city}}",
          "call_to_action": {
            "type":"GET_DIRECTIONS"
          },
        },
        {
          "link": "https://fb.com/store_locator",
          "name": "Check out our stores.",
          "place_data": {
            "type":"DYNAMIC"
          },
        }
      ]
    } 
  }' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/act_<AD_ACCOUNT_ID>/adcreatives

Store Locator, Link Destination

When you create an ad, if you set the link to 'https://fb.com/store_locator', the ad appears with the store locator as the link destination.


Step 5: Create An Ad

Create an ad as follows:

curl \
  -F 'name=My Ad' \
  -F 'adset_id=<AD_SET_ID>' \
  -F 'creative={"creative_id":"<CREATIVE_ID>"}' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/act_<AD_ACCOUNT_ID>/ads

To create ads for store traffic, your page and ad account must be approved for store visits measurement. Otherwise, an error similar to Reach estimate isn't available because 'store_visits' isn't a valid action type displays.

Store Visits Measurement

Store visits is an estimated metric based on data from users with location services enabled. It ultimately offers store visit measurement and optimization for the Store Traffic objective. Store visit measurement is only available for campaigns with the Store Traffic objective.

Store visits are based on clicks and views to ads using the store traffic objective. It's an estimated number of visits to an advertiser's stores by people who have seen or clicked on each store's ads. You can configure the attribution window; you can choose to customize it based on 1-, 7- or 28-day clicks or views. The ad account's default attribution applies unless you customize the configuration. See Insights API, Attribution Window.

These sections describe features added to:

The features relate to reporting for the following items:

  • Store Visits: The number of estimated visits to your store as a result of your ads
  • Cost per Store Visit: The average cost for each estimated visit to your stores as a result of your ads

With Ads Manager

See columns under ENGAGEMENT: ACTIONS. These columns appear in the reporting interface for Store Visits and the Cost per Store Visit.

Store visit data is only available for stores that Facebook team confirms as measurable for a campaign.

With Insights API

You can get data on store visits from the Insights API. We provide these additional fields in the general insights calls: cost_per_store_visit_action and store_visit_actions. See Insights, Reference. For example:

curl -X GET \ -d 'fields="cost_per_store_visit_action,store_visit_actions"' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v7.0/<AD_SET_ID>/insights
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdSet = bizSdk.AdSet; const AdsInsights = bizSdk.AdsInsights; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_SET_ID>'; const api = bizSdk.FacebookAdsApi.init(access_token); const showDebugingInfo = true; // Setting this to true shows more debugging info. if (showDebugingInfo) { api.setDebug(true); } const logApiCallResult = (apiCallName, data) => { console.log(apiCallName); if (showDebugingInfo) { console.log('Data:' + JSON.stringify(data)); } }; let fields, params; fields = [ 'cost_per_store_visit_action', 'store_visit_actions', ]; params = { }; const insightss = (new AdSet(id)).getInsights( fields, params ); logApiCallResult('insightss api call complete.', insightss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdSet; use FacebookAds\Object\AdsInsights; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_SET_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( 'cost_per_store_visit_action', 'store_visit_actions', ); $params = array( ); echo json_encode((new AdSet($id))->getInsights( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adset import AdSet from facebook_business.adobjects.adsinsights import AdsInsights from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_SET_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ 'cost_per_store_visit_action', 'store_visit_actions', ] params = { } print AdSet(id).get_insights( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; import java.util.Arrays; public class SAMPLE_CODE_EXAMPLE { public static void main (String args[]) throws APIException { String access_token = \"<ACCESS_TOKEN>\"; String app_secret = \"<APP_SECRET>\"; String app_id = \"<APP_ID>\"; String id = \"<AD_SET_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdSet(id, context).getInsights() .requestField(\"cost_per_store_visit_action\") .requestField(\"store_visit_actions\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_SET_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_set = FacebookAds::AdSet.get(id) insightss = ad_set.insights({ fields: { 'cost_per_store_visit_action''store_visit_actions' }, })

Sample output looks like:

{
  "data": [
    {
      "cost_per_store_visit_action": [
        {
          "action_type": "store_visits",
          "value": "0.193995",
        }
      ],
      "store_visit_actions": [
        {
          "action_type": "store_visits",
          "value": "93625",
        }
      ],
      "date_start": "2017-12-01",
      "date_stop": "2017-12-30"
    }
  ],
  "paging": {
    "cursors": {
      "before": "AAAAAA",
      "after": "AAAAAA"
    }
  },
  "__fb_trace_id__": "aaaaaaaaaaa"
}   

This result shows the cost per store visit and the estimated value of the store visits.

FieldDescription
point_estimate
int32

The point prediction of the value