This article was previously published in my blog, Just
Like a Magic.
Download twittoo;
our sample application
Overview
This writing discusses the Twitter API
and how you can utilize it in your managed application. It begins by a brief
discussion of the API and the methods used. After that, it digs into the
discussion of how you can utilize the API into your application with help of
code samples and examples. At the end of this writing, there's a nice
open-source Twitter client application available for download. Worth mentioning
that this article focuses on the REST API set of Twitter, specifically XML
endpoints.)
Introduction

Today, we are talking about Twitter as it is one
of the most famous social networking services. This article discusses how you
can create Twitter clients using .NET Framework. Oh, before we start, I'd like
to introduce you my twitter account first J, it's
@elsheimy.
Twitter API
What Twitter API is
Before we dig into the
discussion of Twitter API we need to know first what the Twitter API is.
Twitter API is a set of endpoints
(or methods) that allow applications to work with Twitter data.
Twitter API
consists of three parts:
- Two REST
APIs:
- REST API:
This is the core API set; it allows developers to access core
Twitter data, it contains most (if not all) of the methods and functions you
would use to utilize Twitter data in your application, and it supports (mostly)
three formats (or endpoints) for each method: XML, Atom, and JSON formats.
- Search API:
Fully integrating search methods allows you to search for
tweets based on specific criteria and to search for daily/weekly hot trends. It
supports only the JSON format. This set of API was originally developed by
Summize, Inc. and integrated into the Twitter API. (Twitter is looking
forward to unify the REST API.)
- Streaming
API:
A set of API methods that allow near-realtime
access to various subsets of Twitter public statuses. All methods are in JSON
format, no XML or ATOM endpoints available.
Actually, the REST API
would be very sufficient for you unless you find it easier to handle the JSON
format.
It is worth mentioning that the REST API provides very handful and
sufficient search methods that your application can use. If you need more
searching capabilities (like searching for tweets that confirm to a set of
criteria,) you can dig into the REST Search API.
You are not required to
stick to a specific set of API methods. You can mix the methods you like from
the three APIs.
Wikipedia:
REST (Representational State Transfer) is a style of software architecture for
distributed hypermedia systems such as the World Wide Web.
Wikipedia: JSON
(JavaScript Object Notation) is a lightweight text-based open standard designed
for human-readable data interchange based on the JavaScript programming
language.
Wikipedia:
Atom Publishing Protocol (AtomPub or APP) is a simple HTTP-based protocol for
creating and updating web resources.
This writing focuses on the
XML endpoints of the REST API.
API Documentation
Twitter has with a very nice
API documentation that documents each and all methods and endpoints of the three
parts of the API. This documentation can be found here:
http://apiwiki.twitter.com.
To
avoid duplication, we are not going to discuss the Twitter API again in this
writing; it is already documented and available for all users. Instead, we will
take a brief look at the API and discuss how we can utilize it in our .NET
application. For simplicity, we will focus on XML endpoints. Therefore, all of
our code would rely on the XML features of .NET Framework 2.0 (i.e.
System.Xml.dll library.)
Oh, you are free to write the code the way you like
(e.g. integrating LINQ for XML into the code.)Now, we are going to look
inside the Twitter API.
Things to be Kept into Mind
There are some
basics
of the Twitter API that a developer should keep into his mind.
- Calls
to the Twitter API are limited:
Don't expect to post unlimited updates or
to follow thousands of users in just an hour! Many methods have a call limit
(hourly/daily.) For example, at the time of this writing, you have only 150 API
requests per hour, further requests would return an exception. In addition, you
can send only 1,000 updates and 250 direct messages per day (can we remove
this word "only"?) You can check Twitter limits here.
- Every endpoint has its HTTP methods that need to be set (e.g. GET, POST, and
DELETE) in the request. The documentation of each endpoint lists the HTTP
methods required for that endpoint. For more information about HTTP and HTTP
methods check the RFC 2616
Section 9 document.
- Some methods (like methods send updates) require user authentication and
others not. Authentication is discussed soon.
API Methods
The following is a discussion about
Twitter API methods and how you can call them.
Method Address
Every method has an address (i.e.
URI). The address of a method is something like that:
http://api.twitter.com/1/statuses/public_timeline.
format (for the
statuses/public
timeline method.) We replace the
format field with the
format we like to work with (e.g. xml, atom, json.)
Now, try to copy the
method address
http://api.twitter.com/1/statuses/public_timeline.xml
and paste it into your browser. You should get something like that:
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
<created_at>Wed Apr 14 19:32:07 +0000 2010</created_at>
<id>12179999634</id>
<text>35 Ingenious Examples of Footwear - http://su.pr/1JxMAr</text>
<source>
<a href="http://apiwiki.twitter.com/" emp_href="http://apiwiki.twitter.com/" rel="nofollow">API</a>
</source>
<truncated>false</truncated>
<in_reply_to_status_id></in_reply_to_status_id>
<in_reply_to_user_id></in_reply_to_user_id>
<favorited>false</favorited>
<in_reply_to_screen_name></in_reply_to_screen_name>
<user>
<id>15736190</id>
<name>Smashing Magazine</name>
<screen_name>smashingmag</screen_name>
<location>Freiburg, Germany</location>
<description>Vitaly Friedman, editor-in-chief of SmashingMagazine.com ...</description>
<profile_image_url>http://a3.twimg.com/profile_images/572829723/...</profile_image_url>
<url>http://www.smashingmagazine.com</url>
<protected>false</protected>
<followers_count>166019</followers_count>
<profile_background_color>B2DFDA</profile_background_color>
<profile_text_color>333333</profile_text_color>
<profile_link_color>f51616</profile_link_color>
<profile_sidebar_fill_color>ffffff</profile_sidebar_fill_color>
<profile_sidebar_border_color>eeeeee</profile_sidebar_border_color>
<friends_count>394</friends_count>
<created_at>Tue Aug 05 14:00:40 +0000 2008</created_at>
<favourites_count>4</favourites_count>
<utc_offset>-10800</utc_offset>
<time_zone>Greenland</time_zone>
<profile_background_image_url>http://s.twimg.com/a/...</profile_background_image_url>
<profile_background_tile>true</profile_background_tile>
<notifications>false</notifications>
<geo_enabled>true</geo_enabled>
<verified>false</verified>
<following>false</following>
<statuses_count>8703</statuses_count>
<lang>en</lang>
<contributors_enabled>false</contributors_enabled>
</user>
<geo />
<coordinates />
<place />
<contributors />
</status>
. . .
</statuses>This method returns the 20 most recent statuses from
arbitrarily-selected non-protected Twitter users; this list is called the Public
Timeline. Because we have selected the XML format (or endpoint) we end up with
XML data.
From the data returned we can extract the structure of statuses
and users. Twitter API is good enough; it uses the same structure for all data
returned. In other words, all methods (in the REST API, remember?) use the same
structure (i.e. schema) to represent
status and
user objects.
Those structures are covered soon.
Methods with Arguments
Most of methods accept
arguments, some are optional and others are required. An example of a function
requires a single argument is the
users/show
method that returns profile information about a specific user.
This
function accepts one of three arguments:
- id:
The ID or screen name of the user.
- user_id:
The ID of the user.
- screen_name:
The screen name of the user.
We set arguments the
same way we set web pages query strings. Considering my twitter account
@elsheimy as an example, we could
query user profile using one of four ways:
All
of the four calls return the same results that should be like this:
<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>19645411</id>
<name>Mohammad Elsheimy</name>
<screen_name>Elsheimy</screen_name>
<location>KB, Egypt</location>
<description>Technology evangelist from Egypt born in 1991</description>
<profile_image_url>http://a1.twimg.com/profile_images/833061500/...</profile_image_url>
<url>http://JustLikeAMagic.WordPress.com</url>
<protected>false</protected>
<followers_count>278</followers_count>
<profile_background_color>DBE9ED</profile_background_color>
<profile_text_color>333333</profile_text_color>
<profile_link_color>CC3366</profile_link_color>
<profile_sidebar_fill_color>E6F6F9</profile_sidebar_fill_color>
<profile_sidebar_border_color>DBE9ED</profile_sidebar_border_color>
<friends_count>179</friends_count>
<created_at>Wed Jan 28 10:47:36 +0000 2009</created_at>
<favourites_count>7</favourites_count>
<utc_offset>7200</utc_offset>
<time_zone>Cairo</time_zone>
<profile_background_image_url>http://s.twimg.com/a/1271811071/...if</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<notifications></notifications>
<geo_enabled>true</geo_enabled>
<verified>false</verified>
<following></following>
<statuses_count>1877</statuses_count>
<lang>en</lang>
<contributors_enabled>false</contributors_enabled>
<status>
<created_at>Wed Apr 21 16:21:46 +0000 2010</created_at>
<id>12585240751</id>
<text>I'm reading this, it's really hot: #RFC 2616 - Hypertext Transfer Protocol ...</text>
<source>
<a href="http://bit.ly" rel="nofollow">bit.ly</a>
</source>
<truncated>false</truncated>
<in_reply_to_status_id></in_reply_to_status_id>
<in_reply_to_user_id></in_reply_to_user_id>
<favorited>false</favorited>
<in_reply_to_screen_name></in_reply_to_screen_name>
<geo/>
<coordinates/>
<place/>
<contributors/>
</status>
</user>As you can see, it returns user information and the
last update of that user. Notice that the structure of the data is the same in
all methods.
Methods Require Authentication
Some methods
require user authentication. Examples are functions update status, send direct
messages, retrieve friends' timeline, etc.
Let's take an example. Bring up
your browser and browse to the address of
statuses/friends
timeline method that returns user's friends' timeline,
http://api.twitter.com/1/statuses/friends_timeline.xml.
A small window like this should appear that asks you your Twitter username and
password.
Figure 1. IE Authentication
Provide your Twitter
username and password to pass to the results. If authentication was OK, you
should receive the most recent 20 statuses of your friend timeline in the same
structure (schema) as the Public Timeline method. On the other hand, if the
authentication process failed, you should receive an error:
<?xml version="1.0" encoding="UTF-8"?> <hash>
<request>/1/statuses/friends_timeline.xml</request>
<error>Could not authenticate you.</error>
</hash>
Let's consider another example, the
direct_messages/new
method. This function sends a new direct message to a specific user
from the current user authenticated.This function accepts two required
arguments:
- user:
The ID or screen name of the recipient user. You can use one of two
arguments in place of this argument:
- screen_name:
Screen name of the user.
- user_id:
The ID of the user.
- text:
Message body, not longer than 140 UTF-8-encoded
characters.
The following two calls send the same message ("hi, how is
it going") to the same user:
Notice
that we need to encode arguments before we pass them to the
server.
Because this function updates data, it requires HTTP POST method.
Therefore, you won't be able to try it from your browser unless your browser
allows you to set HTTP methods in the request.
If the function succeeded,
it should return the message object that has been sent, and it should be like
this:
<?xml version="1.0" encoding="UTF-8"?>
<direct_message>
<id>88619848</id>
<sender_id>1401881</sender_id>
<text>all your bases are belong to us.</text>
<recipient_id>7004322</recipient_id>
<created_at>Wed Apr 08 20:30:04 +0000 2009</created_at>
<sender_screen_name>dougw</sender_screen_name>
<recipient_screen_name>igudo</recipient_screen_name>
<sender>
<id>1401881</id>
<name>Doug Williams</name>
<screen_name>dougw</screen_name>
<location>San Francisco, CA</location>
<description>Twitter API Support. Internet, greed, users, dougw ...</description>
<profile_image_url>http://s3.amazonaws.com/twitter_production/...</profile_image_url>
<url>http://www.igudo.com</url>
<protected>false</protected>
<followers_count>1036</followers_count>
<profile_background_color>9ae4e8</profile_background_color>
<profile_text_color>000000</profile_text_color>
<profile_link_color>0000ff</profile_link_color>
<profile_sidebar_fill_color>e0ff92</profile_sidebar_fill_color>
<profile_sidebar_border_color>87bc44</profile_sidebar_border_color>
<friends_count>290</friends_count>
<created_at>Sun Mar 18 06:42:26 +0000 2007</created_at>
<favourites_count>0</favourites_count>
<utc_offset>-18000</utc_offset>
<time_zone>Eastern Time (US & Canada)</time_zone>
<profile_background_image_url>http://s3.amazonaws.com/...</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>3394</statuses_count>
<notifications>false</notifications>
<following>false</following>
<verified>true</verified>
</sender>
<recipient>
<id>7004322</id>
<name>Doug Williams</name>
<screen_name>igudo</screen_name>
<location>North Carolina</location>
<description>A character.</description>
<profile_image_url>http://s3.amazonaws.com/...</profile_image_url>
<url>http://www.igudo.com</url>
<protected>false</protected>
<followers_count>19</followers_count>
<profile_background_color>69A1AA</profile_background_color>
<profile_text_color>000000</profile_text_color>
<profile_link_color>F00</profile_link_color>
<profile_sidebar_fill_color>ACBEC1</profile_sidebar_fill_color>
<profile_sidebar_border_color>8A8F85</profile_sidebar_border_color>
<friends_count>3</friends_count>
<created_at>Thu Jun 21 21:16:21 +0000 2007</created_at>
<favourites_count>0</favourites_count>
<utc_offset>-18000</utc_offset>
<time_zone>Eastern Time (US & Canada)</time_zone>
<profile_background_image_url>http://static.twitter.com/...</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>382</statuses_count>
<notifications>false</notifications>
<following>true</following>
<verified>true</verified>
</recipient>
</direct_message>Notice that the
sender and
recipient are just
user objects. It is worth mentioning that
the
direct_messages
function return a list of
direct_message objects (like the timeline
functions that return list of
status objects.)
Twitter and .NET
Now, you have a solid
understanding of the Twitter API and how you can access it. Now, let's utilize
this API into our .NET applications.
Accessing the API
To access the API from your
.NET application you need to create a HTTP request and send it to the server and
wait for server response. Let's consider an example. The following code snippet
connects to the server and retrieves a System.Xml.XmlDocument that contains the
returned data.
public static Main()
{
GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml");
}
public static XmlDocument GetResponse(string uri)
{
WebRequest req = WebRequest.Create(new Uri(uri));
XmlDocument doc = new XmlDocument();
doc.Load(req.GetResponse().GetResponseStream());
return doc;
}We have used the
System.Xml.WebRequest class to create the request and to get the response (as an
instance of System.Xml.WebResponse class) from the server. Once we get the
XmlDocument, we can walk through the data and retrieve it.
Authentication
You can take one of two
approaches to authorize Twitter users:
- OAuth Authorization:
An
authentication protocol that allows users to approve application to act on their
behalf without sharing their password. As this function requires more overhead
and requires your application to be registered in the Twitter clients'
directory, we would better use the second approach in our examples.
- Request Authorization:
To provide authentication information in each
request you make to the server.
Considering a method like
statuses/update
method that updates the status information of the user (i.e. sends a tweet)
we would develop our previous code to be like this:
public static void Main()
{
GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API",
"elsheimy", "b@zzword", true);
}
public static XmlDocument GetResponse(string uri, string username, string password, bool post)
{
WebRequest req = WebRequest.Create(new Uri(uri));
if (post)
req.Method = "POST";
if ((username != null) && (username.Trim() != String.Empty) && (!String.IsNullOrEmpty(password)))
req.Credentials = new NetworkCredential(username.Trim(), password);
XmlDocument doc = new XmlDocument();
doc.Load(req.GetResponse().GetResponseStream());
return doc;
}Notice how we set the HTTP method based on the function
requirements. It is worth mentioning that a status should not exceed 140
UTF-8-encoded characters.
Encoding URIs
Have you noticed the previous
code? It tries to post the update "hello from the API". Because the text is
included with the URI, special handling to the text should be carried. This
special handling for text included in URIs is usually called Percent-encoding or
URL Encoding. This encoding replaces unsafe characters with their hexadecimal
values preceded by percentage (%) signs. Unsafe characters are those somewhat
conflicted with URI special characters. For example, if we would encode the text
"hello from the API" we would end up with
"hello%20from%20the%20API".
There are many unsafe characters that should
be percent-encoded including
$,
+,
&,
:,
and
=. A nice discussion of URL Encoding can be found in the article
URL
Encoding by
Brian
Wilson.
Once we get the idea, we can create our percent-encoding
class that encodes/decodes strings:
private static string[,] _chars = new string[,]
{
{ "%", "%25" }, // this is the first one
{ "$" , "%24" },
{ "&", "%26" },
{ "+", "%2B" },
{ ",", "%2C" },
{ "/", "%2F" },
{ ":", "%3A" },
{ ";", "%3B" },
{ "=", "%3D" },
{ "?", "%3F" },
{ "@", "%40" },
{ " ", "%20" },
{ "\"" , "%22" },
{ "<", "%3C" },
{ ">", "%3E" },
{ "#", "%23" },
{ "{", "%7B" },
{ "}", "%7D" },
{ "|", "%7C" },
{ "\\", "%5C" },
{ "^", "%5E" },
{ "~", "%7E" },
{ "[", "%5B" },
{ "]", "%5D" },
{ "`", "%60" } };
public static string EncodeUrl(string url)
{
for (int i = 0; i < _chars.GetUpperBound(0); i++)
url = url.Replace(_chars[i, 0], _chars[i, 1]);
return url;
}
public static string DecodeUrl(string url)
{
for (int i = 0; i < _chars.GetUpperBound(0); i++)
url = url.Replace(_chars[i, 1], _chars[i, 0]);
return url;
}
For clearness, we have included the encoded string of each
character along with the character itself. You don't have to do this. You can
convert the character to a number and just output the number in hex
format.
Now we could change the code that updates the status to the
following:
public static void Main()
{
string uri;
string text = UrlEncoder.EncodeUrl("hello from the API");
uri = "http://api.twitter.com/1/statuses/update.xml?status=" + text;
GetResponse(uri, "elsheimy", "b@zzwrd", true);
}
.NET includes a nice function that
escapes (encodes) a URI, System.Uri.EscapeUriString() function. However, this
function does not encode all unsafe characters.
Business Objects
Once you know the structure of
the XML data returned, you can create your business objects that would
encapsulate this data. The following are the three classes that would represent
our three core objects, the user, the status, and the message.
public structure TwitterUser
{
public long ID;
public string Name;
public string ScreenName;
public string Location;
public string Description;
public string ProfileImage;
public string Url;
public bool IsProtected;
public long FollowersCount;
public long FriendsCount;
public string CreatedAt;
public long FavoritesCount;
public bool Verified;
public bool Following;
public long StatusCount;
}
public structure TwitterStatus
{
public string CreatedAt;
public long ID;
public string Text;
public string Source;
public bool Truncated;
public long InReplyToStatusID;
public long InReplyToUserID;
public bool Favorited;
public string InReplyToScreenName;
public TwitterUser User;
}
public structure TwitterMessage
{
public long ID;
public long SenderID;
public long SenderScreenName;
public long RecipientID;
public long RecipientScreenName;
public string Text;
public string CreatedAt;
public TwitterUser Sender;
public TwitterUser Recipient;
}
Retrieving Data
Now you can walk through the XML
data and get that data inside your objects. The following code returns a list of
statuses from your friends' timeline.
public static void Main()
{
GetStatuses("elsheimy", "b@zzword");
}
public static List GetStatuses(string username, string password)
{
XmlNode node = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml",
username, password, true);
List lst = new List(node.ChildNodes.Count);
foreach (XmlNode nd in node.ChildNodes) // for each status
lst.Add(HandleStatus(nd));
return lst;
}
public static TwitterStatus HandleStatus(XmlNode nd)
{
// HandleNumber, FormatText, HandleBool
// are just functions that converts strings
// to numbers, decoded strings, and bool
TwitterStatus status = new TwitterStatus(
nd["created_at"].InnerText,
HandleNumber(nd["id"]),
FormatText(nd["text"]),
FormatText(nd["source"]),
HandleBool(nd["truncated"]),
HandleNumber(nd["in_reply_to_status_id"]),
HandleNumber(nd["in_reply_to_user_id"]),
HandleBool(nd["favorited"]),
FormatText(nd["in_reply_to_screen_name"]),
HandleUser(nd["user"]));
return status;
}
public static TwitterUser HandleUser(XmlNode nd)
{
// HandleNumber, FormatText, HandleBool
// are just functions that converts strings
// to numbers, decoded strings, and bool
long id = HandleNumber(nd["id"]);
TwitterUser user;
user = new TwitterUser(
id,
FormatText(nd["name"]),
FormatText(nd["screen_name"]),
FormatText(nd["location"]),
FormatText(nd["description"]),
nd["profile_image_url"].InnerText,
nd["url"].InnerText,
HandleBool(nd["protected"]),
HandleNumber(nd["followers_count"]),
HandleNumber(nd["friends_count"]),
nd["created_at"].InnerText,
HandleNumber(nd["favourites_count"]),
HandleBool(nd["verified"]),
HandleBool(nd["following"]),
HandleNumber(nd["statuses_count"]));
return user;
}
twittoo, Sample Application
twitto,
is our WinForms sample application that utilizes the Twitter API. This is just a
very simple application with basic functionalities.
Snapshots
The following are snapshots of the
application:
Figure 2 - twitto 0
Figure 3 - twitto 1
Figure 4 - twitto 2
Figure 5 - twitto 3
Figure 6 - twitto 4
Figure 7 - twitto 5
Description
This application was created using C#
and WinForms 2.0 technology; it allows the user to navigate through his friends'
timeline, mentions, direct messages, retweets, and friends, and to update his
status, reply to tweets, retweets, and to direct messages. Data is not refreshed
automatically, the user have to click the "refresh" button.
(You can create
your own routine that creates a timer that updates the data
automatically.)
Interface
As you see, the application uses just
the Windows Common Controls all around the application; no 3rd party controls
were used.
To represent a status, message, or a user, the application
overuses the System.Windows.Forms.TableLayoutPanel control to represent each
status, message, or user. It consists of four columns and two rows. The
following figure shows how the control is laid-out.
Figure 8 - TableLayoutPanel Status Template
URL Shortening
twittoo, has a very nice feature,
it allows the user to insert a shortened URL into his tweets. For this to work,
the application makes use of
http://is.gd URL shortening service. The following
is the function that utilizes the
http://is.gd API:
public static string Shorten(string url)
{
if (!System.Text.RegularExpressions.Regex.IsMatch(url, @"(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?"))
throw new FormatException("The URL you specificed is not in the current format.");
url = Uri.EscapeUriString(url);
string reqUri = String.Format(Properties.Settings.Default.Api_UrlShortner, url);
WebRequest req = WebRequest.Create(reqUri);
req.Timeout = 5000;
using (System.IO.StreamReader reader = new System.IO.StreamReader(req.GetResponse().GetResponseStream()))
{
return reader.ReadLine();
}
}
Download
Download twittoo; our sample
application