Getting Flights info from the Kayak API in ASP.Net

Kayak is one of the top travel meta-search engines on the web, and they have a free API which allows you to get flight and hotel information.

To use the Kayak API, first get a developer key

Then you can read the full specifications to get the info. Or, just use the code below :)

This gets the flights given an Origin, Destination, Departure date and the number of travellers (note: Origin & Destination must be in the form of a 3 letter Airport code, eg: MEL = Melbourne, SYD = Sydney, LAX = Los Angeles).

You can also Download the full source code

        Private Function Flights(ByVal dtDepartDate As Date, _
                ByVal sOriginCode As String, _
                ByVal sDestinationCode As String, _
                ByVal iTravellers As Integer) As DataTable

                Dim sResult As String
                Dim oCookies As New CookieContainer
                Dim sURL As String
                Dim oDoc As New System.Xml.XmlDocument
                Dim sSID As String
                Dim sSearchID As String
                Dim bMorePending As Boolean = True
                Dim oTripList As System.Xml.XmlNodeList
                Dim oTrip As System.Xml.XmlNode
                Dim oFlights As DataTable
                Dim oFlight As DataRow
                Dim iFlightsFound As Integer
                Dim sDeveloperKey As String = "***Your Key Here****"

                'Full specs at:

                'Get the SessionID
                sURL = "" & sDeveloperKey
                sResult = sGetData(sURL, oCookies)
                sSID = oDoc("ident")("sid").InnerText

                'Start the Flight Search & get the SearchID
                sURL = "" & _
                "basicmode=true&oneway=y" & _
                "&origin=" & sOriginCode & _
                "&destination=" & sDestinationCode & _
                "&depart_date=" & Format(dtDepartDate, "MM/dd/yyyy") & _
                "&depart_time=a&" & _
                "return_date=" & _
                "&return_time=a" & _
                "&travelers=" & iTravellers & _
                "&cabin=b&action=doflights&apimode=1&_sid_=" & sSID
                sResult = sGetData(sURL, oCookies)
                sSearchID = oDoc("search")("searchid").InnerText

                oFlights = dtFlightsTableInit()

                Do While bMorePending = True
                        'Get the first lot of results
                        sURL = "" & _
                        "searchid=" & sSearchID & _
                        "&c=10&apimode=1&_sid_=" & sSID
                        sResult = sGetData(sURL, oCookies)
                        bMorePending = IIf(oDoc("searchresult")("morepending").InnerText = "true", True, False)
                        If bMorePending = True Then
                                'Wait for 5 seconds, then poll again
                                System.Threading.Thread.Sleep(5 * 1000)
                        End If
                iFlightsFound = Val(oDoc("searchresult")("count").InnerText)

                'Build the final request, asking for the total number of flights found by
                sURL = "" & _
                "searchid=" & sSearchID & _
                "&c=" & iFlightsFound & _
                "&apimode=1&_sid_=" & sSID
                sResult = sGetData(sURL, oCookies)

                oTripList = oDoc.SelectNodes("//trip")
                For Each oTrip In oTripList
                        oFlight = oFlights.NewRow()
                        oFlight("DepartDate") = oTrip("legs")("leg")("depart").InnerText
                        oFlight("ArriveDate") = oTrip("legs")("leg")("arrive").InnerText
                        oFlight("Airline") = oTrip("legs")("leg")("airline_display").InnerText
                        oFlight("Price") = oTrip("price").InnerText
                        oFlight("BookURL") = oTrip("price").Attributes("url").Value

                Flights = oFlights
        End Function

        Public Function dtFlightsTableInit() As DataTable
                Dim dtFlights As New DataTable

                dtFlights.Columns.Add(New DataColumn("DepartDate", GetType(String)))
                dtFlights.Columns.Add(New DataColumn("ArriveDate", GetType(String)))
                dtFlights.Columns.Add(New DataColumn("Airline", GetType(String)))
                dtFlights.Columns.Add(New DataColumn("Price", GetType(String)))
                dtFlights.Columns.Add(New DataColumn("BookURL", GetType(String)))

                dtFlightsTableInit = dtFlights
        End Function

        Public Function sGetData(ByVal sURL As String, _
Optional ByRef oCookies As CookieContainer = Nothing) As String

                Dim Writer As StreamWriter = Nothing
                Dim WebRequestObject As HttpWebRequest
                Dim sr As StreamReader
                Dim WebResponseObject As HttpWebResponse
                Dim sbResultsBuilder As New StringBuilder
                Dim sBuffer(8192) As Char
                Dim iRetChars As Integer

                WebRequestObject = CType(WebRequest.Create(sURL), HttpWebRequest)
                WebRequestObject.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"
                WebRequestObject.Method = "GET"
                WebRequestObject.Timeout = 55000
                WebRequestObject.ReadWriteTimeout = 55000
                WebRequestObject.AllowAutoRedirect = True

                If Not (oCookies Is Nothing) Then
                        WebRequestObject.CookieContainer = oCookies
                End If

                WebResponseObject = CType(WebRequestObject.GetResponse(), HttpWebResponse)

                sr = New StreamReader(WebResponseObject.GetResponseStream)

                        iRetChars = sr.Read(sBuffer, 0, sBuffer.Length)
                        If iRetChars > 0 Then
                                sbResultsBuilder.Append(sBuffer, 0, iRetChars)
                        End If
                Loop While iRetChars > 0
                sGetData = sbResultsBuilder.ToString

        End Function

