5ab5804d07f6cf6a41aab7f81446292b7b7d0b70
[whoisi.git] / whoisi / templates / api-top-doc.mak
1 # -*- coding: utf-8 -*-
2
3 ## Copyright (c) 2007-2008 Christopher Blizzard <blizzard@0xdeadbeef.com>
4 ##
5 ## Permission is hereby granted, free of charge, to any person
6 ## obtaining a copy of this software and associated documentation files
7 ## (the "Software"), to deal in the Software without restriction,
8 ## including without limitation the rights to use, copy, modify, merge,
9 ## publish, distribute, sublicense, and/or sell copies of the Software,
10 ## and to permit persons to whom the Software is furnished to do so,
11 ## subject to the following conditions:
12 ##
13 ## The above copyright notice and this permission notice shall be
14 ## included in all copies or substantial portions of the Software.
15 ##
16 ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 ## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 ## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 ## SOFTWARE.
24
25 <%def name="get_page_title()">whoisi.com: APIs</%def>
26
27 <%inherit file="master.mak"/>
28
29 <%include file="search-widget.mak"/>
30
31 <br/>
32
33 <h1>API Documentation</h1>
34
35 <hr width="60%" align="left">
36
37 <ul>
38 <li><a href="#overview">Overview</a></li>
39   <ul>
40     <li><a href="#somestuff">Some stuff that applies to all API calls</a></li>
41     <li><a href="#sitetypes">Site Types</a></li>
42   </ul>
43 <li><a href="#httpmethods">HTTP Methods</a></li>
44   <ul>
45     <li><a href="#getMaxPersonID">getMaxPersonID</a></li>
46     <li><a href="#getPeople">getPeople</a></li>
47     <li><a href="#getPerson">getPerson</a></li>
48     <li><a href="#getPersonForURL">getPersonForURL</a></li>
49     <li><a href="#getURLForTinyLink">getURLForTinyLink</a></li>
50   </ul>
51 <li><a href="#samplescripts">Sample Scripts</a></li>
52   <ul>
53     <li><a href="#dumpentiredatabase">Dump the Entire Person Database</a></li>
54     <li><a href="#dumpsingleperson">Dump a Single Person</a></li>
55     <li><a href="#personforurl">Person for URL</a></li>
56     <li><a href="#tinyurllookup">Info for TinyURL</a></li>
57   </ul>
58 </ul>
59
60 <hr width="60%" align="left">
61
62 <div style="width: 60%">
63
64 <a name="overview"><h2>Overview</h2>
65
66 <p>
67 This document describes the APIs that whoisi offers to the public.
68 They are subject to change, of course, but I'll try to keep the
69 changes to a minimum.
70 </p>
71
72 <a name="somestuff"><h3>Some stuff that applies to all API calls:</h3>
73
74 <ul>
75 <li>All of the methods start with the same URL: <code>http://whoisi.com/api/&lt;method&gt;</code>
76 <li>All methods use standard query arguments of name=value pairs.
77 <li>You can use a GET or POST request, either will work.
78 <li>All APIs return JSON data.
79 </ul>
80
81 <a name="sitetypes"><h3>Site Types</h3>
82
83 <p>
84
85 Many of the APIs reference site types.  The current set of distinct
86 sites in the database are:
87
88 <ul>
89 <li>flickr
90 <li>picasa
91 <li>twitter
92 <li>identica
93 <li>delicious
94 <li>linkedin
95 <li>feed
96 </ul>
97
98 The "feed" site type is a catch-all for everything that doesn't have a
99 specific type.  This list will expand in the future to include other
100 types so be sure to handle that in your code.
101
102 </p>
103
104 <hr width="60%" align="left">
105
106 <a name="httpmethods"><h2>HTTP Methods</h2>
107
108 <a name="getMaxPersonID"><h3><a href="#getMaxPersonID">getMaxPersonID</a></h3>
109
110 <p>
111
112 <code>getMaxPersonID</code> returns the ID of the highest-number
113 person in the database.  This number increases as people are added to
114 the database.  This is useful for people who want to pull the person
115 database periodically and want to know if new people have been added.
116
117 </p>
118
119 <h4>Required Arguments</h4>
120
121 <p>
122
123 <b><code>app</code></b>: Name of the app making the request.
124
125 </p>
126
127 <h4>Return Value</h4>
128
129 <p>
130
131 This function will return a JSON object with a dictionary with a
132 single key <code>'person_id'</code> with a single integer as the
133 value.  Please see the example.
134
135 </p>
136
137 <h4>Example</h4>
138
139 <p>
140
141 <code><a href="http://whoisi.com/api/getMaxPersonID?app=example">http://whoisi.com/api/getMaxPersonID?app=example</a></code>
142
143 </p>
144
145 <p>
146
147 Returns this value:
148
149 </p>
150
151 <p>
152
153 <code>
154 <pre>
155 {"person_id": 4070}
156 </pre>
157 </code>
158
159 </p>
160
161 <hr width="60%" align="left">
162
163 <a name="getPeople"><h3><a href="#getPeople">getPeople</a></h3>
164
165 <p>
166
167 <code>getPeople</code> will return up to 100 people from the database
168 based on a range given by the API call.  Each person comes with a
169 bunch of information about the sites that they have associated with
170 them as well.  You can make repeated calls to this API to retreive the
171 entire database of people that are listed on whoisi.
172
173 </p>
174
175 <p>
176
177 If you make a request that has larger than 100 people then you will
178 get a rather uninformative 500 error.
179
180 </p>
181
182 <h4>Required Arguments</h4>
183
184 <p>
185 <b><code>app</code></b>: Name of the app making the request.<br/>
186 <b><code>first</code></b>: First ID of the person you want in this request.<br/>
187 <b><code>last</code></b>: Last ID of the person you want in this request.<br/>
188 </p>
189
190 <h4>Return Value</h4>
191
192 <p>
193
194 This function will return a JSON dictionary with a single value:
195 <code>'people'</code>.  This people item is a a dictionary of values
196 indexed on the ID of the person.  Please see the example for the
197 layout of the data.
198
199 </p>
200
201 <h4>Example</h4>
202
203 <p>
204
205 <code><a
206 href="http://whoisi.com/api/getPeople?app=example&first=4&last=5">http://whoisi.com/api/getPeople?app=example&first=4&last=5</a></code>
207
208 </p>
209
210 <code>
211 <pre>
212 {"people":
213     {"4":
214         {"aliases":
215             ["@moz08", "johnath"],
216          "sites":
217             {"7195":
218                 {"feed": null,
219                  "url": "http://www.linkedin.com/in/johnath",
220                  "type": "linkedin",
221                  "title": null},
222              "170":
223                 {"feed": "http://twitter.com/statuses/user_timeline/6140482.atom",
224                  "url": "http://twitter.com/johnath",
225                  "type": "twitter",
226                  "title": "Twitter / johnath"},
227              "3":
228                 {"feed": "http://blog.johnath.com/index.php/feed/atom/",
229                  "url": "http://blog.johnath.com",
230                  "type": "feed",
231                  "title": "meandering wildly"},
232              "7196":
233                 {"feed": "http://hewitt.controlezvous.ca/johnath/rss",
234                  "url": "http://identi.ca/johnath",
235                  "type": "identica",    
236                  "title": "johnath"},
237              "13":
238                 {"feed": "http://api.flickr.com/services/feeds/photos_public.gne?id=23586883@N00&lang=en-us&format=atom",
239                  "url": "http://www.flickr.com/photos/johnath/",
240                  "type": "flickr",
241                   "title": "Uploads from Johnath"}},
242          "name": "Johnathan Nightingale"},
243      "5":
244         {"aliases":
245             ["@moz08", "linuxnet:shaver", "mozilla:shaver"],
246          "sites":
247             {"564":
248                 {"feed": null,
249                  "url": "http://www.linkedin.com/pub/0/252/5a2",
250                  "type": "linkedin", "title": null},
251              "643":
252                 {"feed": "http://twitter.com/statuses/user_timeline/2319611.atom",
253                  "url": "http://twitter.com/shaver",
254                  "type": "twitter",
255                  "title": "Twitter / shaver"},
256              "4":
257                 {"feed": "http://shaver.off.net/diary/feed/atom/",
258                  "url": "http://shaver.off.net/diary",
259                  "type": "feed",
260                  "title": "shaver"},
261              "61":
262                 {"feed": "http://api.flickr.com/services/feeds/photos_public.gne?id=99971095@N00&lang=en-us&format=atom",
263                  "url": "http://www.flickr.com/photos/shvmoz/",
264                  "type": "flickr",
265                  "title": "Uploads from shvmoz"}},
266          "name": "Mike Shaver"}}}
267 </pre>
268 </code>
269
270 <hr width="60%" align="left">
271
272 <a name="getPerson"><h3><a href="#getPerson">getPerson</a></h3>
273
274 <p>
275
276 <code>getPerson</code> is the singleton version of <a
277 href="#getPeople">getPeople</a> and returns the same data, but in a
278 <code>person</code> container instead of a <code>people</code>
279 container.
280
281 </p>
282
283 <h4>Required Arguments</h4>
284
285 <p>
286
287 <b><code>app</code></b>: Name of the app making the request.<br/>
288 <b><code>person</code></b>: The integer identifier for the person on whoisi.
289
290 </p>
291
292 <h4>Return Value</h4>
293
294 <p>
295
296 This call will return a JSON object with a single value
297 <code>'person'</code> that contains a dictionary for a person.  Please
298 see the example for the full structure.
299
300 </p>
301
302 <h4>Example</h4>
303
304 <p>
305
306 <code><a href="http://whoisi.com/api/getPerson?app=example&person=1">http://whoisi.com/api/getPerson?app=example&person=1</a></code>
307
308 </p>
309
310 <p>
311
312 Returns this value:
313
314 </p>
315
316 <code>
317 <pre>
318 {"person":
319     {"aliases":
320         ["@fisl2008", "@guadec2008", "@moz08", "@oscon2008", "Chris Blizzard",
321          "freenode:blizzard", "gnome:blizzard", "mozilla:blizzard"],
322      "sites":
323         {"2":
324             {"feed": "http://www.0xdeadbeef.com/weblog/?feed=rss2",
325              "url": "http://www.0xdeadbeef.com/weblog",
326              "type": "feed",
327              "title": "Christopher Blizzard"},
328          "5928":
329             {"feed": "http://twitter.com/statuses/user_timeline/15280734.atom",
330              "url": "http://twitter.com/whoisi",
331              "type": "twitter",
332              "title": "Twitter / whoisi"},
333          "6507":
334              {"feed": "http://identi.ca/blizzard/rss",
335               "url": "http://identi.ca/blizzard",
336               "type": "identica",
337               "title": "blizzard"},
338          "12":
339             {"feed": "http://api.flickr.com/services/feeds/photos_public.gne?id=76418115@N00&lang=en-us&format=atom",
340              "url": "http://www.flickr.com/photos/christopherblizzard/",
341              "type":   "flickr",
342              "title ": "Uploads from christopherblizzard"},
343          "51":
344             {"feed": null,
345              "url": "http://www.linkedin.com/in/christopherblizzard",
346              "type": "linkedin",
347              "title": null},
348          "31":
349             {"feed": "http://twitter.com/statuses/user_timeline/12811302.atom",
350              "url": "http://twitter.com/chrisblizzard",
351              "type": "twitter",
352              "title": "Twitter / chrisblizzard"}},
353      "name": "Christopher Blizzard"}}
354 </pre>
355 </code>
356
357 <hr width="60%" align="left">
358
359 <a name="getPersonForURL"><h3><a href="#getPersonForURL">getPersonForURL</a></h3>
360
361 <p>
362
363 <code>getPersonForURL</code> attempts to return a person object for a
364 given URL.  This method isn't very good as it only returns a single
365 person and for many common URLs there are multiple problems that this
366 method can not handle at this time.
367
368 </p>
369
370 <p>
371
372 Many feeds include as their base link the main website instead of a
373 per-user url (reddit, github, others) and this method matches against
374 urls as well as feeds.  So your best bet when using this method is to
375 try the feed instead of the base url.
376
377 </p>
378
379 <p>
380
381 In addition to that problem this method doesn't do an exhaustive
382 search of the possible feed urls that may be related to a site.  For
383 example, there may be an Atom feed, RSS 2 feed and an RDF feed all on
384 the same site.  This only knows about the feed we're using as the
385 source for data.
386
387 </p>
388
389 <p>
390
391 Feedburner URLs also make this somewhat difficult.  In some cases we
392 have the original URL listed on the site's page but that just
393 redirects to a feedburner URL.  If you search for the feedburner URL
394 you will not get a match since we don't keep track of the feedburner
395 URL, only the original source for the feed.  The reverse is also true.
396 Many people list the feedburner URL and the original feed location
397 isn't known.
398
399 </p>
400
401 <p>
402
403 Isn't the internet wonderful?
404
405 </p>
406
407 <h4>Required Arguments</h4>
408
409 <p>
410
411 <b><code>app</code></b>: Name of the app making the request.<br/>
412 <b><code>url</code></b>: URL that is a feed source or base url for a website.
413
414 </p>
415
416 <h4>Return Value</h4>
417
418 <p>
419
420 This call will return a JSON object with a single value
421 <code>'person'</code> that contains a dictionary for a person.  Please
422 see the example for <a href="#getPerson"><code>getPerson</code></a>
423 for an example of the full structure.
424
425 </p>
426
427 <h4>Example</h4>
428
429 <p>
430
431 <code><a href="http://whoisi.com/api/getPersonForURL?app=example&url=http://twitter.com/statuses/user_timeline/15280734.atom">http://whoisi.com/api/getPersonForURL?app=example&url=http://twitter.com/statuses/user_timeline/15280734.atom</a></code>
432
433 </p>
434
435 <p>
436
437 This will return the exact same value as found in <a href="#getPerson"><code>getPerson</code></a>
438
439 </p>
440
441 <hr width="60%" align="left">
442
443 <a name="getURLForTinyLink"><h3><a href="#getURLForTinyLink">getURLForTinyLink</a></h3>
444
445 <p>
446
447 Sick of getting <a href="http://whoisi.com/p/611">rick rolled</a>?
448 Yeah, me too.  This method takes a whoisi tiny url and returns the URL
449 and a pile of related information about the link.  A whoisi tiny url
450 looks like this:
451
452 </p>
453
454 <p>
455
456 <a href="http://whoisi.com/l/107b50"><code>http://whoisi.com/l/107b50</code></a>
457
458 </p>
459
460 <p>
461
462 This is the format:
463
464 </p>
465
466 <p>
467
468 <code>http://whoisi.com/l/&lt;hex number&gt;</code>
469
470 </p>
471
472 <p>
473
474 Note the use of the 'l' which stands for 'link.'
475
476 </p>
477
478 <h4>Required Arguments</h4>
479
480 <p>
481
482 <b><code>app</code></b>: Name of the app making the request.<br/>
483 <b><code>url</code></b>: Full tinyurl to parse.
484
485 </p>
486
487 <h4>Return Value</h4>
488
489 <p>
490
491 This call will return a JSON dictionary object with the keys
492 <code>'url'</code>, <code>'title'</code>, <code>'site'</code>,
493 <code>'person'</code> and <code>'person_id'</code>.  See the example for
494 how everything is laid out.
495
496 </p>
497
498 <p>
499
500 If the url isn't found, it will return a JSON dictionary with a single
501 key <code>'url'</code> set to null.
502
503 </p>
504
505
506 <h4>Example</h4>
507
508 <p>
509
510 This URL:
511
512 </p>
513
514 <p>
515
516 <a href="http://whoisi.com/api/getURLForTinyLink?app=example&tiny=http://whoisi.com/l/107b50"><code>http://whoisi.com/api/getURLForTinyLink?app=example&tiny=http://whoisi.com/l/107b50</code></a>
517
518 </p>
519
520 <p>
521
522 Returns this value:
523
524 </p>
525
526 <code>
527 <pre>
528 {"title": "Nightwish - The Islander",
529  "url": "http://youtube.com/?v=vik1MdsaDX8",
530  "site":
531     {"feed": "http://youtube.com/rss/tag/+RickRoll.rss",
532      "url": "http://youtube.com/rss/tag/+RickRoll.rss",
533      "type": "feed",
534      "id": 1406,
535      "title": "YouTube :: Tag //  RickRoll"},
536  "person":
537     {"611":
538         {"aliases": ["Rick Astley", "rickroll"],
539          "sites":
540             {"1403":
541                 {"feed": "http://api.flickr.com/services/feeds/photos_public.gne?tags=rickroll&lang=en-us&format=atom",
542                  "url": "http://www.flickr.com/photos/tags/rickroll/",
543                  "type": "flickr",
544                  "title": "Recent Uploads tagged rickroll"},
545                  "1406":
546                 {"feed": "http://youtube.com/rss/tag/+RickRoll.rss",
547                  "url": "http://youtube.com/rss/tag/+RickRoll.rss",
548                  "type": "feed",
549                  "title": "YouTube :: Tag //  RickRoll"}},
550      "name": "Rick Roll"}},
551  "person_id": 611}
552 </pre>
553 </code>
554
555 <hr width="60%" align="left">
556
557 <a name="samplescripts"><h2>Sample Scripts</h2>
558
559 <p>
560
561 Several example scripts are provided.  Each of them use the HTTP APIs
562 above.  They are all written in very simple python.
563
564 </p>
565
566 <a name="dumpentiredatabase"><h3>Dump the Entire Person Database</h3>
567
568 <p>
569
570 This script will download the entire person database, 100 people at a
571 time as per the limit in the <a href="#getPeople">API docs for
572 <code>getPeople</code></a>.
573
574 </p>
575
576 <p>
577
578 You can <a
579 href="http://www.0xdeadbeef.com/~blizzard/whoisi/api_examples/whoisi_dump_db.py">download
580 this script.</a>
581
582 </p>
583
584 <hr width="60%" align="left">
585
586 <code>
587 <pre>
588 #!/usr/bin/python
589
590 import urllib
591 import simplejson
592
593 base = 'http://whoisi.com/api/'
594
595 def callAPI(endpoint, **kw):
596     arg = None
597     if kw and len(kw):
598         arg = '?' + urllib.urlencode(kw)
599
600     u = urllib.urlopen(base + endpoint + arg)
601     return simplejson.loads(u.read())
602
603 # get the max id
604 d = callAPI("getMaxPersonID", app="sample")
605 max_person_id = d.get("person_id")
606 print("max person id: %d" % max_person_id)
607
608 # our final list of people
609 people = dict()
610
611 # break the requests into 100-person segments and get the people for
612 # each one
613 steps = int(round(max_person_id/100+.9))
614 start = 1
615
616 for i in range(1, (steps+1)):
617     end = min(i * 100, max_person_id)
618     print("getting %d to %d" % (start, end))
619
620     d = callAPI("getPeople", app="sample", first=start, last=end)
621     p = d.get("people")
622     for k in p.keys():
623         people[int(k)] = p.get(k)
624
625     start += 100
626
627 k = people.keys()
628 k.sort()
629
630 for i in k:
631     p = people.get(i)
632     print("%s - http://whoisi.com/p/%d" % (p.get("name").encode("utf-8"), int(i)))
633     a = p.get("aliases")
634     if len(a):
635         print("  aliases: %s" % a)
636
637     sites = p.get("sites")
638     if len(sites):
639         sk = sites.keys()
640         sk.sort()
641         for j in sk:
642             s = sites.get(j)
643             t = s.get("title")
644             if t:
645                 t = str(t.encode("utf-8"))
646             print("  site: %s" % t)
647             print("    type: %s" % s.get("type"))
648             print("    url: %s" % s.get("url"))
649             print("    feed: %s" % s.get("feed"))
650 </pre>
651 </code>
652
653 <hr width="60%" align="left">
654
655 <a name="dumpsingleperson"><h3>Dump a Single Person</h3>
656
657 <p>
658
659 This script will dump a single person using the <a
660 href="#getPerson"><code>getPerson</code></a> API.
661
662 </p>
663
664 <p>
665
666 You can <a
667 href="http://www.0xdeadbeef.com/~blizzard/whoisi/api_examples/whoisi_dump_person.py">donload
668 this script.</a>
669
670 </p>
671
672 <code>
673 <pre>
674 #!/usr/bin/python
675
676 import urllib
677 import simplejson
678 import sys
679
680 person_id = 1
681 if len(sys.argv) > 1:
682     person_id = int(sys.argv[1])
683
684 base = 'http://whoisi.com/api/'
685
686 def callAPI(endpoint, **kw):
687     arg = None
688     if kw and len(kw):
689         arg = '?' + urllib.urlencode(kw)
690
691     u = urllib.urlopen(base + endpoint + arg)
692     return simplejson.loads(u.read())
693
694 # get the max id
695 d = callAPI("getPerson", app="sample", person=person_id)
696 p = d.get("person")
697
698 print("%s - http://whoisi.com/p/%d" % (p.get("name"), person_id))
699 a = p.get("aliases")
700 if len(a):
701     print("  aliases: %s" % a)
702
703 sites = p.get("sites")
704 if len(sites):
705     sk = sites.keys()
706     sk.sort()
707     for j in sk:
708         s = sites.get(j)
709         print("  site: %s" % s.get("title"))
710         print("    type: %s" % s.get("type"))
711         print("    url: %s" % s.get("url"))
712         print("    feed: %s" % s.get("feed"))
713 </pre>
714 </code>
715
716 <hr width="60%" align="left">
717
718 <a name="personforurl"><h3>Person for URL</h3>
719
720 <p>
721
722 This script will get a person for a url using the <a
723 href="#getPersonForURL"><code>getPersonForURL</code></a> API call.
724
725 </p>
726
727 <p>
728
729 You can <a
730 href="http://www.0xdeadbeef.com/~blizzard/whoisi/api_examples/whoisi_url_lookup.py">download
731 this script.</a>
732
733 </p>
734
735 <code>
736 <pre>
737 #!/usr/bin/python
738
739 import urllib
740 import simplejson
741
742 base = 'http://whoisi.com/api/'
743
744 def callAPI(endpoint, **kw):
745     arg = None
746     if kw and len(kw):
747         arg = '?' + urllib.urlencode(kw)
748
749     u = urllib.urlopen(base + endpoint + arg)
750     return simplejson.loads(u.read())
751
752 # get the person for a url
753 d = callAPI("getPersonForURL", app="sample",
754             url="http://www.0xdeadbeef.com/weblog/")
755 print d
756 </pre>
757 </code>
758
759 <hr width="60%" align="left">
760
761 <a name="tinyurllookup"><h3>Info for TinyURL</h3>
762
763 <p>
764
765 This script will get information for a specific tinyurl that you might
766 see on a web page somewhere.
767
768 </p>
769
770 <p>
771
772 You can <a
773 href="http://www.0xdeadbeef.com/~blizzard/whoisi/api_examples/whoisi_tiny_link_lookup.py">download
774 this script.</a>
775
776 </p>
777
778 <code>
779 <pre>
780 #!/usr/bin/python
781
782 import urllib
783 import simplejson
784
785 base = 'http://whoisi.com/api/'
786
787 def callAPI(endpoint, **kw):
788     arg = None
789     if kw and len(kw):
790         arg = '?' + urllib.urlencode(kw)
791
792     u = urllib.urlopen(base + endpoint + arg)
793     return simplejson.loads(u.read())
794
795 # get the person for a url
796 d = callAPI("getURLForTinyLink", app="sample",
797             tiny="http://whoisi.com/l/136ca")
798
799 print d
800 </pre>
801 </code>
802
803 <hr width="60%" align="left">
804
805 </div>