Getting the share count of a URL

30757382

At work I’m working on a project in which we need to track the share count of a URL. Below are my quick notes. In all examples I’m getting the share count of https://www.facebook.com/. Note that I’m using the jq notation to extract the actual result from the response.

Just GET it

Facebook

Request:

curl --silent -X GET http://graph.facebook.com/?id=https://www.facebook.com/

UPDATE August 23, 2016

As of August 7, 2016 version 2.0 of the Graph API has been disabled. Version 2.1 is now the default one. In between 2.0 and 2.1 the response format has been changed. Thanks go out to Nabil Souk for pointing this out. This post has been updated to reflect this change. For archival purposes both responses (both the old, and new) are still included on this page.

Response (Graph API v2.0, pre August 7, 2016):

{
  "id": "https://www.facebook.com/",
  "shares": 42944662,
  "comments": 1086
}

The count can be found in .shares

Response (Graph API v2.1 and higher, post August 7, 2016):

{
    "og_object": {
        "id": "10151063484068358",
        "title": "Welcome to Facebook - Log In, Sign Up or Learn More",
        "type": "website",
        "updated_time": "2016-08-23T08:50:45+0000"
    },
    "share": {
        "comment_count": 1335,
        "share_count": 113774962
    },
    "id": "https://www.facebook.com/"
}

The count can be found in share.share_count

Twitter

Unfortunately it’s not that easy (anymore). See below for a possible solution.

LinkedIn

Request:

curl --silent -X GET 'https://www.linkedin.com/countserv/count/share?url=https%3A%2F%2Fwww.facebook.com&format=json'

Response:

{
  "count": 1672,
  "fCnt": "1,672",
  "fCntPlusOne": "1,673",
  "url": "https://www.facebook.com"
}

The count can be found in .count

Google Plus

Request:

curl --silent -H 'Content-type: application/json' -X POST -d '[{"method":"pos.plusones.get","id":"p","params":{"nolog":true,"id":"https://www.facebook.com/","source":"widget","userId":"@viewer","groupId":"@self"},"jsonrpc":"2.0","key":"p","apiVersion":"v1"}]' https://clients6.google.com/rpc

Response:

[
 {
  "id": "p",
  "result": {
   "kind": "pos#plusones",
   "id": "https://www.facebook.com/",
   "isSetByViewer": false,
   "metadata": {
    "type": "URL",
    "globalCounts": {
     "count": 430133.0
    }
   },
   "abtk": "AEIZW7RT8UqRam+DqDUFw3fY9+GpV+OwizHp+BDdEKudWO37f2nItnJhQKKGNT3nMuz5XXwcRe8qSDQMNkHTiOOeHtxJCcvJdg=="
  }
 }
]

The count can be found in .[0].result.metadata.globalCounts.count

Other Services

More services (Buffer, Pinterest, etc.) needed? Check https://www.madebymagnitude.com/blog/getting-your-social-share-counts-with-php/ for inspiration.

Scripts / Inspiration

Some scripts that might help you out:

So, what about Twitter?

The problem

In the past it was possible to send a GET request to https://cdn.syndication.twitter.com/widgets/tweetbutton/count.json?url={URL}. However, the “Count Endpoint” has been shut down and now returns a 410 Gone

A possible solution

Use Twitter’s Search API:

The Twitter Search API is part of Twitter’s v1.1 REST API. It allows queries against the indices of recent or popular Tweets

Endpoint URLs that might be of interest:

But:

Before getting involved, it’s important to know that the Search API is focused on relevance and not completeness. This means that some Tweets and users may be missing from search results.

In detail: “Not all Tweets will be indexed or made available via the search interface.” and “[T]he Search API is focused on relevance and not completeness. This means that some Tweets and users may be missing from search results.” and “Search API usually only serves tweets from the past week.”

Oh.

A proper solution then?

As Twitter’s Search API mentions:

If you want to match for completeness you should consider using a Streaming API instead.

The Streaming API allows you to track Public Streams. Track that, and then count yourself.

This code snippet using the twitter-stream-api NPM module should get you started:

var TwitterStream = require('twitter-stream-api'),
    fs = require('fs');

// Create your keys at https://apps.twitter.com/
var keys = {
    consumer_key : "consumer-key",
    consumer_secret : "consumer-secret",
    token : "your-token",
    token_secret : "your-token-secret"
};

var Twitter = new TwitterStream(keys, false);

Twitter.stream('statuses/filter', {
    track: 'npmjs.com/package/defer'
});

