Documentation

Welcome to the restfb documentation.

Here, you will find basic information on how to use the restfb library. While we cover various topics here, we will only provide an overview of the library’s usage. If you require a more in-depth understanding and additional information, please refer to the cookbook. It contains many recipes that cover advanced usage of restfb.

Migration

Important migration information from 1.x to 2.x

For RestFB 2.0.0 we provide a migration guide in which you'll find the notable changes to the RestFB API.

The most important change is the migration to a new JSON parser. With RestFB 2.x we use the very fast minimal-json library. That's the reason why the JsonObject and the other objects found in com.restfb.json have a different API. Please have a look at their GitHub repository to get a sense of how that API looks.

Migration from 2.x to 3.x

The migration from 2.x to 3.x works without major changes in the API. There are some minor changes in the types, but they are all explained in the javadoc.

The biggest change is the Java 8 source level. RestFB 3.0.0 is the first version that needs at least Java 8. You can now use all the fancy Java 8 stuff like streams, lambda functions and so on.

Versioning

At the start of 2022, we decided to switch our versioning scheme for RestFB from pure semantic versioning to calendar versioning. We made this decision to provide developers with a better understanding of when a particular release was made. With the high frequency of changes in the social media space, we felt that it was important to provide more context around each release.

Under our new versioning scheme, the major version number indicates the year of the release, while the minor version number corresponds to the releases within the year.
For example, the 9th release of 2022 would be identified as version 2022.9.0.

We want to assure our users that we will continue to maintain our policy of only making breaking changes to the RestFB API when there is a major position change in the version. This means that such changes will only occur at the turn of the year. This helps to minimize the impact of any changes on existing applications.

Fetching

The easiest way to get an object from Facebook is to use the fetchObject method. To use this method, you will need at least the endpoint you want to access and the result type.

The endpoint can be found in the Facebook Graph API Reference. There you will find the correct syntax for the endpoint and how the response json is defined by Facebook. The type you need to provide is a concrete mapping between the json and a java object.

// For all API calls, you will need to tell RestFB how to convert the JSON
// returned by Facebook into Java objects. In this case, the data
// we get back should be mapped to the User and Page types, respectively.
// Feel free to write your own types!

User user = facebookClient.fetchObject("me", User.class);
Page page = facebookClient.fetchObject("cocacola", Page.class, 
                 Parameter.withFields("fan_count"));

out.println("User name: " + user.getName());
out.println("Page likes: " + page.getFanCount());

See Graph API documentation

If you want to fetch several objects at once and these objects are very similar, you can use the “multiple ids” request. A simple way to fetch two user objects is a call like:

JsonObject fetchObjectsResults =
  facebookClient.fetchObjects(Arrays.asList("me", "123456789"), 
           JsonObject.class, Parameter.withFields("name,id"));

As you can see, you use the fetchObjects method and supply a list of ids as first parameter. You get a JsonObject as a response with the previously provided ids as names in this JsonObject.

{
  "me": {
    "name": "My User",
    "id": "98765432"
  },
  "123456789": {
    "name": "Example User",
    "id": "123456789"
  }
}

If you prefer to work with a strongly-typed object instead of a basic JsonObject, you can define your own. For example, we fetch the user me and the page cocacola. You may even mix different ids. But please pay attention to the fields you request: you may only request intersecting fields, otherwise you will receive an error response.

FetchObjectsResults fetchObjectsResults =
  facebookClient.fetchObjects(Arrays.asList("me", "cocacola"), 
         FetchObjectsResults.class);

out.println("User name: " + fetchObjectsResults.me.getName());
out.println("Page likes: " + fetchObjectsResults.page.getLikes());

...

// Holds results from a "fetchObjects" call.
// You need to write this class yourself!

public class FetchObjectsResults {
  @Facebook
  User me;

  // If the Facebook property name doesn't match
  // the Java field name, specify the Facebook 
  // field name in the annotation.

  @Facebook("cocacola")
  Page page;
}

Additionally, you may work with edges. If all ids have the requested edge you’ll get the requested data. If one id is missing this edge, you’ll get an error instead.

GET graph.facebook.com
  /photos?ids={user-id-a},{user-id-b}

The request is equivalent to these requests:

GET graph.facebook.com
  /{user-id-a}/photos
  
GET graph.facebook.com
  /{user-id-b}/photos

Summary of multiple ids misusage

  1. ids with non intersecting fields requested: Only intersecting fields are allowed.
  2. Edges are requested that are not part of all ids.
  3. More than 50 ids requested: Only 50 ids are allowed in one request.

Multiple ids and the request limit

Although you make only one request, Facebook counts every id. If you provide e.g. 10 ids, the single request is counted as 10.

You should check your implementation: If you have many ids of the same type, a "multiple ids" request can be the better solution. On the other hand, it makes more sense to send single requests if the ids have different types.

Before we can get to the RestFB implementation details, we have to explain some fundamentals. Some requests - like a post’s comments or a user’s feed - are returned as a list. Because these lists can be very large, Facebook provides a paging mechanism. This means you receive only a limited amount of items encapsulated in a single JSON response (a “page”). To get access the next page, you have to call a special URL which is provided for you as part of the page data. You can iterate over all pages by calling these urls. The relevant field in the paging JSON is called next.

Every page includes the data you requested - comments, posts, accounts and so on. To access this data, you have to iterate over the list on the page. It is important to mention that there is a limit query parameter. It limits the elements on one page. Changing the limit parameter does not change the overall number of items. You can still iterate over all pages, but you can change the amount of requests needed for this. For example, if you’d like to query for a maximum of 50 posts, you should use a counter and stop both loops as soon as no new items are found or your limit is reached (source code is further below).

RestFB supports paging, but you need to fetch the endpoint with the special FacebookClient.fetchConnection() method. One page of a pageable result set is called a Connection. Now you can iterate over the Connection and you’ll get a lot of lists of the fetched element. The loading of the next list is automatically done by RestFB so you don’t need to know how the URLs are built.

Use fields to change the configuration of the returned objects

Fetching lists of objects supports this technique to specify which fields should be returned. To get better insight how the fields parameter can be used, check out the Advanced Usage section.

A common use case is to fetch all elements of a specific endpoint and work with those items. You can manage this by nested iteration. So you iterate over the connections and iterate over the list in one connection. This sounds a bit complicated, but the following source code example shows how it works.

Connection<Post> myFeed = facebookClient.fetchConnection("me/feed", Post.class);
// Connections support paging and are iterable

// Iterate over the feed to access the particular pages
for (List<Post> myFeedPage : myFeed) {

  // Iterate over the list of contained data 
  // to access the individual object
  for (Post post : myFeedPage) {
    out.println("Post: " + post);
  }
}

The Connection object implements the Iterable interface. With this interface it is possible to get an iterator object or you can simply use the foreach loop as you can see in the code example above.

We already mentioned the limit parameter and in the following example we show how to use it and how you can use a personal limit.

String postId = "12345"
Connection<Comment> commentConnection 
   = facebookClient.fetchConnection(postId + "/comments", 
         Comment.class, Parameter.with("limit", 10));

int personalLimit = 50;

for (List<Comment> commentPage : commentConnection) {
  for (Comment comment : commentPage) {
    out.println("Id: " + comment.getId());
    personalLimit--;

    // break both loops
    if (personalLimit == 0) {
       return;
    }
  }
}

In some rare cases you may need to prevent RestFB from automatically loading further results and exert deeper control over what the Connection object is doing. To do this, you must not use the normal iteration process and use a different approach instead. You can simply work with the very common iterator pattern as mentioned above. Or you can use the Connection.hasNext() and Connection.next() methods on a connection object. Together with the Connection.getData() you have complete control over the object.

We don't suggest using this, ...

... because it makes the complete process of fetching items from Facebook unnecessarily complicated and error-prone. Nevertheless, here is a short example of how to use the more complicated way to duplicate the functionality of the first code example in this section.

Connection<Post> myFeed = facebookClient.fetchConnection("me/feed", Post.class);
	
// Get the iterator 
Iterator<List<Post>> it = myFeed.iterator();

while(it.hasNext()) {
   List<Post> myFeedPage = it.next();
	   
   // This is the same functionality as the example above
   for (Post post : myFeedPage) {
     out.println("Post: " + post);
   }
}

Insights work almost like any other connections, but the Insight type contains a list of JsonObject. The reason for this unusual approach is the dynamic nature of the different insights. They returned values are not all of the same structure and not defined in the Graph API reference.

To fetch some page insights you simply have to call the endpoint of a page. But don’t forget to add the metrics parameter if you are using Graph API 2.6 or later. An example of this looks like:

Connection<Insight> insightsConnection = 
	facebookClient.fetchConnection("PAGE_ID/insights", 
	      Insight.class, // the insight type
	      Parameter.with("metric", "page_fan_adds_unique,page_fan_adds"));

for (List<Insight> insights: insightsConnection) {
  for (Insight insight : insights) {
    out.println(insight.getName());
  }
}

Fetching objects with RestFB is straightforward, but there’s a special endpoint that is a bit different and can be a little confusing.

If you call the <userid>/picture endpoint you don’t receive a JSON response object. This endpoint redirects directly to the user picture and you get the image’s binary data.

GET https://graph.facebook.com/v3.0/me/picture

The GET request above returns these header fields.

status: 302
content-type: image/jpeg
pragma: no-cache
location: https://example.fbcdn.net/example_profile_picture.jpg

Now, we want to receive the image url in a JSON instead of the binary data. This can be achieved by adding a special parameter to the request URL. The query parameter is redirect=false.

The corresponding Java code looks like this:

FacebookClient client = new DefaultFacebookClient(accessToken, Version.LATEST);
JsonObject picture = 
      client.fetchObject("me/picture", 
	      JsonObject.class, Parameter.with("redirect","false"));

With this “trick” you receive a JSON response as expected when calling the Facebook Graph endpoint.

{
  "data": {
    "is_silhouette": false,
    "url": "https://example.fbcdn.net/example_profile_picture.jpg"
  }
}

Logging

Before we go any further, a few words about logging...

With RestFB 2.x the logging framework used is slf4j if found in the class path. If slf4j is not found, java.util.logging is used as a fallback.

Both types of logging can be used to log the data that's sent over the wire to and from Facebook. It's often helpful to look at the log output to make sure that RestFB is sending the data as expected and that Facebook is returning the correct JSON.

Because RestFB supports the slf4j logging wrapper directly, it is possible to use any logging framework that works with slf4j. Logging RestFB log messages with your favorite logging framework is no problem. You have to change your logging configuration as you do with new project internal loggers.

RestFB tries to use slf4j. As soon as this logging library is included in the application, RestFB will use slf4j and you can use all the loggings frameworks, that slf4j can use. If no slf4j is found, RestFB uses java.util.logging as fallback. Then RestFB works exactly like the older version from the 1.x branch. RestFB can be forced to use java.util.logging - simply set the system property com.restfb.forceJUL to true. Then slf4j is ignored even though slf4j is on the classpath.

To make it easier for developers to configure our logging, we don’t use loggers that are initialised with the name of the class they were created in. Our loggers have fixed names and therefore the logging is clustered into categories.

com.restfb.HTTP

The HTTP logger is used to keep track of the communication between RestFB and the Facebook Graph API. In the log file you'll find the requests/responses, header information and more.

com.restfb.JSON_MAPPER

The JsonMapper is used to convert JSON to RestFB types. Because the conversion involves many interesting steps, we provide a special logger. When everything is working as it should, the logger is very quiet, but the developer can change the log level to DEBUG or TRACE to get much more information about what’s going on during the mapping process. You should only do this if you run into problems and want to trace them back to a specific conversion step.

com.restfb.UTILITY

The logger is used in our utilities. We have utilities for date conversion, base64 calculations and more. Just have a look at com.restfb.util package. These utilities log some information and use this special logger to not clutter the other logs with less important information.

com.restfb.CLIENT

This logger provides information about the communication between the application and RestFB.

com.restfb.types.CHANGE_VALUE_FACTORY

The log messages in the ChangeValueFactory are used to inform the developer about some problems with the conversion of an object that is received via a webhook. Maybe RestFB does not support this special value object and the developer can capture the log output to send a special String to the RestFB team. As this is different from the normal logging, we provide a special logger and the developer can filter these messages and log them to a different log file.

As you can see, all loggers use the package structure with the com.restfb. prefix. This makes it possible to change the log level of all loggers at once.

# Example logging.properties entries
# ==================================

# to log the call to facebook including their responses
# you can change the com.restfb.HTTP log level accordingly during development time
com.restfb.HTTP.level=ALL

# the complete restfb package logging can be configured like this
com.restfb.level=WARNING

Searching

With RestFB it is very easy to search Facebook. But before we go deeper in this topic, you need to know some basic information. First, Facebook differentiates between the public search and the targeted search. With the public search, you can search for public information and get some useful insights in what’s going on at Facebook. The targeted search is another approach to filter your private information.

The public search uses a special endpoint called search. You have to provide a query string (q) and a type parameter (type). With this technique, you’ll get back data that can be marshaled to the normal RestFB types.

Public search changed on 4 April, 2018

With the breaking change on April 2018, Facebook only allows you to search for `place`, `placetopic`, and `ad_*`. All other types are not longer supported. Be careful - some old tutorials are not updated yet!

The supported public search types are place, placetopic, and ad_*. Some types like place need additional parameters, so you should have a look at the Facebook Graph API Search documentation.

// Searching is just a special case of fetching Connections -
// all you have to do is pass along a few extra parameters.

Connection<User> publicSearch =
  facebookClient.fetchConnection("search", Place.class,
    Parameter.with("q", "coffee"), Parameter.with("type", "place"));

With “targeted search” you are able to search in a user’s home, a.k.a. news feed. You can simply call the /me/home endpoint and add the q parameter as mentioned in the sections above.

Connection<User> targetedSearch =
  facebookClient.fetchConnection("me/home", User.class,
    Parameter.with("q", "Mark"), Parameter.with("type", "user"));

out.println("Posts on my wall by friends named Mark: " + targetedSearch.getData().size());

Using FQL

FQL is deprecated and removed with Graph API 2.1

With Graph API 2.1, Facebook discontinued support for FQL (Facebook Query Language). All applications using Graph 2.1 or newer need to change their request and should work with the Graph API.

Because not every kind of request can transferred to Graph API, you should have a look at the Webhooks (former Real-Time Updates).

We support Webhooks with special types and you should look at the Webhooks section in this documentation.

On August 7, 2016 Facebook discontinued support for both Graph API 2.0 and FQL.

Using Webhooks

The Graph API has supported real time updates for a long time and with Graph API 2.2 the real time updates for pages have been improved a lot. Since, Graph API 2.5 the real time updates (RTU) have been renamed and are now called Webhooks.

Webhooks replace the polling mechanism with a pushing solution. With the deprecation of the FQL API, this is the only way to get notification of changes to your News Feed and other Facebook objects. These changes are very light, so you’ll only get some basic information. But you will get all the important information, and you can fill in the missing information with a normal Graph API call.

Technically, you need to provide an endpoint on a server that Facebook can reach and that understands the JSON-formatted POST requests that Facebook sends. You can simply parse them using a JSON library or you can use the new webhook types in RestFB.

For a much deeper insight and a better overview you should take a look at the Facebook explanation of webhooks.

The Webhook types in RestFB are a very special construction. They are fetched differently from other types and while they appear similar on the surface, their internals are completely different. The JSON is pushed to your server and so you cannot use the normal fetchObject method. You have simply a JSON String and you can now use a JsonMapper to convert it to a Java object.

The result is a Webhook entity and you only need to call something like this:

String pushedJsonAsString; // It's up to you to fill this String
JsonMapper mapper = new DefaultJsonMapper();
WebhookObject webhookObject = 
         mapper.toJavaObject(pushedJsonAsString, WebhookObject.class);

Now you can step through the WebhookObject and fetch the necessary information. The structure is a bit cumbersome, but RestFB has to reflect the original Facebook JSON structure. In this way, we are able to react to changes much faster and are able to expand our Webhooks support as new possibilities arrive.

The value-field of the Change object is very interesting. Here we added a new solution and made use of the @JsonMappingCompleted annotation in combination with a ChangeValueFactory.

Because the difference between the WebhookObjects you receive from Facebook lies in the change value, we implemented a factory so you can work with the correct entity and don’t need to implement some logic. Because some change values contain a postId, some a photo URL and other specific differences. These are provided by a group of classes whose names end with Value.

Important notice regarding the new Webhook types

At the moment we only support a subset of all possible webhook JSON types. The main focus lies on the page webhooks. You should be able to use the Webhooks nevertheless, but perhaps you have to deal with the FallBackChangeValue. It encapsulates the raw JSON that represents the change value and you may work with the JSONObject, but it lacks some convenience methods.

If this happens, you will see a log message with a hint to send us the JSON. We would really like to implement more Value classes and with the help of our users and the RestFB community we can provide a complete solution for Webhooks.

Webhooks are a great feature, but using them is not that easy. The objects have a complex structure and mostly in development the changes or the MessagingItem objects are used. These two object classes are the important objects of every webhook and so we have implemented a helper class that gives developers the opportunity to implement a suitable listener. The object structure is run through and a callback method is called for each relevant Value or MessagingItem object. So that developers can write a clear class, we have prepared an abstract class that should be used as a basis.

As an example, we would like to react to FeedCommentValue changes. To do this, we first implement a short class:

class MyFeedCommentListener extends AbstractWebhookChangeListener {
    @Override
    public void feedCommentValue(FeedCommentValue feedCommentValue) {
     // do some stuff with the FeedCommentValue here
    }
}

Then we register the listener and pass a WebhookObject. And the listener is already active.

Webhook webhook = new Webhooks();
webhook.registerListener(new MyFeedCommentListener());
webhook.process(webhookObject);

If you want to react to a MessagingItem instead of the changes, the class must be derived from the AbstractWebhookMessagingListener.

Messenger Platform

See Messenger Platform documentation

With Graph API 2.6, Facebook introduced the Messenger API. The new API allows developers to implement chat bots. With these bots it is possible to provide a great new user experience and the user may contact his/her favorite Facebook pages directly with the messenger.

The implementation of such a bot consists of two elements.

The first element is the webhook. Webhooks are used to receive messages from a user and new JSON requests are sent from Facebook. RestFB supports these new JSON requests and you can find new objects in the webhook packages of the RestFB types. Because the entrypoint to these types is the same as with normal webhooks, you should have a look at the Webhooks section if you need to refresh your knowledge. Otherwise use the webhooks as before and have a short look at the Facebook Messenger Webhook API to understand how they are built.

The second and the more interesting part of the Messenger API is the Send API. After receiving a text from a user, your bot should react to the request and you can now have different possibilities to respond. The different types are covered in the following sections, but you have first to know how the Send API is used and how RestFB supports you with the Send API.

Sending a message to a user requires a unique recipient identifier. There are three types of identifiers that are supported by the Send API.

The first is the id-identifier. You can get the recipient id as soon as a user sends a message to your bot. Please be aware of the fact that the recipient id is not the user id. If you try to send a message to a user id this will not work. You are required to use the recipient id.

If you received a message and took the id from that message you can create a new IdMessageRecipient with this simple line of code:

IdMessageRecipient recipient = new IdMessageRecipient("<userID>");

The second identifier type is the phone number of the user you are attempting to contact. Facebook only sends a message to a phone number if there is a certain level of confidence the the phone number is reachable. Additionally, sending messages to a phone number recipient requires the customer matching feature to be enabled for the app.

With this small piece of code you can create a phone recipient:

PhoneMessageRecipient recipient = new PhoneMessageRecipient("<userPhone>");

And last, but not least, there is the user_ref. This reference is fetched from a webhook message and used to create a UserRefMessageRecipient object. The user_ref is send to the webhook as soon as the Facebook Checkbox Plugin is used and a user opts-in to receive messages from the website that shows this plugin.

The code to create a UserRefMessageRecipient is:

UserRefMessageRecipient recipient = new UserRefMessageRecipient("<userRef>");

Private Replies and the Messenger API

Private replies provide a way to send private messages to a user who created a post or a comment on a Facebook page. So you can start a private conversation from a public “entry point”.

Since September 30, 2019 Facebook allows to use the Send API for private replies. This leads to much better way to communicate with a user and you can use all the cool features the Send API provides.

To use this new way to start the conversation, you simply need the post or comment id. With this id you create a recipient object like this:

// private reply recipient based on a post
String postId = post.getId(); 
PostIdMessageRecipient recipient = new PostIdMessageRecipient(postId);

// private reply recipient based on a comment
String commentId = comment.getId();
CommentIdMessageRecipient recipient = new CommentIdMessageRecipient(commentId);

Then you can use this object as recipient and you are done.

Now we have a recipient and need something we want to send to the recipient.

The simplest message you can send to a user is the text message. To generate a text message simply need to generate a Message object and use the String constructor.

An example looks like this:

Message simpleTextMessage = new Message("Just a simple text");

A Message object can contain an attachment. A simple attachment is the MediaAttachment and you only need to provide a URL that points to the attachment and the type of it. Available types are IMAGE, VIDEO, AUDIO and FILE.

The attachment is then added to the newly-created message. The Message is created like this:

MediaAttachment image = 
     new MediaAttachment(MediaAttachment.Type.IMAGE, "http://example.com/testimage.jpg");
Message imageMessage = new Message(image);

Facebook allows more complex objects and with these it is possible to define postbacks the user can use.

The postback is a button that is presented to the user in the messenger. On click, a specially-defined string is sent back to the bot via the webhook api so the bot knows which button was pressed.

Like the postback button, there exists a WebButton. A click on that button directs the user to a webpage.

In this example we create both types of buttons and add them to a Message.

ButtonTemplatePayload payload = new ButtonTemplatePayload("TEMPLATE TITLE");

// build a new button that links to a web url
WebButton webButton = new WebButton("EXAMPLE TITLE", "http://example.org/sample.html");

// build a button that sends a postback
PostbackButton postbackButton = new PostbackButton("EXAMPLE TITLE", "POSTBACK_STRING");

payload.addButton(webButton);
payload.addButton(postbackButton);

TemplateAttachment templateAttachment = new TemplateAttachment(payload);
Message imageMessage = new Message(templateAttachment);

A more complex example is the “generic” template.

It may contain some of the so-called “bubbles” that are horizontally-aligned and the user can switch between them. Switching between bubbles is performed by clicking on a arrow at the left or at the right side of the messenger view. Devices with touch support like smartphones can switch between these bubbles by swiping them to the left or right. The arrows are only visible on mouseover, so you should not be confused if you add more than one bubble and don’t see the other bubbles at first glance.

Every Bubble can contain further information like subtitles and images as well as the buttons we already saw in the earlier example.

The GenericTemplate allows a developer to create great-looking messages users can interact with in very new ways.

GenericTemplatePayload payload = new GenericTemplatePayload();

// Create a bubble with a web button
Bubble firstBubble = new Bubble("Title of first bubble");
WebButton webButton = new WebButton("EXAMPLE TITLE", "http://example.org/sample.html");
firstBubble.addButton(webButton);

// Create a bubble with a postback button
Bubble secondBubble = new Bubble("Title of second bubble");
PostbackButton postbackButton = new PostbackButton("EXAMPLE TITLE", "POSTBACK_STRING");
secondBubble.addButton(postbackButton);

payload.add(firstBubble);
payload.add(secondBubble);

TemplateAttachment templateAttachment = new TemplateAttachment(payload);
Message imageMessage = new Message(templateAttachment);

See Messenger Platform API

With the Messenger Platform 2.2, the new media template type was introduced. This template allows the developer to send a message with animated content like a video or an animated gif. The animation is shown directly in the message thread. Additionally, buttons are available as optional interaction elements.

An example of this template type follows:

MediaAttachment.MediaTemplateElement mediaTemplateElement =
     new MediaTemplateUrlElement("https://business.facebook.com/<PAGE_NAME>/videos/<NUMERIC_ID>");

// the MediaAttachment contains a list of MediaTemplateElement
MediaAttachment attachment = new MediaAttachment(Collections.singletonList(mediaTemplateElement));
Message recipient = new Message(attachment);	 

If a button is added to the MediaTemplateElement, the example code looks this:

WebButton button = new WebButton("Title", "<WEB_URL>");

MediaAttachment.MediaTemplateElement mediaTemplateElement =
     new MediaTemplateAttachmentElement(MediaAttachment.MediaType.IMAGE, "<ATTACHMENT_ID>");

mediaTemplateElement.addButton(button);

// the MediaAttachment contains a list of MediaTemplateElement
MediaAttachment attachment = new MediaAttachment(Collections.singletonList(mediaTemplateElement));
Message recipient = new Message(attachment);	 

The MediaTemplateAttachmentElement is generated with an attachment id and a MediaType as seen in the example above.

An alternative way to generate the MediaTemplateElement is the MediaTemplateUrlElement. A Facebook URL is used to generate the MediaTemplateElement and only special URLs are allowed. The MediaType is not needed here, because RestFB detects the type automatically according to the given URL. The allowed URL list follows:

Media Type Media Source URL Format
Video Facebook Page https://business.facebook.com/<PAGE_NAME>/videos/<NUMERIC_ID>
Video Facebook Account https://www.facebook.com/<USERNAME>/videos/<NUMERIC_ID>
Image Facebook Page https://business.facebook.com/<PAGE_NAME>/photos/<NUMERIC_ID>
Image Facebook Account https://www.facebook.com/photo.php?fbid=<NUMERIC_ID>

Now, we are ready to do the most important thing with our new build objects: sending them to Facebook.

We use the well known publish method of RestFB and only need to use the right endpoint with the right access token.

To send the message you have to use two parameters, the recipient and the message.

So we are ready to go and do something like this:

// the access token can be found in your Facebook app in the messenger section
String pageAccessToken = "MY PAGE ACCESS TOKEN";

// create a version 2.6 client
FacebookClient pageClient = new DefaultFacebookClient(pageAccessToken, Version.VERSION_2_6);

SendResponse resp = pageclient.publish("me/messages", SendResponse.class,
     Parameter.with("recipient", recipient), // the id or phone recipient
	 Parameter.with("message", message)); // one of the messages from above

Publishing a message with a media attachment can be solved with a single call. In this explanation we like to send an image to the messenger api. We don’t use an URL as image source and we don’t use a pre-uploaded image.

We show how the very common use case is handled where the application has direct access to the binary data of the particular image. To send the data we use the BinaryAttachment we already mentioned earlier.

The developer needs to identify the type of the binary file before creating the MediaAttachment. A good starting point is the MIME type of the file, but this is out of the RestFB scope.

We simply assume, you know what kind of binary data you are publishing to Facebook and you change the MediaAttachment type accordingly.

In combination with a recipient object a complete call looks like this:

IdMessageRecipient recipient = new IdMessageRecipient("<PSID>");
BinaryAttachment attachment = BinaryAttachment.with("funnycats.png", imageAsByteArray);

// we have to tell FB what kind of attachment we are going to publish, 
// in this example it is an image, but you can use file, video and audio, too
MediaAttachment messageAttachment = new MediaAttachment(Type.IMAGE);

// the message can only contain text OR an attachment
Message message = new Message(messageAttachment);

//Now, we publish this stuff
SendResponse resp = client.publish("me/messages", SendResponse.class, attachment,
        Parameter.with("recipient", recipient),
        Parameter.with("message", message));

// in the resp object you find some useful information, like the message id of the new message

See Personas API documentation

The Persona API allows Facebook pages to use virtual persons while sending message with the Send API. With these persons it is possible to show users different contact persons. The contact person has a name an a profile picture. Communication is more personal and users will know who they are talking to, not an abstract company.

Create a new Persona

A Persona consists of a name, a profile picture and the persona id. While creating a new persona only the name and the profile picture url is needed. As soon as you publish this information to Facebook, you get the persona id. Usually it is the easiest way to use the GraphResponse type as response object.

GraphResponse resp = facebookclient.publish("me/personas", GraphResponse.class, 
     Parameter.with("name", "John Mathew"), Parameter.with("profile_picture_url", "https://facebook.com/john_image.jpg"));

Sending messages with a Persona

As soon as a new persona is created, you can use its id in a new message. The id is an additional parameter with the name persona_id.

String personaId = "<persona id>";
Message message = new Message("Just a simple text");
PhoneMessageRecipient recipient = new PhoneMessageRecipient("<userPhone>");

// the access token can be found in your Facebook app in the messenger section
String pageAccessToken = "MY PAGE ACCESS TOKEN";

// create a version 3.0 client
FacebookClient pageClient = new DefaultFacebookClient(pageAccessToken, Version.VERSION_3_0);

SendResponse resp = pageclient.publish("me/messages", SendResponse.class,
     Parameter.with("recipient", recipient), // the id or phone recipient
	 Parameter.with("message", message),
	 Parameter.with("persona_id", personaId)); // one of the messages from above

Guidelines for Personas

These guidelines are defined on Facebook and are copied here for convenience.

  • The name of a persona is freeform, but Facebook recommends a first name and last initial, such as “John Z.”.
  • The page name of the bot will still be included at the top of the thread, even when a persona is invoked. It is not necessary to include the company name in the name field.
  • The persona should not be overly generic.
  • The persona should be clearly distinguished from the page/bot itself.
  • The persona should not attempt to deceive the user.
  • A persona can be created on the fly. It is not necessary to sync one’s entire database of agents to Messenger Platform in advance.
  • The total size of the image in profile_picture_url may not exceed 8MB.

See Messenger Platform API

Remove the deprecated call_to_actions from thread_settings endpoint

The old thread_settings endpoint is deprecated and you should remove any configuration you made earlier. In following sections, you will learn the current way to add the greeting text and so on. Use this snippet to clean your bot's welcome screen:
fbClient.deleteObject("/me/thread_settings", 
     Parameter.with("setting_type","call_to_actions"), 
     Parameter.with("thread_state","new_thread"));

The welcome screen is the first thing a user sees from a Facebook chat bot. On this screen, the user can learn what functionality the bot offers and therefore some basic information about this is presented. The name, profile picture, and cover photo from the Facebook page are shown along with the responsiveness of the bot.

To make it easier for the user to start a conversation with the bot, the developer can use 2 approaches: the “Get Started” button and the Greeting Text.

The Get Started button

Let’s begin with the “Get Started” button. The button text cannot be changed, but you can define the postback payload. As soon as a user clicks on the button, the payload is sent to the webhook and the chat bot receives the request.

To generate the encapsulated payload, we start with a CallToAction object. It is initialized with the payload String. This object is published to the Facebook Graph API. Please see the following example:

CallToAction getStartedPayload = new CallToAction("GET_STARTED_PAYLOAD");

// we assume there's already a FacebookClient
JsonObject response = client.publish("me/messenger_profile", 
     JsonObject.class, // the returned result as JsonObject
	 Parameter.with("get_started", getStartedPayload));

The greeting text

The first opportunity to tell a person why the should chat with your bot is the greeting text. You can include a short description of the bot in your greeting text. It can also be used to show a person what the style of your bot is.

To add text, you only need a Greeting object. This object has several constructors and the simplest way to create the object is a plain String. This results in a default greeting text. If you would like to add a locale-specific message, you can add a locale as String or a Locale object in the constructor. In the following example you see the construction of a default text and a text with a en_US locale. We use the Java Locale object in the constructor.

Afterwards, you simply publish the Greeting objects as a list in a Parameter object to the messenger_profile endpoint.

Greeting defaultLocaleGreetingText = new Greeting("Hello world");
Greeting usLocaleGreetingText= new Greeting(Locale.US, "Timeless apparel for the masses.");

// we assume there's already a FacebookClient
JsonObject response = client.publish("me/messenger_profile", 
     JsonObject.class, // the returned result as JsonObject
	 Parameter.with("greeting", Arrays.asList(defaultLocaleGreetingText, usLocaleGreetingText)));

Personalize your greeting text

Facebook supports a way to personalize the greeting text. You need to add a placeholder in the text template. There are placeholders for first, last, and full name.

  • {{user_first_name}} - user first name
  • {{user_last_name}} - user last name
  • {{user_full_name}} - user full name

An example greeting text template looks like this:

Greeting defaultLocaleGreetingTemplate = new Greeting("Hello {{user_first_name}}");

// we assume there's already a FacebookClient
JsonObject response = client.publish("me/messenger_profile", 
     JsonObject.class, // the returned result as JsonObject
	 Parameter.with("greeting", Arrays.asList(defaultLocaleGreetingText)));

Sometimes you need to send a large text to a recipient. Using the messenger platform this can be done in a very small amount of time. Because this is not a natural behavior for a normal user, it is possible to simulate typing so you let the user think she is talking to a human being.

RestFB supports this with a special enum called SenderActionEnum. In the following example, you can see how the typing simulation can be started.

IdMessageRecipient recipient = new IdMessageRecipient("<userID>");
Parameter senderActionParam = Parameter.with("sender_action", SenderActionEnum.typing_on);
Parameter recipientParam = Parameter.with("recipient", recipient);
SendResponse resp = pageclient.publish("me/messages", SendResponse.class,
     senderActionParam, // the sender action
     recipientParam); // the recipient

To stop the typing you can simply use the SenderActionEnum.typing_off instead.

The SenderActionEnum.mark_seen action is very interesting. With this, you are able to show the recipient that you’ve read his/her message. This is useful to give the user a good experience and the feeling he/she is talking to a human although it is a bot.

As of March 2 2017, there is a new persistent menu available. The new menu can contain submenus and allows developers to prevent user interaction via text input. The user may only select items in the menu and the bot does not need to understand the entered text. It seems Facebook tries to support bots that are “dumb”, because these bots cannot react in a useful way to arbitrary text inputs. More sophisticated bots have to understand the text input and act on the input in a way that makes sense.

Back to the persistent menu: the menu can contains several kinds of items. These can be found in the MenuItem marker interface. A new item type is the NestedButton. This provides access to the submenu and the NestedButton can contain a list of call-to-actions.

At the moment, the hierarchy is limited to 3 levels and 5 actions are allowed per level.

The localization feature is very important. A developer can decide, depending on the user’s language, what kind of menu a bot shows and if text input is supported or not.

The following code example is from the Facebook documentation and has been adapted to RestFB and the persistent menu structure. With this, you are able to compare the JSON with the object-oriented approach RestFB provides.

The menu is shown as soon as the user has the en_us language selected. Then we disable the text input and show 2 buttons, a WebButton and a NestedButton. The Nestedbutton (“My Account”) is the access point to the underlying submenu with some “account details”. Some PostbackButtons are used to let the user decide what she/he wants to do next: “Pay Bill”, “History” or “Contact Info”.

PersistentMenu menu = new PersistentMenu("en_us");
menu.setComposerInputDisabled(true);

WebButton webUrl = new WebButton("Latest News", "http://petershats.parseapp.com/hat-news");
webUrl.setWebviewHeightRatio(WebviewHeightEnum.full);

NestedButton nested = new NestedButton("My Account");
nested.addCallToAction(new PostbackButton("Pay Bill", "PAYBILL_PAYLOAD"));
nested.addCallToAction(new PostbackButton("History", "HISTORY_PAYLOAD"));
nested.addCallToAction(new PostbackButton("Contact Info", "CONTACT_INFO_PAYLOAD"));

menu.addCallToAction(nested);
menu.addCallToAction(webUrl);

With these new objects it is possible to build great persistent menus, and the locale feature allows for more advanced user interaction. After creating the menu, you can set it with a simple call to the Graph API:

JsonObject response = client.publish("me/messenger_profile",
        JsonObject.class,
        Parameter.with("persistent_menu", Arrays.asList(menu)));

See Handover protocol documentation

As of Graph API 2.6, Facebook supports the Handover Protocol API. This API is used to allow several applications to work on the Messenger Platform of one page. The primary message receiver can be changed via the API.

For example, you can switch between a chatbot that answers to incoming messages and a human agent who has to take over the conversation as soon as the chatbot is not able to fulfill the request or answer the question. It’s even possible to switch between dedicated chatbots.

To pass the thread control to another app, you can simply use this call:

GraphResponse result = 
   facebookClient.publish("me/pass_thread_control", 
   GraphResponse.class, // result type
   Parameter.with("recipient", new IdMessageRecipient("<PSID>")),  // recipient
   Parameter.with("target_app_id", 123456789), // app
   Parameter.with("metadata", "String to pass to ...")); // metadata

If the primary receiver app wishes to take back the control, we have to use another request and it will look like this:

GraphResponse result = 
   facebookClient.publish("me/take_thread_control", 
   GraphResponse.class, // result type
   Parameter.with("recipient", new IdMessageRecipient("<PSID>")),  // recipient
   Parameter.with("metadata", "String to pass to ...")); // metadata

Fetch secondary receivers

The primary receiver needs a list of secondary receivers to pass the control to one of them. This list can easily be obtained by calling the Graph API. An example call looks like:

Connection<Application> appConnection = 
	facebookClient.fetchConnection("/me/secondary_receivers", Application.class, Parameter.with("fields","id,name,link,daily_active_users"));

The fields parameter is optional and can be omitted. In that case, only id and name are provided by Facebook. If you need the applications to be prefilled with additional values, you have to add the fields parameter like in the example above.

Facebook does not allow unlimited data to be sent via the Send API and therefore they limit the amount of some fields. RestFB does not limit the data, because they may change in the future and so you don’t have to wait for new relases to use the new limits.

Limitations while creating new messages

  • Titles may have up to 45 characters
  • Subtitles may have up to 80 characters
  • Call-to-action titles may have up to 20 characters
  • Call-to-action items may have up to 3 buttons
  • A message may have up to 10 bubbles

See Messenger Code API

The Messenger Code API is deprecated

On October 29, 2019 the messenger API is deprecated for all API versions. Since August 15 2019, The Messenger camera no longer supports scanning Messenger Codes. As an alternative, Facebook recommend to use the phone's native capabilities to scan QR codes with m.me links.

Advanced Usage

See Graph API documentation

The Facebook Graph API provides a great feature which permits deep insight into node data. This feature is called introspection and it works similarly to the well-known reflection techniques that are part of many programming languages.

To activate introspection, you simply need to add the metadata parameter with the value 1. How this is done can be found in the Passing Parameters section or look at the example at the bottom of this section.

RestFB supports ths feature, too. All types that extend the FacebookType have access to the metadata and you simply need to call the getMetadata method. The returned Metadata contains all the fields, connections and the type of the requested object.

The type is very important if you don’t know what kind of node an id represents. In this metadata field you’ll find the node and you can use this information to send a more complex and type-dependent request to Facebook. For example, if you analyze a feed and its from fields, it is important to discriminate based on type because the from may contain a User or a Page.

// You can specify metadata=1 for many calls, not just this one.
// See the Facebook Graph API documentation for more details.

User userWithMetadata =
  facebookClient.fetchObject("me", User.class, Parameter.withMetadata()));

out.println("User metadata: has albums? "
  + userWithMetadata.getMetadata().getConnections().hasAlbums());

See Graph API documentation

Some requests need additional parameters. With Graph API 2.4 for example it is mandatory to provide the fields parameter to get a more extensive response. RestFB provides the Parameter class to serve this purpose.

The Parameter class needs a name and a value to be sent to Facebook. The value is internally processed by a json mapper, so you can provide a JsonObject, a primitive java type (like String, Boolean and so on), or a class that uses the @Facebook annotation (for example the RestFB types or custom ones).

// You can pass along any parameters you'd like to the Facebook endpoint.

Date oneWeekAgo = new Date(currentTimeMillis() - 1000L * 60L * 60L * 24L * 7L);

Connection<Post> filteredFeed = facebookClient.fetchConnection("me/feed", Post.class,
  Parameter.with("limit", 3), Parameter.with("until", "yesterday"),
    Parameter.with("since", oneWeekAgo));

out.println("Filtered feed count: " + filteredFeed.getData().size());

See Graph API documentation

If you fetch a type from Facebook you get a prefilled version. With Graph API 2.4 Facebook changed the behavior and you get only a very minimalistic object. The basic idea of this approach is to save bandwidth and make the transfer much faster, but you have to tell Facebook which fields you need. The requested fields have to be provided as comma separated list.

Basically you should add the fields parameter on every request. So you can be sure to get the right information. The fields a type supports can be found in the Graph API Reference.

Request with fields (first level)

A simple example looks like the following snippet. A user is fetched with id, name and email field filled.

// fetch user type with id, name and email prefilled
User user = facebookClient.fetchObject("me", User.class,
     Parameter.withFields("id,name,email")); 

out.println("User name: " + user.getName());

Request with fields (limit)

A even more interesting feature is the limit(x) method. If a type contains a field that represents a list of other types, for example the comments field on a post, you can limit the transferred objects with the limit(). This sounds very complicated and abstract but is very easy to use. To show the usage we have a simple example.

// fetch post type with from, to and two comments prefilled
Post post = facebookClient.fetchObject("74133697733_10152424266332734", Post.class,
     Parameter.withFields("from,tp,comments.limit(2)"));

Request with fields (second level)

Now we go a step further and have a look at the second level of the fields. Until now we worked only with the first level, but Facebook supports more levels. In this example we use the second level. The next level is added with the curly brackets and they encapsulate the fields of the next level type. This sounds complicated too, but is easy as soon as you see a example. And here we go:

// fetch post type with from, to and two comments prefilled,
// the comments contain only the from and message fields
Post post = facebookClient.fetchObject("74133697733_10152424266332734", Post.class,
     Parameter.withFields("comments.limit(2){from,message}"));

Request with fields (third to n-th level)

And the last example is the third level. Now we take the last example and request only the id from the from field. This is similar to the second level and so you get a idea how the different levels work and so you can work with n-levels if necessary.

// fetch post type with from, to and two comments prefilled,
// the comments contain only the from and message fields
// the inner from field contains only the id
Post post = facebookClient.fetchObject("74133697733_10152424266332734", Post.class,
     Parameter.withFields("comments.limit(2){from{id},message}"));

see July 2013 Breaking Changes

To fetch the likes, shares and comments count you must use the correct parameters, because Facebook does not sends you this information with a normal call. In the example at the bottom all fields (shares, comments and likes) are fetched and you see how easy it is to fetch these informations with RestFB.

You need to know how the parameters work, so we dive a bit in this specialty. As soon as you request one of the three candidates you get a short list and you can page through the results, but you don’t get a complete overview. As soon as you add the summary(true) part - don’t forget the dot delimiter - you get additional fields from Facebook with interesting information. The most important information is the total count. RestFB wraps the summary in the Comments, Shares and Likes type and e.g. the Post type has the special methods getLikesCount, getSharesCount and getCommentsCount that use the summary.

This explains why you need to add the fields parameters to the request, otherwise the method returns the fallback value 0;

As you can see there is a limit(0) part added to the request. This is simply used to reduce the transferred data. Because no share, comment or like is transferred and only the summary is present. We should support Facebook and don’t request data we don’t use.

// Some Post from the GoT Fanpage with likes and comments total count
Post post = client.fetchObject("74133697733_10152424266332734",
  Post.class,
  Parameter.withFields("from,to,likes.limit(0).summary(true),comments.limit(0).summary(true),shares.limit(0).summary(true)"));

out.println("Likes count: " + post.getLikesCount());
out.println("Shares count: " + post.getSharesCount());
out.println("Comments count: " + post.getCommentsCount());

Publishing

See Graph API documentation

In addition from fetching information from Facebook, it is important to be able to write new comments, posts or other kinds of data to Facebook. A popular item is a simple text. Comments, posts, private messages support this type and a developer should have the possibility to easily publish text on Facebook.

Therefore you should have a look at the next example. Here, we create a new Post on the user’s feed. The great message is “RestFB test”. In return, we get the id of the created post. RestFB provides a special type for these returned ids and it is called GraphResponse.

GraphResponse publishMessageResponse =
  facebookClient.publish("me/feed", GraphResponse.class,
    Parameter.with("message", "RestFB test"));

out.println("Published message ID: " + publishMessageResponse.getId());

It is possible to create more sophisticated items. For example, if you’d like to schedule a post, you can simply read over the next example. In that example, the scheduling timestamp is defined and the created Date object is used as scheduled_publish_time. Because scheduled post may not be published before “now”, the published parameter needs to be false.

Date tomorrow = new Date(currentTimeMillis() + 1000L * 60L * 60L * 24L);
GraphResponse publishMessageResponse =
  facebookClient.publish("me/feed", GraphResponse.class,
    Parameter.with("message", "RestFB test scheduled"), 
	Parameter.with("published", false), 
	Parameter.with("scheduled_publish_time", tomorrow));
	
out.println("Published message ID: " + publishMessageResponse.getId());

See Graph API documentation

Publishing a photo is an example of publishing binary content. There are two ways to publish binary content to Facebook. The old and deprecated way is with an InputStream and the new way with a ByteArray. Although the InputStream method is deprecated, you may use it, and we don’t intend to remove it in the near future.

You may use several endpoints to publish a photo and it depends on your use case which one you have to choose. It is important to use the correct access token, because some endpoints have specific permission requirements.

Note regarding publishing multiple photos at once

Publishing multiple photos simply does NOT work. Directly from the Facebook Graph API documentation:

"There is no way to publish more then one photo in the same graph API call."

(see here in the creating section)

The first example shows the InputStream method. In addition to the binary content, a message is added in the first case and a description in the second one.

// Publishing an image to a photo album is easy!
// Just specify the image you'd like to upload and RestFB will handle it from there.

GraphResponse publishPhotoResponse = facebookClient.publish("me/photos", GraphResponse.class,
  BinaryAttachment.with("cat.png", getClass().getResourceAsStream("/cat.png")),
  Parameter.with("message", "Test cat"));

out.println("Published photo ID: " + publishPhotoResponse.getId());

// Publishing a video works the same way.

facebookClient.publish("me/videos", GraphResponse.class,
  BinaryAttachment.with("cat.mov", getClass().getResourceAsStream("/cat.mov")),
  Parameter.with("description", "Test cat"));

This second example shows how to use a BinaryAttachment with a byte array. The generation of the array is not part of the example code, because it is highly dependent on the libraries used in your project and the way you receive the binary content.

byte[] imageAsBytes = fetchBytesFromImage();
DefaultFacebookClient client =
	new DefaultFacebookClient(getTestSettings().getPageAccessToken(), Version.LATEST);
JsonObject obj = client.publish(getTestSettings().getPageId() + "/photos", JsonObject.class,
	BinaryAttachment.with("test.png", imageAsBytes, "image/png"), Parameter.with("message", "TestImage"));

Publishing a binary object with a special field name

Normally you don't need to define a special form field name, because RestFB handles everything for you. But sometimes it is necessary to provide a special form field. Sending binary data is handled via multi-part form data.

Sometimes Facebook requires the form field name to have a special value when sending a binary object, so RestFB provides a mechanism to do this. You can simply use the BinaryAttachment object. An example for the required field name is adding a caption file to a video. This example is shown in the following code snippet. In that special case, the filename is important too, because it needs to contain the locale of caption file.

byte[] imageAsBytes = fetchBytesFromImage();
GraphResponse obj = client.publish("<video-id>/captions", GraphResponse.class,
BinaryAttachment.with("captions_file", "test.en_US.srt", imageAsBytes), 
     Parameter.with("default_locale","en_US"));

The caption file must contain the locale it represents and must be in srt format. In the example above, we set a caption for the en_US locale and define this as the default locale.

In the previous section focused on publishing a photo. Now we expand on this a bit and include a Tag as part of the publishing process. A tag permits you to link a Facebook user to a photo, providing formal information about who is in the photo.

To tag a user you need two things. First, a custom PhotoTag type, and second, you have to modify the request from the previous section. Because the custom type is the most important and exciting thing, we start with the custom type.

public class MyPhotoTag {
   @Facebook("tag_uid")
   private String tagUid;

   @Facebook("tag_text")
   private String tagText;

   @Facebook
   private Double x;

   @Facebook
   private Double y;

   // add getter and setter here
}

Now we have the custom type and may create a new instance. This is just good old Java. Now, let’s get to the publishing part. To make it easier, we use the publish call from the previous section and simply add the new parameter. When everything is wired together it looks like this:

byte[] imageAsBytes = fetchBytesFromImage();
MyPhotoTag myTag = new MyPhotoTag();
// fill myTag here
ArrayList<MyPhotoTag> myTagList = new ArrayList<MyPhotoTag>();
myTagList.add(myTag);
DefaultFacebookClient client =
	new DefaultFacebookClient(getTestSettings().getPageAccessToken(), Version.LATEST);
JsonObject obj = client.publish(getTestSettings().getPageId() + "/feed", JsonObject.class,
	BinaryAttachment.with("test.png", imageAsBytes, "image/png"),
	Parameter.with("message", "TestImage"),
	Parameter.with("tags", myTagList));

We use the list in this example because Facebook allows up to 40 tags per photo.

See Graph API documentation

We already know how we can publish a single binary file. But RestFB supports publishing several files at once. For example if you like to send a video with a thumbnail.

The main difference is the BinaryAttachment#with(String,String,byte[]) method. In this use case, we have to provide the field name, because Facebook needs to know where the binary data belongs to. The example uses the source field for the video and the thumb field for the thumbnail.

InputStream thumbnailIs = getClass().getResourceAsStream("thumbnail.png");
InputStream videoIs = getClass().getResourceAsStream("video.mp4");

byte[] imageAsBytes = getBytesFromInputStream(thumbnailIs);
byte[] videoAsBytes = getBytesFromInputStream(videoIs);

DefaultFacebookClient client = new DefaultFacebookClient(getPageAccessToken(), Version.LATEST);
GraphResponse response = client.publish("me/videos", //
   GraphResponse.class, // response
   Arrays.asList( // array list
     BinaryAttachment.with("source", "test.mp4", videoAsBytes), // video
     BinaryAttachment.with("thumb", "thumbnail.png", imageAsBytes)), // picture
   Parameter.with("description", "Test video with Thumbnail"));

As a side note: If you send more then one file, you have to support a List of BinaryAttachments.

See Graph API documentation

Similar to various other social networks, Facebook offers its users the capability to upload a distinct form of video content known as ‘Reel.’ In essence, a Reel is a video in MP4 format with a specific aspect ratio of 9:16. This means that it is presented in an upright, portrait-oriented video format, perfectly suited for viewing on mobile devices. One key feature that distinguishes Reels is their duration. Facebook permits Reels to be of a length ranging from 3 to 90 seconds, making them inherently short videos, typically captured using a smartphone camera. This concise and visually engaging format allows users to create and share captivating moments, stories, or messages in a quick and engaging manner.

Facebook introduces a new workflow for developers to publish a Reel using the Graph API. This workflow is divided into three distinct phases:

  1. Initialize the Upload Session: In this first phase, you will initiate the upload session for the Reel content.
  2. Upload the Reel: The second phase involves uploading the Reel video to the session established in the previous step.
  3. Publish the Reel: The final phase is where you publish the Reel to make it accessible to your audience.

This process shares similarities with uploading large files but does have some unique characteristics specific to Reels.

Initialize the upload session

Before you can proceed with publishing a Reel using the Graph API, you must obtain a video upload ID. This unique identifier represents the object in the Facebook graph and serves as the reference for the video content you intend to share.

Once you have the video upload ID, you can proceed to upload the Reel content associated with it. The following code examples assume the use of a Facebook client with a valid page access token. This access token is essential for authenticating your application and gaining the necessary permissions to interact with Facebook’s API.

ReelsUploadStartResponse startResponse = client.publish(pageId + "/video_reels", //
      ReelsUploadStartResponse.class, //
      Parameter.with("upload_phase", "start") //
    );
String videoUploadID = startResponse.getVideoId();

Upload the Reel

After completing the first phase, you should now have obtained the videoUploadId. With this identifier in hand, you’re ready to proceed to the video publishing phase. However, it’s important to note that there are two distinct methods for publishing a video on Facebook, and your choice will depend on your specific use case.

The first method involves sending the video as binary data directly to Facebook’s servers. Alternatively, if the video file is hosted on the internet, you can take advantage of a simpler approach by providing the video’s URL.

Upload by URL

In this section, we will begin by exploring the video upload process using a URL. This method is particularly convenient when your video is hosted online and can be easily accessed via a direct link.

String fileUrl = "https://example.org/reel.mp4";
FacebookReelAttachment reelAttachment = FacebookReelAttachment.withUrl(fileUrl);
GraphResponse response = client.publish(videoUploadID, GraphResponse.class, reelAttachment);

Upload by binary data

While one method for publishing a video on Facebook involves providing a URL to the hosted video, an alternative approach is to upload the video as binary data. This method is particularly useful when your video file is not accessible via a URL or when you want to directly transfer the video’s binary content to Facebook’s servers.

Here’s how you can initiate the video upload process by providing the video as binary data:

byte[] videoBytes = getVideoFileBytes();
FacebookReelAttachment reelAttachment = FacebookReelAttachment.withByteContent(videoBytes);
GraphResponse response = client.publish(videoUploadID, GraphResponse.class, reelAttachment);

Publishing the Reel

In the last step of the process, following the successful upload of your video, you have the opportunity to publish the Reel to your page feed. This is where your Reel becomes visible to your audience, allowing them to engage with and enjoy your video content. You can include captions, descriptions, or any additional information to make your Reel more appealing and informative to your viewers. Once published, your Reel will be readily accessible to your audience on your Facebook page:

GraphResponse publishResponse =
          client.publish(pageId + "/video_reels", GraphResponse.class, //
            Parameter.with("video_id", videoUploadID), //
            Parameter.with("upload_phase", "finish"), //
            Parameter.with("video_state", "PUBLISHED"), //
            Parameter.with("description", "A short description text"));

Additional requests

Another crucial aspect to consider is the request for monitoring the upload status. It’s essential to understand that the video isn’t immediately available for viewing right after the upload process. Before you can publish the video, you should check its processing status. Although you have the option to publish the video as soon as it’s uploaded, the actual publishing is contingent on the video’s processing completion.

The result status field, which is an integral part of the video object, becomes invaluable in this scenario. You can easily access this field, and it provides a limited set of essential information that allows you to monitor the video’s processing progress before making it available for your audience.

Video videoStatus = client.fetchObject(videoUploadID, Video.class, Parameter.withFields("status"));
System.out.println("VideoStatus: " + videoStatus.getStatus());

See Graph API documentation

Publishing a checkin works like publishing a normal post to a user’s feed. You simply have to provide a place parameter with a place id. If you only have longitude and latitude, you have to search for places in the given area first and let the user select the correct place. Publishing via longitude/latitude is not possible since Graph API version 2.0. (see Stackoverflow question)

GraphResponse publishCheckinResponse = facebookClient.publish("me/feed",
  GraphResponse.class, Parameter.with("message", "I'm here!"),
    Parameter.with("place", 1234)));

out.println("Published checkin: " + publishCheckinResponse.getId());

See Graph API documentation

Uploading a short video is the same as uploading a picture. You should take a look at the picture section to understand how it works.

A new feature Facebook added with Graph API 2.4 is resumable upload support. Large videos (up to a file size of about 2GB) should be uploaded with this new approach. The current max value can be found in the Graph API documentation, or you can simply try to start the upload and handle any filesize errors that occur.

The resumable upload is divided in 3 phases: Start, Transfer, and Finish. If you are familiar with databases and transactions, you can compare the start with starting a transaction and finish with committing it. The transfer phase is where the real action takes place.

The Start Phase

The first phase is the Start phase. Instead of uploading the file itself, you tell Facebook you want to upload a file and how big it is. The file size you send to Facebook is in bytes. Facebook checks the size and returns an error if something is not okay, but normally you should get a Video ID, a Session ID, and the range of bytes (expressed as a start and end offset) Facebook expects you to send in the first transfer chunk.

File videoFile = new File(uploadableFile.getFile());
long filesizeInBytes = videoFile.length();
FileInputStream in = new FileInputStream(videoFile);
// We need the file size in bytes to make the start request
FacebookClient fbc = new DefaultFacebookClient(accessToken, Version.LATEST);
ResumableUploadStartResponse returnValue = fbc.publish("PAGE_ID/videos",
     ResumableUploadStartResponse.class, // The return value
     Parameter.with("upload_phase", "start"), // The upload phase
     Parameter.with("file_size", filesizeInBytes)); // The file size

long startOffset = returnValue.getStartOffset();
long endOffset = returnValue.getEndOffset();
Long length = endOffset - startOffset;

// The upload session ID is very important, because Facebook needs
// this ID to identify all the uploads that belong together
String uploadSessionId = returnValue.getUploadSessionId();

The Transfer Phase

From Facebook’s response, you’ll know what the first chunk of the file you need to upload is. Now you enter the Transfer phase. You upload a chunk of the file the same way you normally upload a binary file to Facebook - you simply have to add some additional information about the offset and session so Facebook can join the file correctly.

With each chunk transfer, Facebook will return the next byte range it’s expecting. This happens in a loop. So you upload the next part and get new offsets, and upload those bytes and so on.

// We have to upload the chunks in a loop
while (length > 0) {
     // First copy bytes in byte array
     byte fileBytes[] = new byte[length];
     in.read(fileBytes);

     // Make the request to Facebook
     ResumableUploadTransferResponse filePart = fbc.publish("PAGE_ID/videos",
      // The returned object 
	  ResumableUploadTransferResponse.class,
	  // The file chunk that should be uploaded now
	  BinaryAttachment.with("video_file_chunk", fileBytes),
	  // Tell Facebook that we are in the transfer phase now 
	  Parameter.with("upload_phase", "transfer"),
	  // The offset the file chunk starts
	  Parameter.with("start_offset", startOffset),
	  // The important session ID of this file transfer
          Parameter.with("upload_session_id", uploadSessionId));

     // After uploading the chunk we recalculate the offsets according to the 
     // information provided by Facebook
     startOffset = filePart.getStartOffset();
     endOffset = filePart.getEndOffset();
     length = endOffset - startOffset;
}

At some point you’ll receive a response where start and end byte offsets are equal. This signifies that it’s time to enter the Finish phase.

The Finish Phase

After uploading all binary chunks you enter the third phase and send Facebook the request to publish the video. The response is the frequently-used “success” JSON and you may use the GraphResponse type to easily access the success field.

GraphResponse finishResponse = fbc.publish("PAGE_ID/videos",
     GraphResponse.class,
     Parameter.with("upload_phase", "finish"), // Tell Facebook to finish the upload
     Parameter.with("upload_session_id", uploadSessionId)); // The corresponding session ID

After you receive a success response, you’ll have to wait until Facebook finishes processing the video. This might take a few minutes. Once done, you’ll receive one or more notifications: an email, a Facebook message, and - if you have a working real time update subscription - a call regarding the new video.

Once you receive a notification, the video is online and ready to be played.

Deleting

See Graph API documentation

To delete an object on Facebook, RestFB provides a deleteObject-method. You have to provide the ID of the object and RestFB does the magic. As a return value you receive a boolean - true means the object had been deleted, false otherwise. You might encounter one of the common exceptions if something happened.

Boolean deleted = facebookClient.deleteObject("some object ID");
out.println("Deleted object? " + deleted);

Batch API

See Batch API documentation

Batch requests allow to send many requests to the Graph API in one HTTP call. If the requests are independent, Facebook will execute them in parallel. Then you can save time and speed up requests. Are the requests dependent, this means that in one requests data from another part of the batch request is used, then Facebook executes these calls sequentially.

Overall batch requests can speed up requests or make them easier. Depending on the use case and the used requests.

Limitations of batch request

The requests in a batch are calculated as single requested, so you cannot bypass the rate limits of the Graph API. As example, 8 requests in a batch are still 8 requests in the insights, although it is only 1 HTTP call.

A batch is limited to 50 requests.

In a batch requests, you can define which HTTP requests type you like to simulate, then you can add headers per call and special bodies, too. If you like to publish binary data to the Graph API, this is possible.

RestFB provides a builder to generate the necessary JSON. But it is important for the user to have a basic knowledge of the batch requests and the special responses. In the following section you’ll learn how the builder is used, how a request is built and how you can work with the response afterwards.

Deeper insights to the batch requests, that are not handled here, can be found in the Facebook documentation.

RestFB supports you sending BatchRequest objects. Therefore we have implemented the BatchRequestBuilder. Our builder provides several settings and you can configure the BatchRequest with a fluent interface.

To build a minimal request, you only need to provide a relative path. If you like to request the me resource, you can do it this way.

BatchRequest meRequest = new BatchRequestBuilder("me").build();

Sometimes it is necessary to provide some additional query parameters for the request. For example if you like to fetch only some specific fields you can use the builder, too.

BatchRequest meRequest = new BatchRequestBuilder("me")
  .parameters(Parameter.withFields("name,id")).build();

Sending data to Facebook is as easy as fetching data. You only have to change the request method and provide some data you like to send in the body. The code looks similar to this.

BatchRequest postRequest = new BatchRequestBuilder("me/feed")
  .method("POST")
  .body(Parameter.with("message", "Testing!")).build();

Since you know how to create a BatchRequest you only need to send the request to the Graph API. RestFB provides a special method for this. Overall the complete requests is defined in the following way.

BatchRequest meRequest = new BatchRequestBuilder("me").build();
BatchRequest badRequest = new BatchRequestBuilder("this-is-a-bad-request/xxx").build();
BatchRequest m83musicRequest = new BatchRequestBuilder("m83music/feed")
  .parameters(Parameter.with("limit", 5)).build();
BatchRequest postRequest = new BatchRequestBuilder("me/feed")
  .method("POST")
  .body(Parameter.with("message", "Testing!")).build();

// ...and execute the batch.

List<BatchResponse> batchResponses =
  facebookClient.executeBatch(meRequest, badRequest, m83musicRequest, postRequest);

// Responses are ordered to match up with their corresponding requests.

BatchResponse meResponse = batchResponses.get(0);
BatchResponse badResponse = batchResponses.get(1);
BatchResponse m83musicResponse = batchResponses.get(2);
BatchResponse postResponse = batchResponses.get(3);

After receiving a bunch of BatchResponse objects, you can convert the data contained in the body using the RestFB JsonMapper. Of course you need to know, what kind of object Facebook sends to you in advance, to let everything work correctly.

JsonMapper jsonMapper = new DefaultJsonMapper();
User me = jsonMapper.toJavaObject(meResponse.getBody(), User.class);
out.println(me);

In case of the request is containing a list instead of a single object, you can use the Connection object that is provided by RestFB.

Connection<Post> m83musicPosts =
  new Connection<Post>(facebookClient, m83musicResponse.getBody(), Post.class);

for (List<Post> m83musicPostsConnectionPage : m83musicPosts)
  for (Post post : m83musicPostsConnectionPage)
    out.println(post);

Last but not least there is another special information you need to know. Maybe a request runs into an error you can simple access the http code of that part by using the getCode method of the BatchResponse object.

if(badResponse.getCode() != 200)
  out.println("Batch request failed: " + badResponse);

Sometimes you like to send binary data with a BatchRequest. The way this is expected by Facebook is also supported by RestFB. First you need to define a file identifier. This is a link between the request and the attachments.

The identifier is added to a batch request with the attachedFiles method. If you like to add more then one file in a request, you can add a list of identifier as comma sperated list.

Additional to the batch requests you have to create a list of BinaryAttachment. The single BinaryAttachments needs the former used identifier to do the mapping between the binary data and the BatchRequest.

After creating the requests and the list of binary attachments you can simply send the data with the executeBatch method. The complete procedure as java code looks like this.

// Per the FB Batch API documentation, attached_files is a comma-separated list
// of attachment names to include in the API call.
// RestFB will use the filename provided to your BinaryAttachment minus the file
// extension as the name of the attachment.
// For example, "cat-pic.png" must be referenced here as "cat-pic".

List<BatchRequest> batchRequests = Arrays.asList(
  new BatchRequestBuilder("me/photos").attachedFiles("cat-pic").build(),
  new BatchRequestBuilder("me/videos")
    .attachedFiles("cat-vid, cat-vid-2")
    .body(Parameter.with("description", "This cat is hilarious"))
    .build());

// Define the list of attachments to include in the batch.
List<BinaryAttachment> binaryAttachments = Arrays.asList(
  BinaryAttachment.with("cat-pic.png", getClass().getResourceAsStream("/cat-pic.png")),
  BinaryAttachment.with("cat-vid.mov", getClass().getResourceAsStream("/cat-vid.mov")),
  BinaryAttachment.with("cat-vid-2.mov", getClass().getResourceAsStream("/cat-vid-2.mov")));

// Finally, execute the batch.
facebookClient.executeBatch(batchRequests, binaryAttachments);

See Batch API documentation

A special usage of the batch requests are dependent requests. These requests are put in one batch, but you can for example use the result of the first request in the second request and receive the complete result. The requests are executed sequentially on the Facebook servers.

We like to show you an example that is ported from the Facebook reference to RestFB. First you have to define the request to fetch the friends. The request is named, so it can be used in a second request as we see later on.

BatchRequest friendsRequest = new BatchRequestBuilder("me/friends")
  .parameters(Parameter.with("limit",5))
  .name("get-friends")
  .build();

Now we use the result of that request in the second request we define in the next code snippet. It is important to know, that access to the first result set is done with a JSONPath expression. The complete requests will look like this.

BatchRequest friendsIdsRequest = new BatchRequestBuilder("")
  .parameters(Parameter.with("ids","{result=get-friends:$.data.*.id}"))
  .build();

Now you can send both requests to Facebook as we did in the sections above.

Access Token Operations

Before you can build your Facebook Login Dialog URL you need to look deeper into the the scope concept. Every Access Token is bound to a scope and a scope is a set of permissions. The developer decides which permissions are needed to get the application working correctly.

RestFB supports the developer building the scope and so you should use the ScopeBuilder. The permissions are implemented as Enums. Here’s an example how to build a scope:

ScopeBuilder scopeBuilder = new ScopeBuilder();
scopeBuilder.addPermission(FacebookPermissions.EMAIL);
scopeBuilder.addPermission(FacebookPermissions.USER_ABOUT_ME);

With the ScopeBuilder you can now easily build the login dialog url, like this:

FacebookClient client = new DefaultFacebookClient(Version.LATEST);
String loginDialogUrlString = client.getLoginDialogUrl(appId, redirectUrl, scopeBuilder);

See Graph API documentation

Normally access tokens are only valid for a short time, but you can extend the lifetime with a simple Facebook call. RestFB provides a method to make this easy.

// Tells Facebook to extend the lifetime of MY_ACCESS_TOKEN.
// Facebook may return the same token or a new one.

AccessToken accessToken =
  new DefaultFacebookClient(Version.LATEST).obtainExtendedAccessToken(MY_APP_ID,
    MY_APP_SECRET, MY_ACCESS_TOKEN);

out.println("My extended access token: " + accessToken);

See Graph API documentation

App access tokens are used to make server-to-server calls on behalf of your application. These special access tokens allow you to modify your application settings and make some other calls - for example, publishing Open Graph actions or debugging access tokens.

To obtain an Application Access Token, you’ll need your App ID and App Secret. For this reason, Application Access Tokens are only used for server-to-server communication.

RestFB provides a mechanism to obtain application access tokens. All you need to do is make the following call:

// Obtains an access token which can be used to perform Graph API operations
// on behalf of an application instead of a user.

AccessToken accessToken =
  new DefaultFacebookClient(Version.LATEST).obtainAppAccessToken(MY_APP_ID, MY_APP_SECRET);

out.println("My application access token: " + accessToken);

See Graph API documentation

With the Device Access Token, Facebook provides functionality to connect to devices with limited input or display capabilities.

The device access token is generated using a two-step process. First, you have to call FacebookClient.fetchDeviceCode(see example code). You receive a user code, a code and some additional information. The user code has to be shown to the user and she has to enter it on a special Facebook page. The url of that page is part of the DeviceCode object. You should not hard code this url, because Facebook might change it. As soon as you present this information to the user, you have to poll Facebook and try to fetch the device access token.

Fetching the Device Access Token

With Graph API 2.6, Facebook changed the way a device token is requested. First we need a special access token that consists of the application id and the client token. The client token can be found in the Application Dashboard. The new token looks like <app_id>|<client_token>. With this access token, a new FacebookClient is created and the client is used for the device token interactions with Facebook. Because the application id is part of the access token we don’t need to provide the application id in the method call. So we have two new methods that work with this access token and don’t use the application id as parameter.

The first part - fetching the DeviceCode - looks like this:

String specialAccessToken = MY_APP_ID + "|" + MY_CLIENT_TOKEN;
DefaultFacebookClient deviceTokenClient = 
         new DefaultFacebookClient(specialAccessToken, Version.LATEST);
ScopeBuilder scope = new ScopeBuilder();

DeviceCode deviceCode = client.fetchDeviceCode(scope);

out.println("Open in a browser: " + deviceCode.getVerificationUri);
out.println("Enter this Code: " + deviceCode.getUserCode);

Now we have to call the polling call in a loop and can receive the same exceptions as in earlier versions. One change is important: there is no FacebookDeviceTokenDeclinedException because this information is not sent to the application. This is an important change, because you have to handle this in your application. You can run into the code expired exception or you let the user close a dialog or something and stop the polling in respect to the user action.

The polling call doesn’t need the application id, so it looks like this:

// Obtains an access token which can be used to perform Graph API operations
// on behalf of a user.

AccessToken accessToken =
   deviceTokenClient.obtainDeviceAccessToken(deviceCode.getCode());

out.println("My device access token: " + accessToken);

The checked device token exception

The polling method should return the AccessToken, but may throw a FacebookDeviceAccessToken exception.

Security Operations

// Facebook can send you an encoded signed request, which is only decodable by you
// with your App Secret. Pass the signed request, your app secret, and a class that
// specifies how RestFB should map the decoded signed request JSON.

String signedRequest = "xxx";
String appSecret = "yyy";
Payload payload = facebookClient.parseSignedRequest(signedRequest,
  appSecret, Payload.class);

out.println("Signed request user_id: " + payload.userId);

// You must write your own class to hold signed request payload data
// since RestFB can't know in advance what fields FB will be sending you.
// Some are always present, like user_id, but the rest will be specific
// to your app and situation.

class Payload {
  @Facebook("user_id")
  String userId;

  @Facebook("oauth_token")
  String oauthToken;

  @Facebook
  Long expires;

  @Facebook("issued_at")
  Long issuedAt;

  Date getExpires() {
    return expires == null ? null : new Date(expires * 1000L);
  }

  Date getIssuedAt() {
    return issuedAt == null ? null : new Date(issuedAt * 1000L);
  }

  // Add whatever other fields you might have
}

A user may uninstall an application in his or her Facebook app settings. Because the application owner might need this information, Facebook calls a Deauthorization Callback. A developer can set the deauthorization callback url in the app settings. As soon as someone uninstalls the app, this url is called with a signed POST request.

RestFB provides support for checking these signed requests and decoding the provided information.

The following snippet explains how to use the DeAuth type to get the user id of the user who uninstalled the app. Additionally, you’ll get the date when the app was uninstalled.

// Facebook can send you an encoded signed request, as soon as someone deletes
// your Facebook app. This only happens if you have defined a deauthorization
// callback url in your Facebook App (Settings -> Advanced).

String signedRequest = "xxx";
String appSecret = "yyy";
DeAuth deauthObj = facebookClient.parseSignedRequest(signedRequest,
  appSecret, DeAuth.class);

out.println("This user just deleted your app: " + deauthObj.getUserId());
out.println("Deauthorization at: " + deauthObj.getIssuedAt());

Most requests need an access token. If someone is able to steal the access token, he or she can send spam with this app.

To make this more secure, Facebook provides a special parameter called appsecret_proof. This is a parameter that is transferred as query parameter in a normal request and contains a hashed version of the app secret. The additional security is only achievable if the communication to Facebook occurs via a server controlled by the developer.

The FacebookClient contains the obtainAppSecretProof method to calculate the proof, but a much easier way is to instantiate the DefaultFacebookClient with the additional appsecret_proof parameter. In that case, the appsecret_proof is automatically added to every call.

// If you create a DefaultFacebookClient instance with your app secret, RestFB will
// automatically include appsecret_proof with your requests,
// no work needs to be done on your end.

FacebookClient facebookClient =
  new DefaultFacebookClient(MY_ACCESS_TOKEN, MY_APP_SECRET, Version.LATEST);

// Request will include appsecret_proof

facebookClient.fetchObject("XXX", User.class);

// You may also generate the appsecret_proof value directly (not normally needed).

String proof = new DefaultFacebookClient(Version.LATEST).obtainAppSecretProof(
  MY_ACCESS_TOKEN, MY_APP_SECRET);
out.println("Here's my proof: " + proof);

For a long time RestFB used the access token only as query parameter and this was the only way to do authenticated Graph API calls. For security reasons this is not the best way, because query parameters are most of the time logged in proxy servers and many other appliances.

Facebook supports another mechanism and so it is possible to provide the access token as HTTP header information. This is part of the oauth specification and RestFB allows the developer to enable this way to transport the access tokens to Facebook, too.

By default, RestFB still uses the query parameter, but you can change this using this flag on the DefaultFacebookClient object like shown in this code:

DefaultFacebookClient client = new DefaultFacebookClient("access token", Version.LATEST);
client.setHeaderAuthorization(true);

Please keep in mind, that this feature is not tested in every aspect and with every possible call. So please provide us information if you run into problems with the mechanism.

Instagram

Facebook supports for a long time the connection between a Facebook business page and a Instagram business account. With this connection a business or creator can use an Facebook app to manager their presence on Instagram.

The API can be used to get and publish (only for partners in the beta progam) their media, manage and reply to comments on their media, identify media where they have been @mentioned by other Instagram users, find hashtagged media.

This API cannot be used for consumers.

First you need an access token with the manage_pages and the instagram_basic permissions. With those permissions it is possible to fetch the needed page access token including the instagram id. For this we simply call:

FacebookClient client = new DefaultFacebookClient("<user access token>", Version.VERSION_5_0);

Connection<Page> pageConnection = client.fetchConnection("/me/accounts/", Page.class, Parameter.with("fields","id,name,access_token,instagram_business_account"));

List<Page> instaPageList = StreamSupport.stream(pageConnection.spliterator(), false)
	.flatMap(List::stream)
	.filter((p) -> p.getInstagramBusinessAccount() != null)
    .collect(Collectors.toList());

Now you have a list of pages, containing the page access token and the instagram account id.

After fetching the instagram id that is connected to the facebook business page, you can use that id to fetch your instagram profile information.

With the page access token containing the instagram_basic permission, we create a FacebookClient and then we can fetch the profile. The profile itself is returned as IgUser type.

FacebookClient client = new DefaultFacebookClient("<page access token>", Version.VERSION_5_0);

String profileFields = "biography,id,ig_id,followers_count,follows_count,media_count,name,profile_picture_url,username,website";

IgUser igProfile = 
    client.fetchObject("<instagram profile id>", IgUser.class,
		Parameter.withFields(profileFields));

In this example we define the field list before the call. Adding the fields explicitly to the call allows us to have a better control over the returned object.

The most important elements, you can start with when using the instagram api, are the IgMedia objects. Because we now know how to access the profile, we can access the media list with a similar call. But in this case we have to use a Connection object, because the data is returned as a list with paging.

FacebookClient client = new DefaultFacebookClient("<page access token>", Version.VERSION_5_0);

String mediaFields = "children{permalink,media_type,media_url,timestamp},thumbnail_url,shortcode,timestamp,media_type,media_url,is_comment_enabled,comments{like_count,media,replies,timestamp,user,username,text},permalink,caption";

Connection<IgMedia> mediaConnection = 
    client.fetchObject("<instagram profile id>/media", IgMedia.class,
		Parameter.withFields(mediaFields));
		
List<IgMedia> mediaList = StreamSupport.stream(pageConnection.spliterator(), false)
	.flatMap(List::stream)
	.collect(Collectors.toList());

With this snippet, we get a list of IgMedia objects with almost completely filled objects.

Despite from fetching instagram media objects and the connected comments, it is very interesting to manage the comments. Managing comments is not only answering and sending text, but handle the problems in this area, too.

So a simple solution to stop a flood of comments is to disable or enable the comment function for a media object completely.

To use this function, you first need a media id. You can get one via webhook or while fetching the profiles media list. Then you simply need to make a special request and the comments are disabled.

FacebookClient client = new DefaultFacebookClient("<page access token>", Version.VERSION_5_0);

GraphResponse disableComments =
  facebookClient.publish("<ig media id>", GraphResponse.class,
    Parameter.with("comment_enabled", "false"));

To re-enable the comments again, the call is almost the same. The only difference is the boolean value. Instead of false it is simply true. And the call looks like this.

FacebookClient client = new DefaultFacebookClient("<page access token>", Version.VERSION_5_0);

GraphResponse enableComments =
  facebookClient.publish("<ig media id>", GraphResponse.class,
    Parameter.with("comment_enabled", "true"));

Then the comments are enabled again.

The most important action on a media object is writing comments using the Graph API. The used page access token needs the instagram_manage_comments permission.

You need the media id to access the comments endpoint and on this you can create new comments. The call look like this.

FacebookClient client = new DefaultFacebookClient("<page access token>", Version.VERSION_5_0);

String messageText = "Just a simple comment text";

GraphResponse response =
  facebookClient.publish("<ig media id>/comments", GraphResponse.class,
    Parameter.with("message", messageText));

Instagram comments are all text. The only way to visually enhance the text are emojis.

WhatsApp

With the introduction of the WhatsApp Business Platform, Facebook now also allows WhatsApp to be used via the API. Specifically, there are currently two APIs to use WhatsApp. On the one hand the Cloud API and on the other hand the On-Premises API.

RestFB supports the Cloud API and covers both the communication with the WhatsApp Graph API and the conversion of the incoming Whatsapp Webhook messages.

The easiest way for developers to use the WhatsApp Business Platform is to create a RestFB Client based on the DefaultFacebookClient.

However, communication with the WhatsApp Business Platform differs from communication with the Facebook Graph API in one important respect. The access token must not simply be passed as a query parameter, but must be passed as an HTTP header field. WhatsApp Business Platform only accepts this method, so we created the ability to easily toggle the behavior.

The class method is called setHeaderAuthorization. An example code for switching the behavior and sending an object then looks like this:

DefaultFacebookClient fbClient = 
  new DefaultFacebookClient("<access token>", Version.VERSION_13_0);
fbClient.setHeaderAuthorization(true);
JsonObject message = new JsonObject();
// fill message JSON here - or use WABP objects here 
Body body = Body.withData(message);
JsonObject result = 
  fbClient.publish("FROM_PHONE_NUMBER_ID/messages", JsonObject.class, body);

Running The Examples

RestFB provides a repository on Github with an example project. In this repository RestFB is used to fetch several pieces of information from Facebook, publish some information, and run a legacy example. The whole project is Maven-based and you may use this repository to start your own project using RestFB.

To run the examples you need to download the restfb-examples project as zip file or clone it from Github. Don’t forget to replace the MY_ACCESS_TOKEN with your access token, otherwise the examples won’t run.

$ mvn compile
$ mvn exec:java@run-reader-examples -Daccess_token=MY_ACCESS_TOKEN
$ mvn exec:java@run-publisher-examples -Daccess_token=MY_ACCESS_TOKEN
$ mvn exec:java@run-legacy-examples -Daccess_token=MY_ACCESS_TOKEN

Supported Graph Object Types

RestFB can map JSON to any class that has fields annotated with the @Facebook annotation, which is discussed in more detail in the JSON Mapping Rules section.

However, for your convenience, all basic Graph API Object types have their own Java implementation in the com.restfb.types package.

You may use these builtin types if you wish, or you can subclass them to add additional fields or functionality, or just write your own if you have special requirements. The builtins come with reflective implementations of toString(), hashCode(), and equals(Object o) to make your life simpler. If you subclass any of the builtins and add additional fields with accessors, those fields will be automatically taken into account by the above 3 methods.

In the com.restfb.types package you can find many classes and some packages. The classes represent Graph API types that are used in the “normal” Graph API. The distinction between normal and other classes is really easy if you look at the packages. The Marketing API has its own package called ads. Everything that is only used in the Marketing API can be found in this special package.

The webhook and send packages are associated with each other. Everything is located in the webhook package that is received via a Facebook webhook. Changes to the feed, changes to the user and page and of course incoming messages are received by subscribing to the Messenger Platform webhook.

The send package contains all objects that are used by the Messenger Platform to send new messages to a user. The different templates (even the airplane specific ones), quick replies and so on can be found here. How they are used is explained in the Messenger Platform section of this documentation

So all Graph API types that don’t belong to the Marketing API, the Messenger Platform or the Facebook Webhooks are “normal” Graph API types and can be found in com.restfb.types.

Working with JSON

Per the architecture of RestFB, a JSON mapper is used to convert the Facebook JSON into Java objects and convert them afterwards in the RestFB types according to defined rules.

RestFB 2.x uses a slightly modified minimal-json to map the JSON, because it is not only an implementation that has rather small code base, but it is a fast mapper. Moreover, the minimal-json code is completely covered by junit tests.

Using DefaultJsonMapper, RestFB is able to recursively map JSON fields annotated with @Facebook to the following Java types out of the box:

  • The convenience classes provided by RestFB in com.restfb.types
  • String
  • Integer
  • Boolean
  • Long
  • Double
  • Float
  • java.util.Date
  • Enum
  • BigInteger
  • BigDecimal
  • Optional
  • Connection
  • Your own JavaBean-compliant classes
    Don’t forget to provide a public default constructor!
  • Lists of any of the above types
  • Map as key-value store with String keys

For example:

public class MyClass {
  @Facebook
  String name;

  @Facebook
  BigDecimal value;

  // If a Facebook field doesn't match your field's name, specify it explicitly

  @Facebook("lots_of_numbers")
  List<Integer> lotsOfNumbers;

  // You can annotate methods with @JsonMappingCompleted to perform
  // post-mapping operations.
  //
  // This is useful if you want to massage the data FB returns.

  @JsonMappingCompleted
  void allDone(JsonMapper jsonMapper) {
    if(lotsOfNumbers.size() == 0)
      throw new IllegalStateException("I was expecting more numbers!");
  }
}

Java to JSON mapping

Java to JSON mapping is supported by default via JsonMapper.toJson(Object object). You may recursively convert primitive wrapper types as specified above, Lists, Maps with String keys, and your own Javabean types by applying the @Facebook annotation to any fields you’d like to include in the conversion.

The Connection type is handled different and completely ignored in the Java to JSON conversion.

Additionally the Optional type will return the value in the JSON.

The default behavior of DefaultJsonMapper is to throw a FacebookJsonMappingException if it cannot map JSON to Java correctly. However, given the frequency with which the Facebook API changes, you might want to guard yourself from “surprise” errors in production by exerting more fine-grained control over how the mapper handles mapping exceptions. You can do so like this:

FacebookClient facebookClient = new DefaultFacebookClient("MY_ACCESS_TOKEN",
  new DefaultWebRequestor(), new DefaultJsonMapper(new JsonMappingErrorHandler() {
    public boolean handleMappingError(String unmappableJson, Class<?> targetType, Exception e) {

      // Here you might log the fact that JSON mapping failed.

      err.println(format("Uh oh, mapping %s to %s failed...", unmappableJson, targetType));

      // Returning true tells the mapper to map this JSON as null and keep going.
      // Returning false tells the mapper to throw an exception.

      return true;
    }
  }));

As-is, DefaultJsonMapper should meet the needs of the vast majority of users. If it doesn’t support a feature you need, you can easily subclass it or write your own implementation of JsonMapper instead.

Sometimes you can’t know field names at compile time so the @Facebook annotation can’t be used. Or maybe you’d like full control over the data that gets returned. Either way, RestFB has you covered. Just map any API call to JsonObject.

JsonObject is the lowest-level object type. You have full control over the object, but you don’t have convenient type mapping support available. For example, date fields are just Strings and you have to map them manually to a Date object. The version independence regarding the Graph API is gone, too. You should only work with the JsonObject if you know what you are doing.

Fetching a single object is almost the same as with a standard RestFB type. You simply choose the JsonObject as returned type and you are ready to go. Afterwards you can use the JsonObject API.

The JsonObject API used by RestFB

Since RestFB 2.x the internally-used JsonObject implementation is based on the minimal-json library. In our benchmarks, this implementation is really fast.

JsonObject btaylor = facebookClient.fetchObject("btaylor", JsonObject.class);

out.println(btaylor.getString("name"));

If you’d like to use a JsonObject in a Connection, there are two ways you can go. The first was is to simply use the JsonObject as the returned type and work with fetchConnection as described in the Fetching Object List section.

Connection<JsonObject> photosConnection = facebookClient.fetchConnection("me/photos", JsonObject.class);

for (List<JsonObject> objectList: photosConnection) {
   for (JsonObject photo: objectList) {
       String photoUrl = photo.getString("source");
       out.println(photoUrl);
   }
}

The second way is even more low-level and you only work with the JSON returned by Facebook. You should have a good understanding of the returned JSON in order to access the fields. The example below shows how to do this:

JsonObject photosResponse = facebookClient.fetchObject("me/photos", JsonObject.class);

String firstPhotoUrl = photosResponse.getJsonArray("data").getJsonObject(0).getString("source");
out.println(firstPhotoUrl);

Using the low-level JsonObject is sometimes OK, but perhaps you’d like to map the object or special fields to RestFB types. You can manually invoke the built-in JsonMapper to do this.

List<String> ids = new ArrayList<String>();
ids.add("btaylor");
ids.add("http://www.imdb.com/title/tt0117500/");

// First, make the API call...

JsonObject results = facebookClient.fetchObjects(ids, JsonObject.class);

// ...then pull out raw JSON data and map each type "by hand".
// Normally your FacebookClient uses a JsonMapper internally, but
// there's nothing stopping you from using it too!

JsonMapper jsonMapper = new DefaultJsonMapper();
User user = jsonMapper.toJavaObject(results.getString("btaylor"), User.class);
Url url = jsonMapper.toJavaObject(results.getString("http://restfb.com"), Url.class);

out.println("User is " + user);
out.println("URL is " + url);

Error Handling

All FacebookClient methods may throw FacebookException, which is an unchecked exception as of RestFB 1.6.

These are the FacebookException subclasses that you may catch:

  • FacebookJsonMappingException

    Thrown when an error occurs when attempting to map Facebook API response JSON to a Java object. It usually indicates that you’ve used the @Facebook annotation on a field with an unsupported type or the Facebook API JSON doesn’t map correctly to the fields you’ve annotated (e.g. attempting to map a JSON string to a Java BigDecimal or a JSON object to a Java List).

    You generally should not explicitly catch this exception in your code, as it usually signifies programmer error in setting up @Facebook annotations. One valid use for catching this exception, however, is to detect when Facebook changes what an API call returns on their end, which would break your live code. It may be useful to catch this exception and then send a notification to you or your ops team to notify them that your application needs to be updated.

  • FacebookNetworkException

    Thrown when a failure occurs at the network level. This can happen if your machine doesn't have a network connection or the Facebook API endpoint returns an unexpected HTTP status code. If there's an HTTP status code available, it's included in the exception so you may take custom actions depending on what type of error occurred.

  • FacebookGraphException

    Thrown when the Graph API returns an error, as shown in the example JSON snippet below. Exposes the type and message so you can handle them in your code and take custom action as needed.

    {
      "error": {
        "type": "SomeBizarreTypeOfException",
        "message": "Everything went wrong."
      }
    }

    Note that FacebookGraphException is a catchall Graph API exception. For your convenience, RestFB will throw more-specific subclasses FacebookOAuthException and FacebookQueryParseException if it detects either of these Graph API error types. These are described below.

  • FacebookOAuthException

    Thrown when the Graph API returns an OAuth-related error (type OAuthException or OAuthAccessTokenException), as shown in the example JSON snippet below.

    {
      "error": {
        "type": "OAuthException",
        "message": "Invalid access token signature."
      }
    }
  • FacebookQueryParseException

    Thrown when the Graph API returns an FQL query parsing error (type QueryParseException), as shown in the example JSON snippet below.

    {
      "error": {
        "type": "QueryParseException",
        "message": "Unknown path components: /fizzle"
      }
    }
  • FacebookResponseStatusException

    This is thrown by RestFB when an FQL call fails. FacebookGraphException and its subclasses are not applicable in that case because Facebook returns this “legacy” exception instead due to FQL not yet being a full-fledged member of the Graph API.

    FacebookResponseStatusException will include both the error code and error message returned by the Facebook API so you may take custom actions depending on the type of error that occurred.

  • FacebookResponseContentException

    This is thrown by RestFB when Facebook responds with unexpected data. For example, when extending an access token, Facebook should return an HTTP response body of the form access_token=123&expires=456. But if Facebook does not respond as expected - perhaps they modify the response format in some future API release - FacebookResponseContentException will be thrown.

    It is unlikely that you will ever see this exception.

Here’s some example code to illustrate the above. Keep in mind that your code doesn’t need to handle every single exception the way we’re doing here - this is just to demonstrate what’s possible.

try {
  Connection<User> myFriends = facebookClient.fetchConnection("me/friends", User.class);

} catch (FacebookJsonMappingException e) {
  // Looks like this API method didn't really return a list of users
} catch (FacebookNetworkException e) {
  // An error occurred at the network level
  out.println("API returned HTTP status code " + e.getHttpStatusCode());
} catch (FacebookOAuthException e) {
  // Authentication failed - bad access token?
} catch (FacebookGraphException e) {
  // The Graph API returned a specific error
  out.println("Call failed. API says: " + e.getErrorMessage());
} catch (FacebookResponseStatusException e) {
  // Old-style Facebook error response.
  // The Graph API only throws these when FQL calls fail.
  // You'll see this exception more if you use the Old REST API
  // via LegacyFacebookClient.
  if (e.getErrorCode() == 200)
    out.println("Permission denied!");
} catch (FacebookException e) {
  // This is the catchall handler for any kind of Facebook exception
}

Here’s some example code to illustrate the above. Keep in mind that your code doesn’t need to handle every single exception the way we’re doing here - this is just to demonstrate what’s possible.

See Facebook documentation

To prevent abuse of the Graph API, Facebook uses a rate limiting mechanism. The number of calls you are allowed to make depends on the kind of request. There are 3 types of limits: app limit, user limit and page limit.

The app limit is tied to the app. As soon as you hit this limit, you are in trouble. because you will not be able to make any more calls using this Facebook app. This is a limit that affects all of your users and Pages.

The user limit comes into play when you use a user access token. This limit can can be reached when a user sends too many requests to Facebook. If you reach this limit, the application can be used by other users and the application limit will not be triggered. by other users and the app limit will not be triggered. The user will have to wait before new requests can be sent.

The page limit is a bit different and more complicated. This limit is reached if a page access token is used to send too many requests to Facebook. But the page limit is directly connected to the page and not to the application. If any application that manages a page triggers the limit, all applications that use a page access token for this page are in trouble. In the past, Facebook has permitted the app owner to send an increase request to enlarge the limit for the page. Now they are using an opaque algorithm to detect a suitable limit.

RestFB supports the limits and you can get the limit information from the WebRequestor. The WebRequestor provides a DebugHeaderInfo with rate limiting and some additional debug information Facebook provides per call and developers can process this information and act according to the limits given. In general, the rate limits are not provided until you reach an 80% threshold. Once you are above this limit, the request will include the rate limit and you can reduce your calls.

The DebugHeaderInfo can be fetched after every client call like this:

DebugHeaderInfo headerInfos = client.getWebRequestor().getDebugHeaderInfo();
System.out.println("App usage: " + headerInfos.getAppUsage());
System.out.println("Page usage: " + headerInfos.getPageUsage());

The example above shows only a short sample and you can get more information. Very important for opening new bug reports on Facebook is the trace id. A complete explanation of the returned header fields can be found here.

Extensibility and Unit Testing

In addition to FacebookClient, RestFB provides default implementations for WebRequestor and JsonMapper, two components that DefaultFacebookClient depends on to do its work.

These dependencies are designed to allow for straightforward subclassing (if you just want to replace a little functionality) and simple custom implementations (if you need full control).

This comes in handy when unit testing - for example, you can write your own WebRequestor implementation that simulates a Facebook API endpoint response. You can drop in custom data designed to exercise your application’s Facebook integration or simulate error conditions to make sure you’re handling them properly.

Here’s a trivial example which shows one way you might implement this:

FacebookClient facebookClient = new DefaultFacebookClient(MY_ACCESS_TOKEN,

  // A one-off DefaultWebRequestor for testing that returns a hardcoded JSON
  // object instead of hitting the Facebook API endpoint URL

  new DefaultWebRequestor() {
    @Override
    public Response executeGet(String url) throws IOException {
      return new Response(HttpURLConnection.HTTP_OK,
        "{'id':'123456','name':'Test Person'}");
    }
  }, new DefaultJsonMapper(), Version.LATEST);

// Make an API request using the mocked WebRequestor

User user = facebookClient.fetchObject("ignored", User.class);

// Make sure we got what we were expecting

assert "123456".equals(user.getId());
assert "Test Person".equals(user.getName());

Boost Performance

Generating a SimpleDateFormat instance is incredibly expensive and you should not generate a new formatter for every date string you have to parse. Therefore RestFB provides the possibility to replace the old and slow parsing with a faster solution. But you have to take care of some cleanup because the higher speed can result in a small memory leak. Of course RestFB provides everything you need to do it right.

With version 1.7.0 you can decide which DateFormatStrategy you use. The default strategy is the SimpleDateFormatStrategy. It’s almost the same behavior as in the versions before 1.7.0 and you don’t need to change anything.

The faster strategy is called CachedDateFormatStrategy. You have to set this manually in the DateUtils singleton and call the clearThreadLocal method at the end of a thread to prevent the memory leak mentioned above. You should have a look at the code example below.

Because we provide the DateFormatStrategy as an interface you can implement your own strategy.

DateUtils.setDateFormatStrategy(new CachedDateFormatStrategy());
// do your normal stuff in that thread
...
// clean the thread local cache just before you finish the thread
DateFormatStrategy strategy = DateUtils.getDateFormatStrategy();
if (strategy instanceof CachedDateFormatStrategy)
  ((CachedDateFormatStrategy)strategy).clearThreadLocal();

See Graph API documentation

You can improve your connection’s performance by using the ETagWebRequestor. This WebRequestor is a special implementation wich adds ETag support to the restfb’s requests. For further information about ETag you should have a look at the wikipedia page.

With the EtagWebRequestor the response body of every single request is stored in a cache. If you send the same request again to the Graph API, a hash key is added to the request header. Facebook will check the hash key and if the new response is the same as the stored one, Facebook returns an HTTP 304 and RestFB uses the cached response body. If an HTTP 200 is received instead, the old cached response body is replaced with the newly received response.

With this Requestor you can cut down on a lot of network traffic and it is very easy to use. This snippet shows how the EtagWebRequestor is created and used in RestFB.

Disable Caching while using EtagWebRequestor

Sometimes it is useful to switch to the non-caching version. The EtagWebRequestor provides the isUseCache and setUseCache methods. With these methods you can ask the Requestor if caching is enabled and switch it to your needs.

// create the special WebRequestor with ETag support
ETagWebRequestor webRequestor = new ETagWebRequestor();
FacebookClient client = 
  new DefaultFacebookClient(accessToken, webRequestor, new DefaultJsonMapper(), Version.LATEST);

Possible problems

  1. Because every call is stored in the cache, this may lead to a performance degredation if the cache size increases significantly.
  2. The cache uses a SoftHashMap to store the response bodies. If you drive your system to the memory limit, the JVM will remove elements from the cache to allow the system to work normally. If this happens at a really bad time, RestFB may run into problems and you will have to re-try your request.

It is possible to use another inner Map implementation. We have a static method to change the implementation before the ETagWebRequestor is initiated.

ETagWebRequestor.setMapSupplier(HashMap::new);
ETagWebRequestor requestor = new ETagWebRequestor();

Afterwards you can use the webRequestor as shown above.

Links

Visit RestFB’s home on Github.

Have a question not covered here? Ask in the RestFB Google Group.

Found a bug or have an enhancement request? Create a ticket in the issue tracker.

Download a read-only copy of the current source code using Git:

git clone git://github.com/restfb/restfb.git