Error: Failed to load processor TracNav
No macro or processor named 'TracNav' found

Error: Failed to load processor TOC
No macro or processor named 'TOC' found

This page NeedsTranslation.


GET & Authentication

To browse the public marks posted on Blogmarks.net just fetch this URL :

http://api.blogmarks.net/marks

By default, the last 30 public marks posted on blogmarks.net are sent.

Corresponding to the following parameters :

  • last : 30
  • order_type : DESC
  • order_by : created

And so the following URL :

http://api.blogmarks.net/marks?last=30&order_type=DESC&order_by=created

So you can grab the 10 most popular marks of all time, by fetching this URL :

http://api.blogmarks.net/marks?last=10&order_by=popularity

Let's write our first HTTP query and fetch the last mark :

GET /marks?last=1 HTTP/1.0
Host: api.blogmarks.net
Accept: application/atom+xml

The server response :

HTTP/1.1 200 OK
Date: Mon, 07 Mar 2005 19:32:10 GMT
Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.9-1.dotdeb.3
X-Powered-By: PHP/4.3.9-1.dotdeb.3
Content-length: 1293
Connection: close
Content-Type: text/xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<feed
version="draft-ietf-atompub-format-05:do not deploy"
xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
xmlns:bm="http://api.blogmarks.net/ns#">
<head>
	<title>Last public marks</title>
	<link rel="alternate" type="application/xhtml+xml" href="?last=1"/>
	<link rel="next" type="application/atom+xml" href="http://api.blogmarks.net/marks?last=1&amp;offset=1&amp;order_type=DESC&amp;order_by=created"/>
	<updated>2005-03-07T20:23:04Z</updated>
</head>
<entry>
	<id>tag:blogmarks.net,2005:marks,31171</id>
	<title type="TEXT">Kevin Kelly -- Cool Tools:Wall Full of Whiteboard</title>
	<link rel="related" href="http://www.kk.org/cooltools/archives/000679.php" type="text/html"/>
	<link rel="alternate" href="http://blogmarks.net/user/delicious.popular/archives/2005/03/#mark31171" type="application/xhtml+xml" title="Kevin Kelly -- Cool Tools:Wall Full of Whiteboard"/>
	<link rel="image" href="http://www.blogmarks.net/screenshots/2005/03/07/2d3eb782512a6aff2540f74a0a32bd3d.png" type="image/png"/>
	<updated>2005-03-07T20:23:04Z</updated>
	<published>2005-03-07T20:23:04Z</published>
	<author><name>delicious.popular</name></author>
	<edit>http://api.blogmarks.net/atom/marks/31171</edit>
	<bm:created>2005-03-07T20:23:04Z</bm:created>
</entry>
</feed>

If you want to write this in a script, we recommand you to use an HTTP library like Pear HTTP:Request (in case you're using PHP) :

<?php

require_once "HTTP/Request.php";

$req =& new HTTP_Request("http://api.blogmarks.net/marks?last=1");
$req->addHeader("Accept", 'application/atom+xml');

$response = $req->sendRequest();

if (PEAR::isError($response)) {
	echo $response->getMessage();
} else {
	$code = $req->getResponseCode();
	switch ( $code ) {
	  case 200:
	    $xml = $req->getResponseBody();
	    // Handle the resulting Atom response here
	    echo $xml;
	    break;
	  default:
	    echo $code . " Error\n";
	    break;
	}
	
}

?>

For your curiosity, in vanilla PHP, you can write this (very basic, no error handling) :

<?php

$request  = 'GET /marks?last=1 HTTP/1.0' . "\n";
$request .= 'Host: api.blogmarks.net' . "\n";
$request .= 'Accept: application/atom+xml' . "\n";
$request .= "\n";

$fp = fsockopen('api.blogmarks.net', 80, $errno, $errstr, 30);

fputs($fp,$request);

$response = '';
while ( !feof ( $fp ) ) {
	$line = fgets ($fp, 128);
	$response .= $line;
}
fclose($fp);

echo $response;

?>

Now, imagine you want to fetch your private marks and private tags. This feature require authentication. As advised in the Atom authentication tutorial by Mark Pilgrim, we'll use WSSE Username Token authentication. For detailed explanation about WSSE authentication, the tutorial is a must read.

We start with 4 variables :

  • $Username : znarf
  • $Password : foo
  • $Nonce (a cryptographically random string ) : 15253654
  • $CreationTimestamp (the current time in W3CDTF format) : 2004-03-08T17:11:42Z

In our implementation we use the md5 hash of the password. If you have the original password hash it, else just use the hashed one.

So eventually :

  • $PasswordHash = md5( $Password );

We create the password digest :

  • $PasswordDigest = Base64 ( SHA1 ( $Nonce + $CreationTimestamp + $PasswordHash ) )

Basically we add 2 lines in the HTTP header and we are authenticated. Note we've set the private parameter to TRUE to request the private marks and private tags.

GET /marks?last=30&private=true HTTP/1.0
Host: api.blogmarks.net
Accept: application/atom+xml
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="$Username", PasswordDigest="$PasswordDigest", Nonce="$Nonce", Created="$CreationTimestamp"

The corresponding PHP code is :

<?php

$Username = 'znarf';
$Password = 'foo';
$Nonce = rand( 1 , 100000000000000 );
$CreationTimestamp = date('Y-m-d\Th:i:s\Z');

$PasswordHash = md5( $Password );

$PasswordDigest = base64_encode( sha1( $Nonce . $CreationTimestamp . $PasswordHash  ) );

require_once "HTTP/Request.php";

$req =& new HTTP_Request("http://api.blogmarks.net/marks?last=1");
$req->addHeader("Accept", 'application/atom+xml');
$req->addHeader('Authorization', 'WSSE profile="UsernameToken"');
$req->addHeader('X-WSSE', 'UsernameToken Username="' . $Username . '",
      PasswordDigest="' . $PasswordDigest . '", Nonce="' . $Nonce . '", Created="' . $CreationTimestamp . '"');

$response = $req->sendRequest();

if (PEAR::isError($response)) {
	echo $response->getMessage();
} else {
	$code = $req->getResponseCode();
	switch ( $code ) {
	  case 200:
	    $xml = $req->getResponseBody();
	    // Handle the resulting Atom response here
	    echo $xml;
	    break;
	  default:
	    echo $code . " Error\n";
	    break;
	}
	
}

?>

Note that we've handled directly the authentication but the best is to use core components of your framework of choice.

POST

You add a new mark on blogmarks.net by posting an Atom entry on a postURI. The postURI is http://api.blogmarks.net/marks.

In the Atom entry, you must specify at least a related link and a title.

First example

We'll se how to create the most simple allowed blogmark.

Authentication is explained in AtomApiTutorial/Get

The client send

POST /marks HTTP/1.0
Host: api.blogmarks.net
Accept: application/atom+xml
Content-Type: text/xml; charset=utf-8
Content-length: 267
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="$Username", PasswordDigest="$PasswordDigest", Nonce="$Nonce", Created="$CreationTimestamp"

<entry
  version="draft-ietf-atompub-format-05:do not deploy"
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#">
<title>Znarf Blog</title>
<link rel="related" href="http://upian.net/znarf/blog/" />
</entry>

The server response :

HTTP/1.1 201 Created
Date: Wed, 09 Mar 2005 11:21:03 GMT
Content-length: 811
Connection: close
Content-Type: application/atom+xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<entry
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#>
 <id>tag:blogmarks.net,2005:marks,31523</id>
 <title type="TEXT">Znarf Blog</title>
 <link rel="related" href="http://upian.net/znarf/blog/" type="text/html"/>
 <link rel="alternate" href="http://blogmarks.net/user/znarf/archives/2005/03/#mark31523" type="application/xhtml+xml" title="Znarf Blog Test"/>
 <link rel="image" href="http://blogmarks.net/screenshots/2005/03/09/dbc0c60c8794c0ca4f2e99f10a5f2408.png" type="image/png"/>
 <updated>2005-03-09T12:21:03Z</updated>
 <author><name>znarf</name></author>
 <edit>http://api.blogmarks.net/marks/31523</edit>
 <bm:created>2005-03-09T12:21:03Z</bm:created>
</entry>

Second example

We'll create a more advanced blogmark.

  • In the Atom mapping, tags are called category
  • By specifing published=0000-00-00, we say that the blogmarks is private (not published)
  • bm:created is the main blogmark date. This is the one which is used to sort blogmarks chronologically
POST /marks HTTP/1.0
Host: api.blogmarks.net
Accept: application/atom+xml
Content-Type: text/xml; charset=utf-8
Content-length: 535
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="$Username", PasswordDigest="$PasswordDigest", Nonce="$Nonce", Created="$CreationTimestamp"

<?xml version="1.0" encoding="UTF-8"?>
<entry
  version="draft-ietf-atompub-format-05:do not deploy"
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#">
 <title>Znarf Blog</title>
 <link rel="related" href="http://upian.net/znarf/blog/" />
 <link rel="via" href="http://blogmarks.net/" />
 <summary>This blog really rox</summary>
 <category label="blog" />
 <category label="dotclear" />
 <published>0000-00-00</published>
 <bm:created>2005-03-09</bm:created>
</entry>

Same code in PHP / Pear HTTP Request

<?php

$Username = 'znarf';
$Password = '';
$Nonce = rand( 1 , 100000000000000 );
$CreationTimestamp = date('Y-m-d\Th:i:s\Z');

$PasswordHash = md5( $Password );

$PasswordDigest = base64_encode( sha1( $Nonce . $CreationTimestamp . $PasswordHash  ) );

require_once "HTTP/Request.php";

$req =& new HTTP_Request('http://api.blogmarks.net/marks');
$req->setMethod(HTTP_REQUEST_METHOD_POST);
$req->addHeader('X-WSSE', 'UsernameToken Username="' . $Username . '", PasswordDigest="' . $PasswordDigest . '", Nonce="' . $Nonce . '", Created="' . $CreationTimestamp . '"');

$xml  =
'<?xml version="1.0" encoding="UTF-8"?>
<entry
  version="draft-ietf-atompub-format-05:do not deploy"
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#">
 <title>Znarf Blog</title>
 <link rel="related" href="http://upian.net/znarf/blog/" />
 <link rel="via" href="http://blogmarks.net/" />
 <summary>This blog rox</summary>
 <category term="http://api.blogmarks.net/tags/" sheme="blog" />
 <category term="http://api.blogmarks.net/tags/" sheme="dotclear" />
 <published>0000-00-00</published>
 <bm:created>2005-03-09</bm:created>
</entry>';

$req->addRawPostData($xml);

$response = $req->sendRequest();

if (PEAR::isError($response)) {
    echo $response->getMessage();
} else {
    $code = $req->getResponseCode();
    switch ($code) {
    case 201:
      echo "OK\n";
      echo $req->getResponseBody();
      break;
    default:
      echo $code . " Error\n";
      break;
    }
}

?>

Good practices

  • Don't flood : Flooding is posting a lot of blogmarks (say more than 5) and by the way filling the Last Public Marks list. Others users really don't like that. The recommended practice is to specify a bm:created in the past. You may have one if you import marks for example. If you don't have one, we recommand to put the current date without specifying the hour. <bm:created>2005-03-08Z</bm:created>

Put

To update a blogmark, just PUT an Atom entry on the mark editURI.

The editURI can be found in Atom entries and is basically http://api.blogmarks.net/marks/$MarkID

Authentication is explained in AtomApiTutorial/Get

The client send :

PUT /marks/31523 HTTP/1.0
Host: api.blogmarks.net
Accept: application/atom+xml
Content-Type: text/xml; charset=utf-8
Content-length: 433
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="$Username", PasswordDigest="$PasswordDigest", Nonce="$Nonce", Created="$CreationTimestamp"

<entry
  version="draft-ietf-atompub-format-05:do not deploy"
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#">
 <title>Znarf Blog</title>
 <link rel="related" href="http://upian.net/znarf/blog/" />
 <summary>This blog *really* rox</summary>
 <category term="http://api.blogmarks.net/tags/" sheme="blog" />
 <category term="http://api.blogmarks.net/tags/" sheme="dotclear" />
 <category term="http://api.blogmarks.net/tags/" sheme="daily" />
 <category term="http://api.blogmarks.net/tags/" sheme="topsite" />
</entry>

The server response :

HTTP/1.1 200 OK
Date: Wed, 09 Mar 2005 11:21:03 GMT
Content-length: 811
Connection: close
Content-Type: application/atom+xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<entry
  xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05"
  xmlns:bm="http://api.blogmarks.net/ns#>
 <id>tag:blogmarks.net,2005:marks,31523</id>
 <title type="TEXT">Znarf Blog</title>
 <link rel="related" href="http://upian.net/znarf/blog/" type="text/html"/>
 <link rel="alternate" href="http://blogmarks.net/user/znarf/archives/2005/03/#mark31523" type="application/xhtml+xml" title="Znarf Blog"/>
 <link rel="image" href="http://blogmarks.net/screenshots/2005/03/09/dbc0c60c8794c0ca4f2e99f10a5f2408.png" type="image/png"/>
 <summary>This blog *really* rox</summary>
 <updated>2005-03-09T11:21:03Z</updated>
 <author><name>znarf</name></author>
 <edit>http://api.blogmarks.net/marks/31523</edit>
 <category term="http://api.blogmarks.net/tags/" sheme="blog" label="blog"/>
 <category term="http://api.blogmarks.net/tags/" sheme="dotclear" label="dotclear"/>
 <category term="http://api.blogmarks.net/tags/" sheme="daily" label="daily"/>
 <category term="http://api.blogmarks.net/tags/" sheme="topsite" label="topsite"/>
 <bm:created>2005-03-09T12:21:03Z</bm:created>
</entry>

Delete

To delete a blogmark, just send an HTTP Delete on the mark editURI.

The editURI can be found in Atom feeds and is basically http://api.blogmarks.net/marks/MARK_ID

Authentication is explained in AtomApiTutorial/Get

The client send :

DELETE /marks/1025 HTTP/1.0
Host: api.blogmarks.net
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="$Username", PasswordDigest="$PasswordDigest", Nonce="$Nonce", Created="$CreationTimestamp"

The server response :

HTTP/1.1 200 OK
Date: Thu, 10 Mar 2005 12:10:26 GMT
Connection: close

With PHP / Pear HTTP:Request you write

<?php

$Username = 'znarf';
$Password = '';
$Nonce = rand( 1 , 100000000000000 );
$CreationTimestamp = date('Y-m-d\Th:i:s\Z');

$PasswordHash = md5( $Password );

$PasswordDigest = base64_encode( sha1( $Nonce . $CreationTimestamp . $PasswordHash  ) );

require_once "HTTP/Request.php";

$req =& new HTTP_Request('http://api.blogmarks.net/marks/1025');
$req->setMethod(HTTP_REQUEST_METHOD_DELETE);
$req->addHeader('X-WSSE', 'UsernameToken Username="' . $Username . '", PasswordDigest="' . $PasswordDigest . '", Nonce="' . $Nonce . '", Created="' . $CreationTimestamp . '"');

$response = $req->sendRequest();

if (PEAR::isError($response)) {
    echo $response->getMessage();
} else {
    $code = $req->getResponseCode();
    switch ($code) {
    case 200:
      echo "OK\n";
      break;
    default:
      echo $code . " Error\n";
      echo $req->getResponseBody();
      break;
    }
}

?>

See also : DeveloperDocs