Twitter.on('connection success', function (uri) {
    console.log('connection success', uri);
});

Twitter.on('connection aborted', function () {
    console.log('connection aborted');
});

Twitter.on('connection error network', function (error) {
    console.log('connection error network', error);
});

Twitter.on('connection error stall', function () {
    console.log('connection error stall');
});

Twitter.on('connection error http', function (httpStatusCode) {
    console.log('connection error http', httpStatusCode);
});

Twitter.on('connection rate limit', function (httpStatusCode) {
    console.log('connection rate limit', httpStatusCode);
});

Twitter.on('connection error unknown', function (error) {
    console.log('connection error unknown', error);
    Twitter.close();
});

Twitter.on('data', function (obj) {
    console.log('data', obj.toString());
});

Twitter.on('data keep-alive', function () {
    console.log('data keep-alive');
});

Twitter.on('data error', function (error) {
    console.log('data error', error);
});

Twitter.pipe(fs.createWriteStream('tweets.json'));

Note: Protected Streams won’t show up in here, of course.

Note: If the script is not running you’ll, as this is a “live” API, miss counts! Filling the gaps with a normal REST API request won’t be 100% accurate (see above).

Good to know: “Note that display_url does not contain a protocol, so this is not required to perform a match.”

Need to know: “URLs are considered words for the purposes of matches which means that the entire domain and path must be included in the track query for a Tweet containing an URL to match.”
→ Translated: no partial URL matches!

Beware though: “Each phrase must be between 1 and 60 bytes, inclusive.”
→ Translated: no long URLS!

Why make it so difficult, Twitter?

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Join the Conversation

14 Comments

  1. Great post, Bramus!

    I’d like to add some notes on Twitter:
    1. Twitter Search is indeed not complete, however, it generally only omits results from very low quality accounts (like ones with < 10 followers). Those tend to a) produce very little tweets or b) produce too many spammy tweets so they're worth suppressing. So, generally you don't lose much with Search API.
    2. Stream API seems like a more precise solution, but it's not. The problem is with shortened urls (and other url variants that do not mention the target domain name). Very big volume of shares happens through shortened urls. Twitter crawls all urls to determine the target destination, and that destination is indexed by Twitter search. So when you request domain name with Search API, you get tweets with short urls too. However, that is not the case with Stream API. Stream API will only get you tweets that mention the domain name explicitly. Which means that you lose all tweets with shortened urls. And of course you have to ensure 100% uptime of streaming client or you lose tweets too.

    I'd recommend some third-party solution for tracking Twitter share counts, like newsharecounts.com (which I'm involved with), or twitcount.com. Those handle all the complexities and let you get your share counts without any pains.

    Have a nice day!

  2. Hello there.
    Note that the Facebook response has changes. I just noticed that in my implementation. The response for the shares count is as following inside the api response object:
    share: {
    comment_count: 0,
    share_count: 62
    }
    Cheers,

      1. Thank you Bramus! and Nabil. Our Facebook share counts unceremoniously disappeared Monday. If it weren’t for this post, I’d still be combing through the Facebook Developers pages.

        – Joe

        1. I have applied this way, thanks for the article 🙂
          But I’m having some problems,
          1. If I have not logged on facebook in the browser, the share count number does not appear.
          2. I applied this share count method on my websites that have high traffic, and someday, count share number that appears is 0, and when i checked the response from facebook, “request access limit”.
          Is there any way I can obtain share count without affected by the limitations of facebook?

          Sorry if my english bad 😀

      2. Hi,
        Could you update the jsfiddle to reflect the Facebook API changes? Doesn’t seem to work 🙂

        Best regards,

        Asbjoern

    1. Hi Nathan,

      The Graph API Docs have this description for the share_count (Graph API v2.1 – the now default one):

      The number of shares of this URL on Facebook. For numbers of likes see og_object.engagement. This count is different from the number of shares on the post that this URL is shared in.

      Haven’t tested this, but it looks like the share_count should only hold the actual share count (sans the number of likes).

  3. HI!
    First of all thanks for your post, i beleive this might help me a lot, since ALL my fb shares on mi blog are now gone…
    My question is, how do i insert the code? Should i place it on the social plugin i user or where exactly? Once i do this, am i gonna be able to “retrieve” the old shares i’ve lost?
    I’m pretty much a rookie on coding. Apreciate the help!

  4. Hi

    Why graph api gives share count for all urls of entire
    domain summed up rather than particular url for which I am making a request?

    Do I need to change any settings ??

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.