The Kayak API is pretty good, but does have some limitations, so I think is works best when you use it for stats rather than bookings. Anyone else know of some good travel APIs?

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. SEOMoz Linkscape API wrapper in VB.Net
  2. Create a Website Comparison Widget using GoogleTrends
  3. Using to Generate Truly Random Numbers in .Net
  4. Allowing special characters (forward slash, hash, asterisk etc) in ASP.Net MVC URL parameters

See how your website looks in over 60 browsers - in minutes

You’ve just updated your site. The changes look great - both in IE and Firefox - so you release it. And all is good.

Until a few days later, when the emails start to pile up. It doesn’t work in IE6. Problem is, you don’t have IE6 installed anymore, and don’t have access to a machine that does. Enter Browsershots.

Browsershots will give you a screen shot of your website (or specific page) in over 60 browsers. It does this by farming out the work to various ‘factories’ (people that are running the browsers on their machines. Just submit your URL & choose the browsers you want, wait a few minutes in the queue, then view the results.

Here are some results for (the thumbnails are small, but you get bigger pics at browsershots).
Screenshots of

This is so useful, it just blows me away. Oh, and it’s free. Anyone know of any other sites like this?

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. How to write a FaceBook Application in 10 minutes
  2. Add Google Maps to your .Net site in 10 minutes

Quick SQL Server Tuning Tip

Tuning SQL Server can be a real pain - everyone knows the basics; return the minimum rows, use indexes etc - but sometimes you need to get some hard stats.

Before we begin, I am going to assume you are running in Query Analyzer, or some tool that allows you to change the connection parameters.



This gives you useful info on how many pages were read in your query (both from the cache and from disk)   and how long it takes.

Eg, after running a query, here are the stats:

Table 'DG_UserProperties'. Scan count 0, logical reads 2154, physical reads 10, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘DG_Users’. Scan count 1, logical reads 1083, physical reads 27, read-ahead reads 1, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
CPU time = 16 ms,   elapsed time = 213 ms.

It is important to reduce the ‘reads’ and the CPU time. Elapsed time can vary depending on server load, so isn’t as important when comparing queries.

After each set of statistics, clear the cache otherwise the next query will use it & give an incorrect comparison.


This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. Convert MySQL to MS Sql Server

How to access HttpServerUtility.MapPath in a Thread

Usually, when you want the root directory of your website, it is easy to call:


or just


from a page.

But if you are running your code in a thread or timer, then HttpContext.Current is null (giving you a NullReferenceException), and you are out of luck. :(

However, as long as you are not using virtual directories, there is an easy fix:


will give you the same result, and doesn’t depend on a current HTTP context.

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. We Got Domain - over 10,000 aged domains for sale
  2. See how your website looks in over 60 browsers - in minutes

Remove Name Mangling from ASP.Net Master Pages (get your ID back!)

If you have been programming using ASP.Net Master Pages and javascript then you have no doubt come across the problem where you try to access an element in javascript using:

var sValue = document.getElementById(’TextBox1′).value;

And you keep getting an error that the element cannot be found. If you check the HTML source for the page, you see that has “helpfully” added something like ‘ctl00_ContentPlaceHolder1_’ to the front of the control ID, so you have to use:

var sValue = document.getElementById(’ctl00_ContentPlaceHolder1_TextBox1′).value;

I’m not going to try & sugar coat it - this totally sucks.

The solution that I have been using overwrites the .getElementById method, allowing you to search for the element just using the normal ID.

<script type='text/javascript'>
document.newGetElementById = document.getElementById;
document.getElementById = function(sElementID)
var oFirstTry;

oFirstTry = document.newGetElementById(sElementID);
if (oFirstTry)
	return oFirstTry;
	return document.newGetElementById('ctl00_ContentPlaceHolder1_' + sElementID);

Thanks to Scooby Doo for pointing out the earlier error, it has now been corrected

Add this code to the top of your page or .js file, and then you can go back to using:

var sValue = document.getElementById(’TextBox1′).value;

without caring if it is in a content page or not.

Note: The above code won’t work if you change the name of your ContentPlaceHolder control from the standard ‘ContentPlaceHolder1′. If you do like to have unique names then change

return document.getElementById(’ctl00_ContentPlaceHolder1_’ + sElementID);


return document.newGetElementById(’<%=me.Form.Controls(1).ClientID %><%=Me.ClientIDSeparator %>’ + sElementID);

The code must now always be placed in your page (not an external .js file). However, calls to .getElementById can still be in external .js files.

Finally: A common solution to the problem is to use the ClientID property:

var sValue = document.getElementById(’<% =TextBox1.ClientID %>’).value;

but this method requires that all calls to getElementById be in the page, which is not as flexible as the solution given.

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. Remove HTML tags and Javascript from pages

Add GeoLocation to your site in less time than it takes to Drink a Beer

This is what a regular programmer looks like
Adding Geolocation to your website is so easy these days that I’ve decided that not only can we do it within 5 minutes, but to make it interesting I’ll bet that you’ve finished it before you can finish a beer.

OK? Got your beer? Lets go.

  1. Crack open the beer and start a new project. Call it ‘GEOTest’. Take a well deserved drink.
  2. Download the MaxMind Geo database and wrapper code from MaxMind (Complete list of supported languages - thanks again to MaxMind for doing all the work). Have some more beer while it downloads (thanks to my spankinglyfast ADSL2+ connection, no beer for me).
  3. Open the zip file into your project directory. Another sip. Add the files   ‘testCountryLookUp.aspx’, ‘testCountryLookUp.aspx.vb’ and ‘CountryLookup.vb’ to your project.
  4. Thirsty? More beer.
  5. Set the page ‘testCountryLookUp.aspx’ as your startup page, and then run the project. At this point it should run, but you may get a ‘Parser Error’ in the ‘testCountryLookUp.aspx’ file. To fix it, change:
    in the   ‘testCountryLookUp.aspx’ file.
  6. Thats it! Done! You’ve probably still got plenty of beer left, so lets make some of our own code to get the country from IP Address.

Function CountryFromIP(ByVal Address As String) As String
     Dim oCountryLookup As CountryLookup = New CountryLookup(Server.MapPath("data/GeoIP.dat"))

     CountryFromIP = oCountryLookup.LookupCountryName(Address)
End Function

To call it, just use:
CountryName = CountryFromIP(Request.UserHostAddress)
Doesn’t work? Maybe too much beer…Passing your UserHostAddress won’t work from your local machine. To test, grab your IP address from somewhere like

Still doesn’t work? Definitely time for another beer…

This post brought to you by - Over 10,000 Aged domains for sale!

No related posts.

Opening a new browser window with POST data

Every now and again I want to have a link that opens a new browser window, and passes some values in. It’s easy when you just need to pass in GET data:

But it becomes harder when you want to pass in POST values.

Maybe not that hard…How about opening the new window, adding the POST variables to the form, and then posting it to the relevant domain. That would work :)

This code gives you a button which will open a new window and automatically log in to your WordPress blog.


        <script type='text/javascript'>
        function LoginToWordPressBlog()
        var sData;
        var sUserName = "YourUserName";
        var sPassword = "YourPassword";
        var sDomain = "";

        sData = "<form name='loginform' id='loginform' action='" + sDomain + "/wp-login.php' method='post'>";
        sData = sData + "<input type='text' name='log' id='user_login' class='input' value='" + sUserName + "' />";
        sData = sData + "<input type='password' name='pwd' id='user_pass' class='input' value='" + sPassword + "' />";
        sData = sData + "<input type='submit' name='wp-submit' id='wp-submit' value='Login »' />";
        sData = sData + "<input type='hidden' name='redirect_to' value='/wp-admin/' />";
        sData = sData + "<input type='hidden' id='wordpress_test_cookie' name='wordpress_test_cookie' value='WP Cookie check' />";
        sData = sData + "</form>";
        sData = sData + "<script type='text/javascript'>";
        sData = sData + "document.cookie='wordpress_test_cookie=home; expires=Fri, 11 Jul 2009 05:23:14 +0000; path=/';";
        sData = sData + "document.loginform.submit();</sc" + "ript>";"", "newwin");
        <form id="form1">
                 <input id="Button1" type="button" value="Login To Wordpress" onclick='LoginToWordPressBlog()' />

The method above is in javascript, so it should work for pretty much everybody. The steps are:

  1. Create a new window
  2. Use document.write to write the HTML POST data tags to the new window.
  3. Submit the new window.

The only tough part now is finding out which POST tags you need - and the best tool for that is Fiddler. It even shows you the cookie values that you need.

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. MySpace API
  2. Copying cookies across domains in ASP.Net
  3. MySpace API - Automate your Blog posts
  4. Hiding the Digg button from Diggers
  5. Easy Cross Domain Cookies (Sharing cookies between domains)

Using to Generate Truly Random Numbers in .Net

Random numbers can be a pain if you have to generate new sequences each day. The normal ‘randomize()’ statement is seeded from the system timer so if you are running the generator at the same time, then there is a chance you will get the same sequence.

Over at, Mads Haahr has been generating random numbers using atmospheric noise since 1998, and his method has the pseudo-random number generators build into VB.Net beat.

Here is the VB.Net wrapper to call and get your numbers:

Imports System.Net

Public Class clsRandom
        Public Sub RandomizeReseed()
                Dim iSeed(0) As Integer

                iSeed = RandomIntegers(-1000000000, 1000000000, 1)
        End Sub

        Public Function RandomIntegers(ByVal MinValue As Integer, _
                ByVal MaxValue As Integer, _
                ByVal NumberCount As Integer) As Integer()

                Dim iC As Integer
                Dim iRetInts As Integer()
                Dim sRetIntsAsStr As String()
                Dim sURL As String

                If Math.Abs(MinValue) > 1000000000 Then
                        Dim ex As New Exception("MinValue cannot be greater than 1,000,000,000 or less than -1,000,000,000")
                        Throw ex
                End If

                If Math.Abs(MaxValue) > 1000000000 Then
                        Dim ex As New Exception("MaxValue cannot be greater than 1,000,000,000 or less than -1,000,000,000")
                        Throw ex
                End If

                If MinValue > MaxValue Then
                        Dim ex As New Exception("MinValue cannot be greater than MaxValue")
                        Throw ex
                End If

                If NumberCount > 10000 Then
                        Dim ex As New Exception("NumberCount cannot be greater than 10,000")
                        Throw ex
                End If

                sURL = ""
                sURL = sURL & "num=" & NumberCount
                sURL = sURL & "&min=" & MinValue
                sURL = sURL & "&max=" & MaxValue
                sURL = sURL & "&col=1&base=10&format=plain&rnd=new"

                sRetIntsAsStr = CallRandomDotOrg(sURL)
                ReDim iRetInts(sRetIntsAsStr.GetUpperBound(0))
                For iC = 0 To sRetIntsAsStr.GetUpperBound(0)
                        iRetInts(iC) = sRetIntsAsStr(iC)

                RandomIntegers = iRetInts
        End Function

        Private Function CallRandomDotOrg(ByVal sGetURL As String) As String()
                Dim objWebClient As New WebClient()

                Dim sResponse As String
                Dim sReturnInts() As String

                sResponse = objWebClient.DownloadString(sGetURL)
                If Right$(sResponse, 1) = vbLf Then
                        sResponse = Left$(sResponse, Len(sResponse) - 1)
                End If

                sReturnInts = sResponse.Split(vbLf)

                CallRandomDotOrg = sReturnInts
        End Function
End Class

Using the class is pretty simple. You can get an array of integers using the function:
RandomIntegers(MinValue, MaxValue, Count)

or you can seed the built-in random number generator with a truly random number by calling

This post brought to you by - Over 10,000 Aged domains for sale!

Related posts:

  1. SEOMoz Linkscape API wrapper in VB.Net
  2. Getting Flights info from the Kayak API in ASP.Net
  3. Create a Website Comparison Widget using GoogleTrends

We Got Domain - over 10,000 aged domains for sale

We Got Domain is a quick site that I built to make buying domains from forums like dnforum, digitalpoint etc easier. It extracts domain names from the sale threads and then finds out extra info like Backlinks, Pagerank, Creation date etc. Domains can only be bought on the forums so hopefully We Got Domain will save time for people buying domains while driving extra traffic to the forums (and they get tons of do-follow links as a small bonus).

The site was partly inspired by a the post Purchasing Domains on as well as Shoemoney Marketplace and

I hope you find the site useful - send me an email if you have any suggestions or improvements.

Your own Lolcats widget (I Can Has Cheezburger?)

OK, I’m the first to admit that the whole lolcats thing might not be for everyone. But it’s for me. If it’s for you, too, then enjoy :)
If you would like this widget on your site/blog, just add the code:
<script src='' type='text/javascript'></script>
And here’s how it was done:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim sCacheKey As String = "CACHE_ICanHasCheezburger"
        Dim oLolCat As clsLolCat
        Dim sHTML As String        

        oLolCat = CType(Cache(sCacheKey), clsLolCat)        

        If oLolCat Is Nothing Then
            oLolCat = PicAndCaptionGet()
            Cache.Insert(sCacheKey, oLolCat, Nothing, Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration)
        End If        

        sHTML = "<div><a href='" & oLolCat.sLinkURL & "'><img border='0' src='" & oLolCat.sPicURL & "'/></a>"
        sHTML = sHTML & "<div>More at LolCats at <a href=''>I Can Has Cheezburger</a></div></div>"        

        sHTML = "document.write(""" & sHTML & """);"

    End Sub

This first part just checks to see if we have the LolCat HTML in the cache. If we don’t then generate the HTML and put it in the cache again. Saves us hitting the site all the time (or in this case, feedburner).

    Private Function PicAndCaptionGet() As clsLolCat
        Dim Http As HttpWebRequest
        Dim sRequestURL As String
        Dim WebResponse As HttpWebResponse
        Dim responseStream As Stream
        Dim Reader As StreamReader
        Dim sText As String
        Dim oRSSDoc As New System.Xml.XmlDocument
        Dim oFirstNode As System.Xml.XmlNode
        Dim oLolCat As New clsLolCat        

        sRequestURL = ""
        Http = CType(WebRequest.Create(sRequestURL), HttpWebRequest)
        Http.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate")
        WebResponse = CType(Http.GetResponse(), HttpWebResponse)
        responseStream = WebResponse.GetResponseStream        

        If (WebResponse.ContentEncoding.ToLower().Contains("gzip")) Then
            responseStream = New GZipStream(responseStream, CompressionMode.Decompress)
        ElseIf (WebResponse.ContentEncoding.ToLower().Contains("deflate")) Then
            responseStream = New DeflateStream(responseStream, CompressionMode.Decompress)
        End If        

        Reader = New StreamReader(responseStream, Encoding.Default)
        sText = Reader.ReadToEnd()

        oFirstNode = oRSSDoc.SelectSingleNode("//item")
        oLolCat.sCaption = oFirstNode("title").InnerText
        oLolCat.sLinkURL = oFirstNode("link").InnerText
        oLolCat.sPicURL = oFirstNode("media:content").Attributes("url").Value        

        PicAndCaptionGet = oLolCat
    End Function        

End Class        

Public Class clsLolCat
    Public sPicURL As String
    Public sCaption As String
    Public sLinkURL As String
End Class

This is the code that visits the icanhascheezburger RSS feed and extracts the latest post details. This is all pretty simple stuff, with one cool bit that takes care of the gzip compression that is on the feed. Full credit must go to Rick Strahl for his post talking about gzip: