JavaScript API Tutorial


JavaScript API: The IN Object

The full power of our new framework lies within the JavaScript API. This API is effectively a bridge between the user's browser and our REST endpoint. As a developer, you use a simple, consistent JavaScript interface to interact with the fundamental LinkedIn data types (Profiles, Connections, People Search, etc). Under the hood, we translate your request into a REST call which we make on your behalf via Ajax. All the details of cross domain ajax and OAuth 2 are abstracted away - you simply invoke a resource and receive JSON in return.

To start, let's rewrite Hello World. This time you'll use the JavaScript API to fetch the user's profile information instead of tags and templates.

Begin, as before, by importing the same JavaScript with your API key. This time, add another two parameters: onLoad and authorize.

<script type="text/javascript" src="https://platform.linkedin.com/in.js">
  api_key: DXjUrY-9apre6gnyK080MpTIoIS4f38AleG08Y0kLM0DH2xeNQATIfDMuoisCMO_
  onLoad: onLinkedInLoad
  authorize: true
</script>

The onLoad parameter tells the framework to wait until it's finished loading and then invoke a JavaScript function of your choosing. This is very convenient if you have some initialization code which needs to reference the LinkedIn JavaScript API. In this example, the function's name is onLinkedInLoad. You can name it whatever you want.

As discussed previously, setting authorize to true automatically authenticates any viewers who have previously granted your site permission using the JavaScript API.

Authenticating the Member

Many LinkedIn API calls require authentication (i.e. they require that an active user is signed into your application and has granted your app permission to call LinkedIn on his or her behalf). Thus, most of your code's interaction with the LinkedIn APIs will only work after an authorization event has occurred. The framework has eventing support to notify you when an 'auth' occurs. Inside our framework load handler (onLinkedInLoad), let's register an auth event handler:

function onLinkedInLoad() {
     IN.Event.on(IN, "auth", onLinkedInAuth);
}

The IN object is your way to hook into the LinkedIn JavaScript API. It's what the framework script tag inserts into your page. All the JavaScript API functions will begin with IN..

You see the first API already: In.Events. Events let you hook into the system, listen for activities, and run code when they happen. This line says to listen for the auth event, which triggers when someone signs in, and execute the onLinkedInAuth() function afterwards. Events are covered in more detail in the JavaScript API Reference.

You have a function ready to run when the person viewing your page signs-in, but you still need them to do that. The Login tag is the easiest way to invoke the authentication process, so add this to the document body:

<script type="IN/Login"></script>
 

It renders as:

See the Sign In button in action! (If you're already signed in, you'll get a blank page. This is good!)

Retrieving Profile Information

Now you're all set to run code after you get an authenticated member. We'll start by fetching the member's profile:

function onLinkedInAuth() {
     IN.API.Profile("me").result(displayProfiles);
}

The first part, IN.API.Profile("me"), makes a request to the LinkedIn API servers for the profile belonging to the signed-in user. The keyword "me" is a special short-hand way of referring to the signed-in member.

After it successfully retrieves data, the JavaScript framework sends the information to a callback function you specify in the result() method. In this case, it's a reference to the displayProfiles() function which we'll define below.

The In.API.Profile() method supports three of ways to identify a member: "me", member ids (like 4KoRHzE6GO), and public profile URLs (like https://www.linkedin.com/in/jakobheuser).

Processing Profile Information

Results from the Profile API come grouped under one parameter:

function displayProfiles(profiles) {
     member = profiles.values[0];
     document.getElementById("profiles").innerHTML = 
          "<p id=\"" + member.id + "\">Hello " +  member.firstName + " " + member.lastName + "</p>";
}

The displayProfiles() function receives a parameter which is the complete JSON-formatted result of the underlying call. For example:

By convention the data is always in a values array. Since this is the Profile API call, values contains profile data. In this case, there's only one profile for the signed-in member, me, so it's in values[0]. The fields and field names returned for a profile are the same as in the templating section: id, firstName, lastName, headline, and pictureUrl. There is also a _total property. This is metadata about the request and is discussed in more detail later on.

You need a place to display that data, so in body of the HTML page, put an empty div with id of profiles. Then, via JavaScript, you can fetch that div and set its content to be a paragraph of "Hello firstName lastName" using data fetched dynamically from the API.

<div id="profiles"></div>

At the same time, you can set the id of the <p> element to be the id of the member. This lets you refer to it later on and know which person you mean. Member ids are the best way to unique identify a person who has granted your application access to make calls on their behalf. Store these values in a database on your server to remember them. You can use them when printing out Tags, making JavaScript API calls, and even using the LinkedIn REST APIs. The same member id works across all three systems. These ids are specific to your application.

Putting it all together

Here's the entire HTML page:

<html>
<head>
<title>LinkedIn JavaScript API Hello World</title>

<!-- 1. Include the LinkedIn JavaScript API and define a onLoad callback function -->
<script type="text/javascript" src="https://platform.linkedin.com/in.js">/*
  api_key: DXjUrY-9apre6gnyK080MpTIoIS4f38AleG08Y0kLM0DH2xeNQATIfDMuoisCMO_
  onLoad: onLinkedInLoad
  authorize: true
*/</script>

<script type="text/javascript">
  // 2. Runs when the JavaScript framework is loaded
  function onLinkedInLoad() {
    IN.Event.on(IN, "auth", onLinkedInAuth);
  }

  // 2. Runs when the viewer has authenticated
  function onLinkedInAuth() {
    IN.API.Profile("me").result(displayProfiles);
  }

  // 2. Runs when the Profile() API call returns successfully
  function displayProfiles(profiles) {
    member = profiles.values[0];
    document.getElementById("profiles").innerHTML = 
      "<p id=\"" + member.id + "\">Hello " +  member.firstName + " " + member.lastName + "</p>";
  }
</script>
</head>
<body>

<!-- 3. Displays a button to let the viewer authenticate -->
<script type="IN/Login"></script>

<!-- 4. Placeholder for the greeting -->
<div id="profiles"></div>

</body>
</html>

See Hello World in action!

To recap:

  1. Include the LinkedIn JavaScript in the head, along with your api_key and onLoad handler.
  2. Define three functions: one to execute once the LinkedIn JavaScript is loaded, another to make the Profile API call for the signed-in member, and the third to greet the member!
  3. Add an IN/Login tag to let the member sign in.
  4. Insert a <div> as a placeholder for the greeting. You'll dynamically set the greeting using JavaScript-based manipulation of the DOM.

Multiple Members

Of course, IN.API.Profile() isn't restricted to a single member. You can pass multiple member ids to retrieve a group of profiles at once:

function onLinkedInAuth() {
  IN.API.Profile("me","url=https://www.linkedin.com/in/jakobheuser")
    .result(displayProfiles);
}

Then adjust the displayProfiles() function to iterate through the results:

function displayProfiles(profiles) {
  var profilesDiv = document.getElementById("profiles");

  var members = profiles.values;
  for (var member in members) {
    profilesDiv.innerHTML += "<p>Welcome " + members[member].firstName + " " + members[member].lastName + "</p>";
  }
}

See Multiple Members in action!

Before, you retrieved an array with one element. This time, there are multiple elements in the members variable, so you must iterate through each element with a for loop, or any of your favorite array iteration techniques.

Additional Profile Fields

By default, the profiles you receive from the Profile API will only contain a few basic fields (id, firstName, lastName). But many more fields are available within the LinkedIn Platform. To request additional profile fields, such as industry or distance, use the fields() method:

function onLinkedInAuth() {
  IN.API.Profile("me","url=https://www.linkedin.com/in/jakobheuser")
    .fields("firstName", "lastName", "industry")
    .result(displayProfiles);
}

Chain the fields() method along the requested fields in between the members and the result() method.

This data is then available in displayProfiles():

function displayProfiles(profiles) {
  var profilesDiv = document.getElementById("profiles");

  var members = profiles.values;
  for (var member in members) {
    profilesDiv.innerHTML += "<p>" + members[member].firstName + " " + members[member].lastName 
      + " works in the " + members[member].industry + " industry.";
  }
}

See Additional Profile Fields in action!

To learn more, read about all the availableProfile Fields. Since this is JSON, you'll need to convert field names from hyphened to camelCase. For instance, first-name becomes firstName.

Handling Errors

The JavaScript API will inspect the REST response on your behalf and if an error occurred (the API returns an HTTP non-200 status code), the framework will notify you. You can specify a special error handler for using the optional error() method:

function onLinkedInAuth() {
  IN.API.Profile("me","url=https://www.linkedin.com/in/an-invalid-public-profile-url")
    .fields("firstName", "lastName", "industry")
    .result(displayProfiles)
    .error(displayProfilesErrors);
}

Like result(), it takes a function reference as its parameter.

function displayProfilesErrors(error) {
  profilesDiv = document.getElementById("profiles");
  profilesDiv.innerHTML = "<p>Oops!</p>";
  console.log(error);
}
          

See Handling Errors in action!

The error object contains four properties: errorCode, message, status, and timestamp. At present, the message property is your best bet for debugging. We hope to improve the metadata associated with API errors in the future.

Moving Beyond Profiles (to Connections, People Search, Network Updates)

LinkedIn contains many other useful resources beyond member profiles. The JavaScript API allows you access to every part of LinkedIn exposed through REST APIs. For a complete list read out REST APIs documentation.

The commonly used resources have high-level wrapper classes to help you access them (IN.API.Profile is one such wrapper). Currently, these are:

  • IN.API.Connections
  • IN.API.MemberUpdates
  • IN.API.NetworkUpdates
  • IN.API.PeopleSearch
  • IN.API.Profile

The same pattern used in IN.API.Profiles() applies to these calls. Fundamentally, you access a resource by specifying up to three different types of parameters:

  • ids - the unique identifiers of the members in question; frequently provided as arguments to the resource constructor, as in IN.API.Profile("me")
  • fields - the list of data fields which should be included in the response
  • params - additional parameters which control the behavior of the resource (these are passed as HTTP query string parameters) 

With this consistent pattern, the other resources are hopefully familiar. For instance, here's a call to fetch the current user's connections:

function onLinkedInAuth() {
  IN.API.Connections("me")
    .fields("firstName", "lastName", "industry")
    .result(displayConnections)
    .error(displayConnectionsErrors);
}

Since IN.API.Connections() returns a result that's formatted similar to IN.API.Profile (after all, they are both just collections of people-descriptions), we can reuse our displayProfiles() with minimal changes. Thus, we arrive at displayConnections():

function displayConnections(connections) {
  var connectionsDiv = document.getElementById("connections");

  var members = connections.values; // The list of members you are connected to
  for (var member in members) {
    connectionsDiv.innerHTML += "<p>" + members[member].firstName + " " + members[member].lastName
      + " works in the " + members[member].industry + " industry";
  }     
}

function displayConnectionsErrors(error) { /* do nothing */ }

See Connections in action!

Other than swapping connections for profiles, this function is the same. However, if you're like many LinkedIn members, the output might be very different. Instead of getting two results, you will receive 10s or 100s of profiles. This could be more than you want. For no other reason, it can take a long time to retrieve and process.

A new chained method comes to the rescue: params(). With params() you pass in a hash of additional query parameters to modify the request. Usual suspects include count, to limit the results to a maximum number and start, to shift across the result set. The specific ones vary from function to function, but here's the Connections API documentation.

One key to remember about params is that -- unlike the rest of the JavaScript API -- you should use a hyphenated name, not a camel case name.

To only retrieve connections 11-15, do:

function onLinkedInAuth() {
  IN.API.Connections("me")
    .fields("firstName", "lastName", "industry")
    .params({"start": 10, "count": 5}) // start begins at 0
    .result(displayConnections)
    .error(displayConnectionErrors);
}

When you limit results similar to this, your callback function doesn't necessarily need to change. All it needs to know is that it's processing a group of people. But you may want to add some logic to provide pagination or display the total count. These values are there as metadata variables: _count, _start, and _total:

function setConnections(connections) {
  var profileDiv = document.getElementById("connections");

  var start = connections._start + 1; // because humans count from 1, not 0.
  var range = connections._start + connections._count;
  connectionsDiv.innerHTML = "<p>Displaying " + start + "-" + range + " of " + connections._total + " connections.</p>";

  var members = connections.values;
  for (var member in members) {
    profileDiv.innerHTML += "<p>" + members[member].firstName + " " + members[member].lastName
      + " works in the " + members[member].industry + " industry.</p>";
  }
}

See Parameters in action!

Use the metadata variables to tell the viewer they're looking at one page of the result set. In this example, it says:

Displaying 5-14 of 605 connections.

People Search

The PeopleSearch API provides you with the full searching power of linkedin.com. The complete set of search options is available in the main People Search REST API documentation, but here are a few examples to get started:

Find the 10 people who currently work at LinkedIn who are closest in the network to you:

IN.API.PeopleSearch()
  .fields("firstName", "lastName", "distance")
  .params({"company-name": "LinkedIn", "current-company": true, "count": 10, "sort": "distance"})
  .result(displayPeopleSearch)
  .error(displayPeopleSearchError);

Or the most relevant people who are currently employed as Senior Ninjas:

IN.API.PeopleSearch()
  .fields("firstName", "lastName", "positions")
  .params({"title": "Senior Ninja", "current-title": true})
  .result(displayPeopleSearch)
  .error(displayPeopleSearchError);

There are numerous different parameters and values you can use here. Don't forget that any params() values need to be hyphenated (company-name) instead of camel case (companyName).

Once the call is made, the information you receive is organized slightly different than IN.API.Profile() and IN.API.Connections():



function displayPeopleSearch(peopleSearch) {
  var peopleSearchDiv = document.getElementById("peoplesearch");
     
  var members = peopleSearch.people.values; // people are stored in a different spot than earlier example
  for (var member in members) {
    // but inside the loop, everything is the same
    // extract the title from the members first position
    peopleSearchDiv.innerHTML += "<p>" + members[member].firstName + " " + members[member].lastName 
      + " is a " + members[member].positions.values[0].title + ".</p>";
  }
}

See People Search in action!

Instead of placing the list of people directly under the returned object, it's placed inside a people object. But, once you reference the people, the data looks the same (a collection of profiles). So in our code, only the assignment to the members variable needs to change.

Notice also that unlike earlier data returned inside the values array, positions is not a string. It is an array, because a person may have multiple current and past positions. Therefore, as previous arrays placed data inside of values, positions follows that convention. In this example, you grab the title from the first element in that array. As the image shows, there are other values available for you, too. Check the People Search API for the full list.

Member and Network Updates

Member Updates and Network Updates provide access to a user's network updates, which is the LinkedIn term for the user's feed. This call returns most of what shows up in the timeline of the LinkedIn.com home page, either for the member or the member's connections.

IN.API.MemberUpdates() retrieves recent updates for a specific member or members. IN.API.NetworkUpdates() retrieves recent updates from a member's first degree connections. Be careful, while you can assume that you can always retrieve the current (signed-in) user's feed, you may not be able to see the feed of another arbitrary member. Additionally, you can never retrieve the full Network Updates for any member other than the current (signed-in) member.

Here's a short snippet showing how to use IN.API.NetworkUpdates():

function onLinkedInAuth() {
  IN.API.NetworkUpdates()
     .params({"type": "SHAR", "count": 5}) // get the five most-recent Shares
     .result(displayNetworkUpdates)
     .error(displayNetworkUpdatesError);
}

The format for IN.API.NetworkUpdates() is the same as what you've seen. This function does take a wide variety of query parameters, which are documented on the REST API page for this call. Here you're asking for the 5 most recent updates of type SHAR, which is the code for Shares.

What you get back is formatted differently because it's not a list of people, but a list of updates:

So, you cannot reuse the same base processing code. Instead, you need to peek inside updateContent to find a familiar person object, with first and last name. There is also a new item, currentShare, under which lives a comment field.

function displayNetworkUpdates(updates) {
  var profileDiv = document.getElementById("networkupdates");
     
  for (var i in updates.values) {
    var key = updates.values[i].updateKey; // each update has a unique key
    var share = updates.values[i].updateContent.person; // the person sharing content
    profileDiv.innerHTML += "<p id='" + key + "'>" + share.firstName + " " + share.lastName 
      + " shared " + share.currentShare.comment + ".</p>";
   }     
}

See Network Updates in action!

You can only fetch Network Updates for the currently signed-in member (in other words, you are not allowed to see the full "network view" for any member other than the current viewer). However, you can retrieve the updates generated by a single user, using IN.API.MemberUpdates(), for any person.  The syntax and response is identical, except that MemberUpdates() allows you to pass an id or multiple ids.

function onLinkedInAuth() {
  IN.API.MemberUpdates("me","EjDUWNoC3C")
     .params({"type": "SHAR", "count": 5}) // get the five most-recent Shares for the viewer and EjDUWNoC3C
     .result(displayNetworkUpdates)
     .error(displayNetworkUpdatesError);
}

See Member Updates in action! (This example only has your updates.)

Multiple Values for One Parameter Name

To pass multiple values for a single parameter name, use an array instead of a string. For example, to request both the SHAR and APPS type:

function onLinkedInAuth() {
  IN.API.MemberUpdates("me","EjDUWNoC3C")
     .params({"type": ["SHAR", "APPS"], "count": 5}) // get the five most-recent Shares and Application Updates 
                                                     // for the viewer and EjDUWNoC3C
     .result(displayNetworkUpdates)
     .error(displayNetworkUpdatesError);
}

Raw API Calls

While the helper methods above provide you access to fundamental pieces of LinkedIn data, there are other resources in the LinkedIn API which don't yet have JS API helpers. Don't worry - the JavaScript API allows you to make these calls manually by exposing raw access to our REST endpoint.

Here's a short example showing how to use IN.API.Raw() to retrieve the full list of people who have liked a Network Update:

function displayNetworkUpdates(updates) {
  var profileDiv = document.getElementById("networkupdates");
     
  for (var i in updates.values) {
    var key = updates.values[i].updateKey; // unique key used to reference <p> in Raw API call results
    var share = updates.values[i].updateContent.person; // person making update
    profileDiv.innerHTML += "<p id='" + key + "'>" + share.firstName + " " + share.lastName
      + " shared " + share.currentShare.comment + ".</p>";

    IN.API.Raw("/people/~/network/updates/key=" + key + "/likes") // construct REST URL
      .result( function(value) { return function(likes) { // need to wrap inside function to get proper key
        document.getElementById(value).innerHTML += " (Liked by " + likes._total + " people)";
       } }(key))
      .error( function(error) { /* do nothing */ } );
  }
}

See the Raw API in action!

As above, there is code to loop through each Network Update and print out the name and comment. While a Network Update returns the first three people who have liked an update, it doesn't return them all. However, there's an API for that.

Reading the documentation shows that the URL is https://api.linkedin.com/v1/people/~/network/updates/key={KEY}/likes, where {KEY} is the updateKey field from the response. The IN.API.Raw() function assumes the leading https://api.linkedin.com/v1, so you only need to pass the end of the string.

As with the other IN.API functions, you can chain result() and error() methods to the end. Since these are all asynchronous calls, this example uses some closure-fu to capture the key, letting you easily append to the end of each update the information retrieved. This is a similar technique (without using closures) that we did when we assigned the member id to the paragraph tag at the top of the page. Here, for simplicity, is the total number of likers.

Using the POST, PUT, and DELETE Methods

All the calls so far are only using the GET HTTP Method. However, the JavaScript API supports the full range of HTTP Methods, such as POST, PUT, and DELETE. Invoke these methods using the method() method, optionally passing a POST body string in the body() method.

Here's how to use them, using a member's current-status as an example. First, a GET request to read the current status:

IN.API.Raw("/people/~/current-status") // Read (GET) the status
  .result( function(result) { document.getElementById("statusDiv").innerHTML = result; } ) // XXX: Unsafe!
  .error(  function(error)  { /* do nothing */ } );

This uses IN.API.Raw() to retrieve the status and assigns it to statusDiv.

Be careful! Because a status can be any string, you must escape the results. Otherwise, you allow someone to inject HTML or JavaScript into your page. Dojo uses dojo.string.substitute() with a transform and jQuery uses .text().

To unset the status, use the same URI, but pass the DELETE method:

IN.API.Raw("/people/~/current-status") // Unset (DELETE) the status
  .method("DELETE")
  .result( function(result) { document.getElementById("statusDiv").innerHTML = "Status deleted"; } )
  .error(  function(error)  { /* do nothing */ } );

Many API calls that update content return either a 201 or 204 response code. These indicate the request was successful and either Created a new resource or has No Content in the response, respectively. In this case, we return 204 on success.

The JavaScript API funnels all 2xx responses to the result() callback function. However, you cannot determine which specific code was returned, nor can you access other HTTP Headers, such as a the Location header.

Last is the example to update a status. This uses the PUT method, to indicate that we're update the resource, along with JSON text in the body() with the new status.

IN.API.Raw("/people/~/current-status") // Update (PUT) the status
  .method("PUT")
  .body(JSON.stringify("Updating my status using the JavaScript API"))
  .result( function(result) { document.getElementById("statusDiv").innerHTML = "Status updated"; } )
  .error(  function(error)  { /* do nothing */ } );

The best way to convert a JavaScript value into JSON is with JSON.stringify(). Here is converts the JavaScript string into a JSON string. Mainly it adds quotation marks around the text. However, this also works for arrays and hashes, even when they are nested multiple levels. The function is also useful when you accept user input using a HTML form.

Next Steps

Check out Instant Search, a demonstration application that uses the LinkedIn JavaScript API. It uses IN.API.PeopleSearch() in a splashy way.

Read the JavaScript API Reference for full details on this and other API functions.

Start writing code!