Does Order Of Twitter API Parameters Make A Difference?

Posted January 30, 2009 at 9:04 AM by Ben Nadel

Tags: ColdFusion

I've been having a hell of a time trying to clean up KinkyTwits - my ColdFusion powered Twitter client - for release. One big problem that I've been having is that the API seems to suddenly stop working. It will be chugging along fine and then all of a sudden, I get nothing but the following errors:

Twitter / Over capacity - Too many tweets! Please wait a moment and try again.

This error will keep coming back for hours. Then, completely randomly, it will work for a moment, and then stop again for hours.

Now, I know it can't be that the API is over capacity because when this starts to happen, I'll jump on TweetDeck and it's fine. I've been banging my head against the wall for many hours on this problem and I think I might have finally gotten to the bottom of the issue. I don't see this mentioned anywhere in the API documentation, but it seems as if the order of the Parameters passed in the GET request makes a difference. To see what I'm saying, take a look at this test code:

  • <!---
  • Make the first API request putting the SINCE_ID paramter
  • followed by the COUNT parameter.
  • --->
  • <cfhttp
  • method="get"
  • url="http://www.twitter.com/statuses/friends_timeline.json"
  • useragent="KinkyTwitsTwitterBot"
  • username="bennadel"
  • password="123xyz"
  • result="objTwitterRequest">
  •  
  • <cfhttpparam
  • type="url"
  • name="since_id"
  • value="1158700580"
  • />
  •  
  • <cfhttpparam
  • type="url"
  • name="count"
  • value="200"
  • />
  •  
  • </cfhttp>
  •  
  • <!--- Output response. --->
  • <cfdump
  • var="#ToString( objTwitterRequest.FileContent )#"
  • label="Request With SINCE_ID First."
  • />
  •  
  •  
  • <!---
  • Make the second API request putting the COUNT paramter
  • followed by the SINCE_ID parameter (the rest of the code
  • is exactly the same as the first API request).
  • --->
  • <cfhttp
  • method="get"
  • url="http://www.twitter.com/statuses/friends_timeline.json"
  • useragent="KinkyTwitsTwitterBot"
  • username="bennadel"
  • password="123xyz"
  • result="objTwitterRequest">
  •  
  • <cfhttpparam
  • type="url"
  • name="count"
  • value="200"
  • />
  •  
  • <cfhttpparam
  • type="url"
  • name="since_id"
  • value="1158700580"
  • />
  •  
  • </cfhttp>
  •  
  • <!--- Output response. --->
  • <cfdump
  • var="#ToString( objTwitterRequest.FileContent )#"
  • label="Request With COUNT First."
  • />

Notice that in the first request, I am sending the SINCE_ID followed by the COUNT parameter. This is the same order as it is listed in the API documentation (although no mention of order specification could be found). In the second request, I am then just switching the order of the parameters. Now, when I run this, very consistently, the first request returns JSON-formatted statuses and the second request returns the "Too many tweets! Please wait a moment and try again" error.

If you are thinking that this is happening because I am following one API request with another immediately after it, don't worry - I thought of that. If I run the code above, with the CFHttp tags reversed, I get the same, consistent outcome but in the reverse order (first request fails, second one succeeds).

Could it be that the order of the URL parameters in the Twitter API requests actually makes a difference? Is that possible? I can't see how that could be true in any sort of name-keyed system. Has anyone else had any experience with this?

So, why does this work sometimes for a while and then start failing all of a sudden? I assume it's because I am passing my Parameters collection through to my Twitter API execution method as a ColdFusion struct. I am then looping over that struct and creating CFHttpParam tags:

  • <!--- Make URL parameters for GET method. --->
  • <cfloop
  • item="LOCAL.Key"
  • collection="#ARGUMENTS.Parameters#">
  •  
  • <cfhttpparam
  • type="url"
  • name="#LCase( LOCAL.Key )#"
  • value="#ARGUMENTS.Parameters[ LOCAL.Key ]#"
  • />
  •  
  • </cfloop>

Because ColdFusion has no inherent order for structs, the order of the CFHttpParams is generated randomly. Of course, from CFDump experience, we know that this order is not totally random - it does get stuck in a rut (or perhaps comes out alphabetical). Either way, if we need ordering to be specific, we simply cannot rely on ColdFusion structs. Looks like I'm gonna have to go back and start passing my parameters through using an array of name-value pairs.




Reader Comments

Jan 30, 2009 at 9:14 AM // reply »
2 Comments

Could it be the API Limit being reached you can only make 100 requets in one hour? Not sure but that could be the reason?


Jan 30, 2009 at 9:17 AM // reply »
10,640 Comments

@BigMadKev,

I thought that, but if I reverse the order of the CFHTTP tests above, they fail in reverse order. If it was an API request limit, they would both have to fail on repeated requests. But, the once with "since_id" first always works, regardless of test placement.


Jan 30, 2009 at 9:18 AM // reply »
10,640 Comments

@BigMadKev,

Plus and API limit issue would return with a 400 status code - these are returning with a 200 OK status code.


Jan 30, 2009 at 9:24 AM // reply »
2 Comments

In that case then it's time to blame the technology behind twitter ;)

As it's not the developers fault is it :)

Same as MySpace.com was the Technologies fault not the devleopers.

:p hehe


Jan 30, 2009 at 9:34 AM // reply »
10,640 Comments

@BigMadKev,

I just switched from using Structs to using Arrays for my Parameters collection and its working :) Crazy stuff!


Jan 30, 2009 at 11:11 AM // reply »
25 Comments

The only thing I can see is that they're using index-based arguments, rather than the actual argument key.

ie: count = arguments[ 1 ].

Then again, if that's whay they're doing the order would impact the actual values assigned.


Jan 30, 2009 at 11:54 AM // reply »
42 Comments

Ben,

I am using the twitter api cfc from Ria Forge and I just looked at the internals and here is what I found-

<cfhttp
url="http://twitter.com/#arguments.resource#.#returnFormat#?#queryString#"
method="#arguments.method#"
username="#variables.username#"
password="#variables.password#"
useragent="ColdFusion/8.0">

<cfif arguments.method eq "post">
<cfloop collection="#arguments.params#" item="key">
<!--- Deal with nulls so passing the arguments scope works properly. --->
<cfif structKeyExists(arguments.params,key)>
<cfhttpparam name="#lCase(key)#" value="#arguments.params[key]#" type="formfield">
</cfif>
</cfloop>
</cfif>
</cfhttp>

That appears to be doing about the same thing that you are doing. I haven't had any problems whatsoever.


Jan 30, 2009 at 11:58 AM // reply »
10,640 Comments

@Brandon,

I have no idea :) Like I said, mine was working for a long time and then stopped. Maybe ColdFusion started sorting the params differently? Now that I have switched to an array, its all good. Depending on what params you have, CF might just be ordering them correctly.


Jan 30, 2009 at 12:03 PM // reply »
48 Comments

I'm throwing a hail mary here, but could it be that they're using some sort of hash for the requests you are sending as a key to throttle you and the order of the parameters results in a different hash?


Jan 30, 2009 at 12:11 PM // reply »
10,640 Comments

@Todd,

I am not sure what you mean.


Jan 30, 2009 at 1:10 PM // reply »
48 Comments

Check this:

<cfset a = createObject("java", "java.util.HashMap") />
<cfset a.put("keyOne", "valOne") />
<cfset a.put("keyTwo", "valTwo") />

<cfset b = createObject("java", "java.util.HashMap") />
<cfset a.put("keyTwo", "valTwo") />
<cfset a.put("keyOne", "valOne") />

<cfset c = createObject("java", "java.util.HashMap") />
<cfset c.put("keyOne", "valOne") />
<cfset c.put("keyTwo", "valTwo") />

<cfdump var="#a#">
<cfdump var="#b#">

A = B?<br />
<cfdump var="#a.equals(b)#">
<br />
A = C?<br />
<cfdump var="#a.equals(c)#">


Jan 30, 2009 at 1:15 PM // reply »
48 Comments

DAMN! I got all excited thinking that was correct (that A != B but A=C), until I saw my copy paste error. Here is the correct code:

<cfset a = createObject("java", "java.util.HashMap") />
<cfset a.put("keyOne", "valOne") />
<cfset a.put("keyTwo", "valTwo") />

<cfset b = createObject("java", "java.util.HashMap") />
<cfset b.put("keyTwo", "valTwo") />
<cfset b.put("keyOne", "valOne") />

<cfset c = createObject("java", "java.util.HashMap") />
<cfset c.put("keyOne", "valOne") />
<cfset c.put("keyTwo", "valTwo") />

<cfdump var="#a.hashCode()#"><br />
<cfdump var="#b.hashCode()#"><br />
<cfdump var="#c.hashCode()#"><br />

A = B?<br />
<cfdump var="#a.equals(b)#">
<br />
A = C?<br />
<cfdump var="#a.equals(c)#">

A == B == C

So, at least in Java the order of the keys in a map does not result in a different hashcode for a hashmap. But do you see where I was going with it?


Jan 30, 2009 at 1:22 PM // reply »
10,640 Comments

@Todd,

I am not sure I follow?? We are not passing the hash map to Twitter. The Struct is merely used as a collection over which we iterate to write out the CFHttpParam tags. So, collection-aside, the values are passed individually.


Jan 30, 2009 at 1:37 PM // reply »
48 Comments

Right, but, my skewed thinking was that somehow on Twitter's side of the API they are taking the arguments that you are passing in and creating a hash of that argument structure as a key to identify your requests. My thought was that they use that key to throttle you if you send too many requests within a specified time period. If the order in which you passed the args (or, the order in which they received the args) resulted in a different hash code then theoretically your requests would appear to be from a different source if you passed them in a different order.

Crazy theory - just disregard me ;)


Jan 30, 2009 at 1:40 PM // reply »
10,640 Comments

@Todd,

Thanks for getting creative with your thinking! :)


Feb 1, 2009 at 11:51 PM // reply »
10 Comments

Ben ...

Have you increased your limits with twitter?

http://twitter.com/help/request_whitelisting



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 12, 2012 at 3:37 AM
Learning ColdFusion 8: CFImage Part III - Watermarks And Transparency
Hi Ben, Just to ask currently it is placed bottom right corner, if i need to replace the same rendered image on the bottom left side or in the bottom center, how that can be calculated. bottom ce ... read »
Feb 11, 2012 at 9:29 PM
Use jQuery's SlideDown() With Fixed-Width Elements To Prevent Jumping
I can't say how glad I am that I found your post. Thank you very much. ... read »
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »