Retrieving Leads

You can read leads via TSV download, bulk read, and with Webhooks.

Permissions

You can read leads or real-time updates by using one of these access tokens:

  • Page access token — Allows you to read ad-specific fields, such as ad_id, campaign_id, as long as you have at least advertiser level permissions on the ad account associated with the lead ad.

  • User Access Token — Allows you to read ad-specific fields, such as ad_id and campaign_id, as long as the backing user has an advertiser role on the ad account. To access all of the lead data and the ad-level data, the access token should have manage_pages and ads_management scopes.

Note: If this page admin never customized leads, nor given access permission with the Leads Access Manager, then ALL page admins will have leads access permission. If leads access permission is customized by business admins, then it depends on the business admin's configuration, whether a page basic admin has leads access permission or not.

To retrieve lead information once a lead ID is received via Webhooks, you need ads_management, the Lead Retrieval API leads_retrieval permission, and manage_pages permission. You should submit your app to App Review.

As of February 1, 2019, we will stop sending data collected in Lead Ads forms via Webhooks to apps in Dev Mode.

You can manage user rights with Page roles.


TSV Download

You can directly query a specific lead generation form. The field is labeled leadgen_export_csv_url, although the only supported format is TSV.

use FacebookAds\Object\LeadgenForm;

$form = new LeadgenForm(<FORM_ID>);
$form->read();
from facebookads.adobjects.leadgenform import LeadgenForm

form = LeadgenForm(<LEADGEN_FORM_ID>)
form.remote_read()
curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<FORM_ID>

Response:

{
  "id": "<LEAD_GEN_FORM_ID>",
  "leadgen_export_csv_url": "https://www.facebook.com/ads/lead_gen/export_csv?type=form&amp;id=<FORM_ID>",
  "locale": "en_US",
  "name": "My Form",
  "status": "ACTIVE"
}

Filtering

Optionally you can filter the URL response to download leads for a specified date range. Use from_date and to_date in a POSIX or UNIX time format, expressing the number of seconds since epoch. For example, to download leads for the time period starting 2016-01-13 18:20:31 UTC and ending 2016-01-14 18:20:31 UTC:

https://www.facebook.com/ads/lead_gen/export_csv/?id=<FORM_ID>&amp;type=form&amp;from_date=1482698431&amp;to_date=1482784831

Notes:

  • If from_date is not set, or is a value less than the form creation time, the form creation time is used.
  • If to_date is not set, or is a timestamp greater than the present time, current time is used.

If any entries lack Ad IDs or Adgroup IDs in the TSV, it can be due to the following reasons:

  • The lead is generated from organic reach. In this case, is_organic in the TSV displays 1; otherwise, the value is 0.
  • The lead may be submitted from an Ad Preview.
  • Anyone downloading leads lacks Advertiser privileges on the Ad Account running the Lead ad.

Bulk Read

Apps created after July 2, 2018 are forced to use leads_retrieval permission to read leads.

The leads exists on both ad group and form nodes. This returns all data associated with their respective objects. Because a form can be re-used for many ads, your form could contain far more leads than an ad using it.

To read in-bulk by ad:

curl -X GET \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/{adgroup-id}/leads
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const Ad = bizSdk.Ad; const Lead = bizSdk.Lead; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_GROUP_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 = { }; const leadss = (new Ad(id)).getLeads( fields, params ); logApiCallResult('leadss api call complete.', leadss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\Ad; use FacebookAds\Object\Lead; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_GROUP_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( ); echo json_encode((new Ad($id))->getLeads( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.ad import Ad from facebook_business.adobjects.lead import Lead from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_GROUP_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { } print Ad(id).get_leads( 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_GROUP_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new Ad(id, context).getLeads() .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_GROUP_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad = FacebookAds::Ad.get(id) leadss = ad.leads({ fields: { }, })

To read by form:

curl -G \
  -d 'access_token=&lt;ACCESS_TOKEN&gt;' \
  -d 'fields=created_time,id,ad_id,form_id,field_data' \
  https://graph.facebook.com/&lt;API_VERSION&gt;/&lt;FORM_ID&gt;/leads

The response:

{
  "data": [
    {
      "created_time": "2018-02-28T08:49:14+0000", 
      "id": "<LEAD_ID>", 
      "ad_id": "<AD_ID>",
      "form_id": "<FORM_ID>",
      "field_data": [
        {
          "name": "car_make",
          "values": [
            "Honda"
          ]
        }, 
        {
          "name": "full_name", 
          "values": [
            "Joe Example"
          ]
        }, 
        {
          "name": "email", 
          "values": [
            "joe@example.com"
          ]
        },
      ], 
      ...
    }
  ],
  "paging": {
    "cursors": {
      "before": "OTc2Nz3M8MTgyMzU1NDMy", 
      "after": "OTcxNjcyOTg8ANTI4NzE4"
    }
  }
}

Reading Store Locator Question Value

Store locator question is not any different than any other question. A store locator question will also have a field ID that's going to mapped against them during the form creation. They are also going to be sent similarly as other questions. The value passed will come from the Store Number of the selected location.

For example, let's say you have a store locator question with selected_dealer as the field ID. To fetch the leads in bulk, you can call:

curl -G \
  -d 'access_token=&lt;ACCESS_TOKEN&gt;' \
  -d 'fields=created_time,id,ad_id,form_id,field_data' \
  https://graph.facebook.com/&lt;API_VERSION&gt;/&lt;FORM_ID&gt;/leads

The response:

{
  "data": [
    {
      "created_time": "2018-02-28T08:49:14+0000", 
      "id": "<LEAD_ID>", 
      "ad_id": "<AD_ID>",
      "form_id": "<FORM_ID>",
      "field_data": [
        {
          "name": "car_make",
          "values": [
            "Honda"
          ]
        }, 
        {
          "name": "full_name", 
          "values": [
            "Joe Example"
          ]
        }, 
        {
          "name": "email", 
          "values": [
            "joe@example.com"
          ]
        },
        {
          "name": "selected_dealer", 
          "values": [
            "99213450"
          ]
        }
      ], 
      ...
    }
  ],
  "paging": {
    "cursors": {
      "before": "OTc2Nz3M8MTgyMzU1NDMy", 
      "after": "OTcxNjcyOTg8ANTI4NzE4"
    }
  }
}

Reading Custom Disclaimer Responses

The field_data does not contain the responses to optional custom disclaimer check boxes that the user would have filled out. To retrieve the responses, use the custom_disclaimer_responses field.

curl \
  -F "access_token=<ACCESS_TOKEN>" \
  "https://graph.facebook.com/<API_VERSION>/<LEAD_ID>?fields=custom_disclaimer_responses"

Response:

{
  "custom_disclaimer_responses": [
    {
      "checkbox_key": "optional_1",
      "is_checked": "1"
    },
    {
      "checkbox_key": "optional_2",
      "is_checked": ""
    }
  ],
  "id": "1231231231"
}

Filtering Leads

This example filters leads based on timestamps. Timestamps should be a Unix timestamp.

curl -X GET \ -d 'filtering=[ { "field": "time_created", "operator": "GREATER_THAN", "value": 1555454751 } ]' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/{adgroup-id}/leads
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const Ad = bizSdk.Ad; const Lead = bizSdk.Lead; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_GROUP_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 = { 'filtering' : [{'field':'time_created','operator':'GREATER_THAN','value':1546549613}], }; const leadss = (new Ad(id)).getLeads( fields, params ); logApiCallResult('leadss api call complete.', leadss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\Ad; use FacebookAds\Object\Lead; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_GROUP_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'filtering' => array(array('field' => 'time_created','operator' => 'GREATER_THAN','value' => 1546549613)), ); echo json_encode((new Ad($id))->getLeads( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.ad import Ad from facebook_business.adobjects.lead import Lead from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_GROUP_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'filtering': [{'field':'time_created','operator':'GREATER_THAN','value':1546549613}], } print Ad(id).get_leads( 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_GROUP_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new Ad(id, context).getLeads() .setParam(\"filtering\", \"[{\\"field\\":\\"time_created\\",\\"operator\\":\\"GREATER_THAN\\",\\"value\\":1546549613}]\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_GROUP_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad = FacebookAds::Ad.get(id) leadss = ad.leads({ fields: { }, filtering: [{'field':'time_created','operator':'GREATER_THAN','value':1546549613}], })

Tokenization

If the form has customized field IDs, the fields and values returned will be the specified fields and values.


Webhooks

Get real-time updates when leads are filled out. See Lead Ads with Webhooks, Video.

Many CRMs provide real time updates to migrate Lead ads data into the CRMs. See Available CRM Integration.

Setup

Step 1: Set up endpoint to handle real-time pings

The ping for real-time updates is structured as follows. Read more at Real Time Updates, Blog.

Multiple changes can come in through the ping in the changes array.

array(
  "object" => "page",
  "entry" => array(
    "0" => array(
      "id" => 153125381133,
      "time" => 1438292065,
      "changes" => array(
        "0" => array(
          "field" => "leadgen",
          "value" => array(
            "leadgen_id" => 123123123123,
            "page_id" => 123123123,
            "form_id" => 12312312312,
            "adgroup_id" => 12312312312,
            "ad_id" => 12312312312,
            "created_time" => 1440120384
          )
        ),
        "1" => array(
          "field" => "leadgen",
          "value" => array(
            "leadgen_id" => 123123123124,
            "page_id" => 123123123,
            "form_id" => 12312312312,
            "adgroup_id" => 12312312312,
            "ad_id" => 12312312312,
            "created_time" => 1440120384
          )
        )
      )
    )
  )
)

You can use leadgen_id to retrieve data associated with the lead:

curl -X GET \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/{lead-id}/
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const Lead = bizSdk.Lead; 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 = { }; const sample_code = (new Lead(id)).get( fields, params ); logApiCallResult('sample_code api call complete.', sample_code);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\Lead; 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( ); echo json_encode((new Lead($id))->getSelf( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.lead import Lead 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 = { } print Lead(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 Lead(id, context).get() .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 lead = FacebookAds::Lead.get(id ,'')

Response:

{
  "created_time": "2015-02-28T08:49:14+0000", 
  "id": "<LEAD_ID>", 
  "ad_id": "<AD_ID>",
  "form_id": "<FORM_ID>",
  "field_data": [{
    "name": "car_make",
    "values": [
      "Honda"
    ]
  }, 
  {
    "name": "full_name", 
    "values": [
      "Joe Example"
    ]
  }, 
  {
    "name": "email", 
    "values": [
      "joe@example.com"
    ]
  },
  {
    "name": "selected_dealer", 
    "values": [
      "99213450"
    ]
  }],
  ...
}

Step 2: Subscribe app to leadgen events

To subcribe to leadgen event, your server should respond with HTTP GET requests as described in Receiving API Updates in Real Time with Webhooks.

After your callback URL is set up, subscribe to the leadgen webhook in your Apps's Dashboard or through an API call:

To subscribe through the API, you need an app access token, not a user access token:

curl \
  -F "object=page" \
  -F "callback_url=https://www.yourcallbackurl.com" \
  -F "fields=leadgen" \
  -F "verify_token=abc123" \
  -F "access_token=<APP_ACCESS_TOKEN>" \
  "https://graph.facebook.com/<API_VERSION>/<APP_ID>/subscriptions"

Step 3: Get system user token

Generate a single, long-lived page token to continuously fetch data without worrying about it expiration:

a. Get a regular user token. b. Convert it into a long-lived token c. With the long-lived user's access token, request the page token:

curl -X GET \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/{user-id}/accounts
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const User = bizSdk.User; 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 = { }; const accountss = (new User(id)).getAccounts( fields, params ); logApiCallResult('accountss api call complete.', accountss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\User; 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( ); echo json_encode((new User($id))->getAccounts( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.user import User 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 = { } print User(id).get_accounts( 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 User(id, context).getAccounts() .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 user = FacebookAds::User.get(id) accountss = user.accounts({ fields: { }, })

The response:

{
  "data": [
    {
      "access_token": "[REDACTED]",
      "category": "Pet",
      "name": "Puppy",
      "id": "153125381133",
      "perms": [
        "ADMINISTER",
        "EDIT_PROFILE",
        "CREATE_CONTENT",
        "MODERATE_CONTENT",
        "CREATE_ADS",
        "BASIC_ADMIN"
      ]
    },
  ]
}

This long-lived page token has no expiration date; you can hard-code it in simple RTU integrations to get leads data.

Step 4: Subscribe the page to the app

Starting with Graph API v3.2, we require users to specify the field that the app listens to from the page. When subscripting leadgen with v3.2 or above, you need leads_retrieval permission. For v3.1 or prior, you need manage_page permission.

With the access_token for the page you need to subscribe, make the call below to authenticate an app for your page. You need to have MANAGE_PAGES and LEADS_RETRIEVAL permission to the page in order to perform this action.

curl -X POST \ -F 'subscribed_fields="leadgen"' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/{page-id}/subscribed_apps
'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 = '<PAGE_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 = { 'subscribed_fields' : 'leadgen', }; const subscribed_apps = (new Page(id)).createSubscribedApp( fields, params ); logApiCallResult('subscribed_apps api call complete.', subscribed_apps);
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 = '<PAGE_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'subscribed_fields' => 'leadgen', ); echo json_encode((new Page($id))->createSubscribedApp( $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 = '<PAGE_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'subscribed_fields': 'leadgen', } print Page(id).create_subscribed_app( fields=fields, params=params, )
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<PAGE_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end page = FacebookAds::Page.get(id) subscribed_apps = page.subscribed_apps.create({ subscribed_fields: 'leadgen', })

The Facebook app associated with your token is authenticated for page updates; you don't need to specify app ID.

On success, real-time pings occur on events with a delay of up to a few minutes. See Troubleshooting Real-time Integrations.