368

I am trying to get my friend name and ids with Graph API v2.0, but data returns empty:

{
  "data": [
  ]
}

When I was using v1.0, everything was OK with the following request:

FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
                                              NSDictionary* result,
                                              NSError *error) {
    NSArray* friends = [result objectForKey:@"data"];
    NSLog(@"Found: %i friends", friends.count);
    for (NSDictionary<FBGraphUser>* friend in friends) {
        NSLog(@"I have a friend named %@ with id %@", friend.name, friend.id);
    }
}];

But now I cannot get friends!

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Kaslico
  • 3,707
  • 3
  • 10
  • 10

7 Answers7

631

In v2.0 of the Graph API, calling /me/friends returns the person's friends who also use the app.

In addition, in v2.0, you must request the user_friends permission from each user. user_friends is no longer included by default in every login. Each user must grant the user_friends permission in order to appear in the response to /me/friends. See the Facebook upgrade guide for more detailed information, or review the summary below.

If you want to access a list of non-app-using friends, there are two options:

  1. If you want to let your people tag their friends in stories that they publish to Facebook using your App, you can use the /me/taggable_friends API. Use of this endpoint requires review by Facebook and should only be used for the case where you're rendering a list of friends in order to let the user tag them in a post.

  2. If your App is a Game AND your Game supports Facebook Canvas, you can use the /me/invitable_friends endpoint in order to render a custom invite dialog, then pass the tokens returned by this API to the standard Requests Dialog.

In other cases, apps are no longer able to retrieve the full list of a user's friends (only those friends who have specifically authorized your app using the user_friends permission). This has been confirmed by Facebook as 'by design'.

For apps wanting allow people to invite friends to use an app, you can still use the Send Dialog on Web or the new Message Dialog on iOS and Android.

UPDATE: Facebook have published an FAQ on these changes here: https://developers.facebook.com/docs/apps/faq which explain all the options available to developers in order to invite friends etc.

Simon Cross
  • 13,275
  • 3
  • 28
  • 26
  • Any news since your last edit or we at the same page? – Ilya Gazman Aug 12 '16 at 21:56
  • Can't see the `taggable_friends` in the items for review submission. – AlikElzin-kilaka Apr 17 '17 at 16:25
  • 8
    I can not understand why they had to restrict it this much. I mean the old system was way too abusable but this one is next to useless. Seeing the public profile of all your friends would surely not be a privacy issue. Unless you think that the connection of friendship is private itself (but than again why is it not in the case of taggable friends). This was not a move to restrict privacy abuse that is a side effect, they wanted to close the ability for normal (non facebook affiliated) developers to grow their business using facebook data. – Daniel Sharp Sep 01 '17 at 01:15
  • 3
    Facebook has recently published major changes the available data: see [changelog](https://developers.facebook.com/docs/graph-api/changelog/breaking-changes/#4-4-2018). The `taggable_friends` now returns nothing. Also, login review is required before you can be granted `user_friends` permission. – Duncan Jones Apr 11 '18 at 18:51
  • 1
    What's the point of providing an API if it does not let you do a basic task such as listing the friends? – 6infinity8 Jun 06 '18 at 12:42
  • @DuncanJones so there is no way to get my own friends list using the API? – baptx Feb 04 '20 at 09:30
17

Although Simon Cross's answer is accepted and correct, I thought I would beef it up a bit with an example (Android) of what needs to be done. I'll keep it as general as I can and focus on just the question. Personally I wound up storing things in a database so the loading was smooth, but that requires a CursorAdapter and ContentProvider which is a bit out of scope here.

I came here myself and then thought, now what?!

The Issue

Just like user3594351, I was noticing the friend data was blank. I found this out by using the FriendPickerFragment. What worked three months ago, no longer works. Even Facebook's examples broke. So my issue was 'How Do I create FriendPickerFragment by hand?

What Did Not Work

Option #1 from Simon Cross was not strong enough to invite friends to the app. Simon Cross also recommended the Requests Dialog, but that would only allow five requests at a time. The requests dialog also showed the same friends during any given Facebook logged in session. Not useful.

What Worked (Summary)

Option #2 with some hard work. You must make sure you fulfill Facebook's new rules: 1.) You're a game 2.) You have a Canvas app (Web Presence) 3.) Your app is registered with Facebook. It is all done on the Facebook developer website under Settings.

To emulate the friend picker by hand inside my app I did the following:

  1. Create a tab activity that shows two fragments. Each fragment shows a list. One fragment for available friend (/me/friends) and another for invitable friends (/me/invitable_friends). Use the same fragment code to render both tabs.
  2. Create an AsyncTask that will get the friend data from Facebook. Once that data is loaded, toss it to the adapter which will render the values to the screen.

Details

The AsynchTask

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {

    private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
    GraphObject graphObject;
    ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

    @Override
    protected Boolean doInBackground(FacebookFriend.Type... pickType) {
        //
        // Determine Type
        //
        String facebookRequest;
        if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
            facebookRequest = "/me/friends";
        } else {
            facebookRequest = "/me/invitable_friends";
        }

        //
        // Launch Facebook request and WAIT.
        //
        new Request(
            Session.getActiveSession(),
            facebookRequest,
            null,
            HttpMethod.GET,
            new Request.Callback() {
                public void onCompleted(Response response) {
                    FacebookRequestError error = response.getError();
                    if (error != null && response != null) {
                        Log.e(TAG, error.toString());
                    } else {
                        graphObject = response.getGraphObject();
                    }
                }
            }
        ).executeAndWait();

        //
        // Process Facebook response
        //
        //
        if (graphObject == null) {
            return false;
        }

        int numberOfRecords = 0;
        JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
        if (dataArray.length() > 0) {

            // Ensure the user has at least one friend ...
            for (int i = 0; i < dataArray.length(); i++) {

                JSONObject jsonObject = dataArray.optJSONObject(i);
                FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                if (facebookFriend.isValid()) {
                    numberOfRecords++;

                    myList.add(facebookFriend);
                }
            }
        }

        // Make sure there are records to process
        if (numberOfRecords > 0){
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Boolean... booleans) {
        // No need to update this, wait until the whole thread finishes.
    }

    @Override
    protected void onPostExecute(Boolean result) {
        if (result) {
            /*
            User the array "myList" to create the adapter which will control showing items in the list.
             */

        } else {
            Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
        }
    }
}

The FacebookFriend class I created

public class FacebookFriend {

    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                // Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                // Parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
        }
    }
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
LEO
  • 2,464
  • 24
  • 31
  • 20
    How about for non-gaming apps? A dating website... a game to find people, an app to find restaurants... a game to find restaurants, etc. you get the idea. So is this REALLY close to games or this is just a way of forcing you to place an app inside Facebook and spending money on their advertisement platform? Who's going to decide what a game is? Who decided that your app is actually a game or not? Don't get me wrong on this but for me it makes no sense to block non-gaming apps unless you just want to force them to use the facebook ecosystem. – Mário Nov 17 '14 at 16:14
  • @Mário That is Facebook intention under the hood. You can put anything and mark it as a game as they do not care if it is really a game or not. all they care is getting money from your pocket. – sandalone Apr 20 '17 at 11:57
12

Facebook has revised their policies now. You can’t get the whole friendlist anyway if your app does not have a Canvas implementation and if your app is not a game. Of course there’s also taggable_friends, but that one is for tagging only.

You will be able to pull the list of friends who have authorised the app only.

The apps that are using Graph API 1.0 will be working till April 30th, 2015 and after that it will be deprecated.

See the following to get more details on this:

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jobins John
  • 1,391
  • 25
  • 41
3

In Swift 4.2 and Xcode 10.1:

If you want to get the friends list from Facebook, you need to submit your app for review in Facebook. See some of the Login Permissions:

Login Permissions

Here are the two steps:

1) First your app status is must be in Live

2) Get required permissions form Facebook.

1) Enable our app status live:

  1. Go to the apps page and select your app

    https://developers.facebook.com/apps/

  2. Select status in the top right in Dashboard.

    Enter image description here

  3. Submit privacy policy URL

    Enter image description here

  4. Select category

    Enter image description here

  5. Now our app is in Live status.

    Enter image description here

One step is completed.

2) Submit our app for review:

  1. First send required requests.

    Example: user_friends, user_videos, user_posts, etc.

    Enter image description here

  2. Second, go to the Current Request page

    Enter image description here

    Example: user_events

  3. Submit all details

    Enter image description here

  4. Like this submit for all requests (user_friends , user_events, user_videos, user_posts, etc.).

  5. Finally submit your app for review.

    If your review is accepted from Facebook's side, you are now eligible to read contacts, etc.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
iOS
  • 11,881
  • 4
  • 71
  • 92
  • 3
    The `user_friends` permission is very limited. From Facebook's documentation (April, 2019): _[This only] grants an app permission to access a list of friends that also use said app. This permission is restricted to a limited set of partners and usage requires prior approval by Facebook. In order for a person to show up in another's friend list, both people must have shared their list of friends with the app and not disabled this permission during login._ – dearsina Apr 22 '19 at 05:52
2

As Simon mentioned, this is not possible in the new Facebook API. Pure technically speaking you can do it via browser automation.

  • this is against Facebook policy, so depending on the country where you live, this may not be legal
  • you'll have to use your credentials / ask user for credentials and possibly store them (storing passwords even symmetrically encrypted is not a good idea)
  • when Facebook changes their API, you'll have to update the browser automation code as well (if you can't force updates of your application, you should put browser automation piece out as a webservice)
  • this is bypassing the OAuth concept
  • on the other hand, my feeling is that I'm owning my data including the list of my friends and Facebook shouldn't restrict me from accessing those via the API

Sample implementation using WatiN:

class FacebookUser
{
  public string Name { get; set; }
  public long Id { get; set; }
}

public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
{
  var users = new List<FacebookUser>();
  Settings.Instance.MakeNewIeInstanceVisible = false;
  using (var browser = new IE("https://www.facebook.com"))
  {
    try
    {
      browser.TextField(Find.ByName("email")).Value = email;
      browser.TextField(Find.ByName("pass")).Value = password;
      browser.Form(Find.ById("login_form")).Submit();
      browser.WaitForComplete();
    }
    catch (ElementNotFoundException)
    {
      // We're already logged in
    }
    browser.GoTo("https://www.facebook.com/friends");
    var watch = new Stopwatch();
    watch.Start();

    Link previousLastLink = null;
    while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
    {
      var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
                       && l.GetAttributeValue("data-hovercard").Contains("user.php")
                       && l.Text != null
                     ).LastOrDefault();

      if (lastLink == null || previousLastLink == lastLink)
      {
        break;
      }

      var ieElement = lastLink.NativeElement as IEElement;
      if (ieElement != null)
      {
        var htmlElement = ieElement.AsHtmlElement;
        htmlElement.scrollIntoView();
        browser.WaitForComplete();
      }

      previousLastLink = lastLink;
    }

    var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
      && l.GetAttributeValue("data-hovercard").Contains("user.php")
      && l.Text != null
    ).ToList();

    var idRegex = new Regex("id=(?<id>([0-9]+))");
    foreach (var link in links)
    {
      string hovercard = link.GetAttributeValue("data-hovercard");
      var match = idRegex.Match(hovercard);
      long id = 0;
      if (match.Success)
      {
        id = long.Parse(match.Groups["id"].Value);
      }
      users.Add(new FacebookUser
      {
        Name = link.Text,
        Id = id
      });
    }
  }
  return users;
}

Prototype with implementation of this approach (using C#/WatiN) see https://github.com/svejdo1/ShadowApi. It is also allowing dynamic update of Facebook connector that is retrieving a list of your contacts.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ondrej Svejdar
  • 19,236
  • 4
  • 49
  • 77
  • 2
    Your code assumes that the app has access to the user's username and password. This isn't how OAuth works, and most users aren't going to provider their FB username and password to an application. That would be a huge security risk. – jbustamovej Jul 27 '14 at 07:01
  • 32
    This is against Facebook's policy. This advice should not be followed. – Simon Cross Aug 06 '14 at 20:40
  • @Ondrej - I agree that this is critical for business, and I am struggling with not having this ability. That said, this is a horrible answer!! This is not only against Facebook's policy, it's completely against OAuth. The whole point of OAuth is to keep a user's password safe while authenticating them. I'm just planning on pushing users to use Twitter and Google to get at this data. I am still going to have Facebook OAuth. Overtime, I am hoping that more and more users use my app, the expected functionality will come. – Rogala Aug 31 '14 at 22:06
  • 3
    On another note, you're not even safe guarding your user's credentials in your hack. Just in case someone decides to use your code, please make sure it uses SSL by changing the uri to https. – Rogala Aug 31 '14 at 22:10
  • 1
    @OndrejSvejdar - I wouldn't say this is an evil corporate policy. Considering the backlash Facebook has received in regards to privacy, I believe this is them listening to their users (the friends who have their information shared without their consent.) Although it's painful for me, I can understand there logic. I am sure that another driving factor is the cost associated with buying up all the start-ups that base their infrastructure on Facebook's data. I do hope that Facebook adds some type of functionality that safe guards the users data, while enabling some type of messaging system. – Rogala Sep 02 '14 at 12:46
  • "you don't have to care about facebook policies" seriously? :D – Khurram Nov 15 '14 at 15:54
  • 8
    Plus one for shoving it up their face! – Skynet Mar 18 '15 at 07:16
  • 12
    You all sound like Facebook policies are some kind of International Law and by breaking it you are a criminal. Also you think Facebook has only the best for the user in mind, which I think is wrong like @OndrejSvejdar stated, because then they would just let you choose if you want to be seen in other apps. I think this is just a move by Facebook to disrupt the growth of others through their platform FOR FREE. Instead people should start developing their apps, services, games, news etc directly on Facebook. And pay for Facebook advertising when if they won't. – JustGoscha Apr 16 '15 at 15:04
  • 2
    It might be an idea to grab the data from the mobile site instead: https://m.facebook.com/friends The mobile version is much simpler and uses less bandwidth. – Danny Jul 17 '15 at 21:52
  • Also, you probably won't need a username and password if your friends lists parser is part of a browser extension. The app should work as long as the user's browser is set to a Facebook page. – Danny Jul 17 '15 at 22:08
  • @jbustamovej if someone wants to backup their friends list without entering their credentials in a third-party app, I made a script that can be used in a web browser: https://gist.github.com/baptx/1525f338d93fa01db4e0 – baptx Feb 04 '20 at 11:20
2

Try /me/taggable_friends?limit=5000 using your JavaScript code

Or

try the Graph API:

https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • 2
    As I think - downvotes here are because "Use of this endpoint requires review by Facebook and should only be used for the case where you're rendering a list of friends in order to let the user tag them in a post." You can read more about this in selected answer. – moonvader Apr 14 '17 at 09:05
  • { "data": [ ], "summary": { "total_count": 414 } } i got this json data block null not get friends. What can i do? – Makvin Feb 20 '19 at 07:05
-1

In the Facebook SDK Graph API v2.0 or above, you must request the user_friends permission from each user in the time of Facebook login since user_friends is no longer included by default in every login; we have to add that.

Each user must grant the user_friends permission in order to appear in the response to /me/friends.

let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
fbLoginManager.logIn(withReadPermissions: ["email","user_friends","public_profile"], from: self) { (result, error) in
    if (error == nil) {

        let fbloginresult : FBSDKLoginManagerLoginResult = result!
        if fbloginresult.grantedPermissions != nil {
            if (fbloginresult.grantedPermissions.contains("email")) {
                // Do the stuff
            }
            else {
            }
        }
        else {
        }
    }
}

So at the time of Facebook login, it prompts with a screen which contain all the permissions:

Enter image description here

If the user presses the Continue button, the permissions will be set. When you access the friends list using Graph API, your friends who logged into the application as above will be listed

if ((FBSDKAccessToken.current()) != nil) {
    FBSDKGraphRequest(graphPath: "/me/friends", parameters: ["fields" : "id,name"]).start(completionHandler: { (connection, result, error) -> Void in
        if (error == nil) {

            print(result!)
        }
    })
}

The output will contain the users who granted the user_friends permission at the time of login to your application through Facebook.

{
    data = (
             {
                 id = xxxxxxxxxx;
                 name = "xxxxxxxx";
             }
           );
    paging = {
        cursors = {
            after = xxxxxx;
            before = xxxxxxx;
        };
    };
    summary = {
        "total_count" = 8;
    };
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Lineesh K Mohan
  • 1,584
  • 12
  • 18