diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 6ed73e1f7a93b9e134d567130f19588e7855dff2..fb26a886e5f067cecfdaa48ec257cc2cdd0ed7e8 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -192,15 +192,15 @@ class Piwik_API_DocumentationGenerator
 		$aParameters['flat'] = false;
 		$aParameters['include_aggregate_rows'] = false;
         $aParameters['filter_truncate'] = false;
-		
+        
 		$moduleName = Piwik_API_Proxy::getInstance()->getModuleNameFromClassName($class);
-		$urlExample = '?module=API&method='.$moduleName.'.'.$methodName.'&';
-		foreach($aParameters as $nameVariable=> $defaultValue)
+		$aParameters = array_merge(array('module' => 'API', 'method' => $moduleName.'.'.$methodName), $aParameters);
+		
+		foreach($aParameters as $nameVariable => &$defaultValue)
 		{
 			if(isset($knowExampleDefaultParametersValues[$nameVariable]))
 			{
-				$exampleValue = $knowExampleDefaultParametersValues[$nameVariable];
-				$urlExample .= $nameVariable . '=' . $exampleValue . '&';
+				$defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
 			}
 			// if there isn't a default value for a given parameter, 
 			// we need a 'know default value' or we can't generate the link
@@ -209,7 +209,7 @@ class Piwik_API_DocumentationGenerator
 				return false;
 			}
 		}
-		return substr($urlExample,0,-1);
+		return '?'.Piwik_Url::getQueryStringFromParameters($aParameters);
 	}
 	
 	
diff --git a/core/DataTable/Renderer/Xml.php b/core/DataTable/Renderer/Xml.php
index 01e127a11c123d73bd277b755d47ce28003fe2f0..74bb78be41e90859cb8e31f3b83632a12cafa91e 100644
--- a/core/DataTable/Renderer/Xml.php
+++ b/core/DataTable/Renderer/Xml.php
@@ -323,7 +323,7 @@ class Piwik_DataTable_Renderer_Xml extends Piwik_DataTable_Renderer
 			if(count($row) === 1
 				&& key($row) === 0)
 			{
-				$value = current($row);
+				$value = self::formatValueXml(current($row));
 				$out .= $prefixLine . $value;				
 			}
 			else
diff --git a/core/Tracker.php b/core/Tracker.php
index 19f4c66920607393e03d0db1fe5b24b46d21cfd3..7e76a2362a4527dc69b7c2392f7ce41d1d77d345 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -47,11 +47,61 @@ class Piwik_Tracker
 	static protected $forcedVisitorId = null;
 
 	static protected $pluginsNotToLoad = array();
+	
+	/**
+	 * The set of visits to track.
+	 * 
+	 * @var array
+	 */
+	private $requests = array();
+	
+	/**
+	 * The token auth supplied with a bulk visits POST.
+	 * 
+	 * @var string
+	 */
+	private $tokenAuth = null;
 
 	public function __construct($args = null)
 	{
-		$this->request = $args ? $args : $_GET + $_POST;
+		if (!empty($args) || !empty($_GET) || !empty($_POST))
+		{
+			$this->requests = $args ? $args : array($_GET + $_POST);
+		}
+		else
+		{
+			// doing bulk tracking. POST data can be array of string URLs or array of arrays w/ visit info
+			$rawData = file_get_contents("php://input");
+			$jsonData = Piwik_Common::json_decode($rawData, $assoc = true);
+			
+			if (isset($jsonData['requests']))
+			{
+				$this->requests = $jsonData['requests'];
+			}
+			
+			$this->tokenAuth = Piwik_Common::getRequestVar('token_auth', false, null, $jsonData);
+			
+			if (!empty($this->requests))
+			{
+				foreach ($this->requests as &$request)
+				{
+					// if a string is sent, we assume its a URL and try to parse it
+					if (is_string($request))
+					{
+						$params = array();
+					
+						$url = @parse_url($request);
+						if (!empty($url))
+						{
+							@parse_str($url['query'], $params);
+							$request = $params;
+						}
+					}
+				}
+			}
+		}
 	}
+	
 	public static function setForceIp($ipString)
 	{
 		self::$forcedIpString = $ipString;
@@ -99,19 +149,40 @@ class Piwik_Tracker
 	 */
 	public function main()
 	{
-		$this->init();
-
-		try {
-			if( $this->isVisitValid() )
+		if (!empty($this->requests))
+		{
+			// handle all visits
+			foreach ($this->requests as $request)
 			{
-				self::connectDatabase();
-
-				$visit = $this->getNewVisitObject();
-				$visit->setRequest($this->request);
-				$visit->handle();
-				unset($visit);
+				$this->init($request);
+		
+				try
+				{
+					if ($this->isVisitValid())
+					{
+						self::connectDatabaseIfNotConnected();
+
+						$visit = $this->getNewVisitObject();
+						$visit->setRequest($request);
+						$visit->handle();
+						unset($visit);
+					}
+				} catch (Piwik_Tracker_Db_Exception $e) {
+					printDebug("<b>".$e->getMessage()."</b>");
+				} catch(Piwik_Tracker_Visit_Excluded $e) {
+				} catch(Exception $e) {
+					Piwik_Tracker_ExitWithException($e);
+				}
 			}
-
+		}
+		else
+		{
+			$this->handleEmptyRequest($_GET + $_POST);
+		}
+		
+		// run scheduled task
+		try
+		{
 			// don't run scheduled tasks in CLI mode from Tracker, this is the case
 			// where we bulk load logs & don't want to lose time with tasks
 			if(!Piwik_Common::isPhpCliMode()
@@ -119,13 +190,12 @@ class Piwik_Tracker
 			{
 				Piwik_Common::runScheduledTasks($now = $this->getCurrentTimestamp());
 			}
-		} catch (Piwik_Tracker_Db_Exception $e) {
-			printDebug("<b>".$e->getMessage()."</b>");
-		} catch(Piwik_Tracker_Visit_Excluded $e) {
-		} catch(Exception $e) {
+		}
+		catch (Exception $e)
+		{
 			Piwik_Tracker_ExitWithException($e);
 		}
-
+		
 		$this->end();
 	}
 
@@ -143,12 +213,12 @@ class Piwik_Tracker
 	/**
 	 * Initialization
 	 */
-	protected function init()
+	protected function init( $request )
 	{
-		$this->handleTrackingApi();
-		$this->loadTrackerPlugins();
+		$this->handleTrackingApi($request);
+		$this->loadTrackerPlugins($request);
 		$this->handleDisabledTracker();
-		$this->handleEmptyRequest();
+		$this->handleEmptyRequest($request);
 
 		printDebug("Current datetime: ".date("Y-m-d H:i:s", $this->getCurrentTimestamp()));
 	}
@@ -232,7 +302,7 @@ class Piwik_Tracker
 		return $db;
 	}
 
-	public static function connectDatabase()
+	public static function connectDatabaseIfNotConnected()
 	{
 		if( !is_null(self::$db))
 		{
@@ -321,11 +391,11 @@ class Piwik_Tracker
 		$this->stateValid = $value;
 	}
 
-	protected function loadTrackerPlugins()
+	protected function loadTrackerPlugins( $request )
 	{
 		// Adding &dp=1 will disable the provider plugin, if token_auth is used (used to speed up bulk imports)
-		if(isset($this->request['dp'])
-			&& !empty($this->request['dp'])
+		if(isset($request['dp'])
+			&& !empty($request['dp'])
 			&& $this->authenticated)
 		{
 			Piwik_Tracker::setPluginsNotToLoad(array('Provider'));
@@ -347,9 +417,9 @@ class Piwik_Tracker
 		}
 	}
 
-	protected function handleEmptyRequest()
+	protected function handleEmptyRequest( $request )
 	{
-		$countParameters = count($this->request);
+		$countParameters = count($request);
 		if($countParameters == 0)
 		{
 			$this->setState(self::STATE_EMPTY_REQUEST);
@@ -369,9 +439,9 @@ class Piwik_Tracker
 		}
 	}
 
-	protected function authenticateSuperUserOrAdmin()
+	protected function authenticateSuperUserOrAdmin( $request )
 	{
-		$tokenAuth = Piwik_Common::getRequestVar('token_auth', false);
+		$tokenAuth = $this->getTokenAuth();
 
 		if( $tokenAuth )
 		{
@@ -384,7 +454,7 @@ class Piwik_Tracker
 			}
 
 			// Now checking the list of admin token_auth cached in the Tracker config file
-			$idSite = Piwik_Common::getRequestVar('idsite', false, 'int', $this->request);
+			$idSite = Piwik_Common::getRequestVar('idsite', false, 'int', $request);
 			if(!empty($idSite)
 				&& $idSite > 0)
 			{
@@ -400,17 +470,27 @@ class Piwik_Tracker
 		}
 		return false;
 	}
+	
+	protected function getTokenAuth()
+	{
+		if (!is_null($this->tokenAuth))
+		{
+			return $this->tokenAuth;
+		}
+		
+		return Piwik_Common::getRequestVar('token_auth', false);
+	}
 
 	/**
 	 * This method allows to set custom IP + server time when using Tracking API.
 	 * These two attributes can be only set by the Super User (passing token_auth).
 	 */
-	protected function handleTrackingApi()
+	protected function handleTrackingApi( $request )
 	{
 		$shouldAuthenticate = Piwik_Config::getInstance()->Tracker['tracking_requests_require_authentication'];
 		if($shouldAuthenticate)
 		{
-			if(!$this->authenticateSuperUserOrAdmin())
+			if(!$this->authenticateSuperUserOrAdmin($request))
 			{
 				return;
 			}
@@ -422,21 +502,21 @@ class Piwik_Tracker
 		}
 
 		// Custom IP to use for this visitor
-		$customIp = Piwik_Common::getRequestVar('cip', false, 'string', $this->request);
+		$customIp = Piwik_Common::getRequestVar('cip', false, 'string', $request);
 		if(!empty($customIp))
 		{
 			$this->setForceIp($customIp);
 		}
 
 		// Custom server date time to use
-		$customDatetime = Piwik_Common::getRequestVar('cdt', false, 'string', $this->request);
+		$customDatetime = Piwik_Common::getRequestVar('cdt', false, 'string', $request);
 		if(!empty($customDatetime))
 		{
 			$this->setForceDateTime($customDatetime);
 		}
 
 		// Forced Visitor ID to record the visit / action
-		$customVisitorId = Piwik_Common::getRequestVar('cid', false, 'string', $this->request);
+		$customVisitorId = Piwik_Common::getRequestVar('cid', false, 'string', $request);
 		if(!empty($customVisitorId))
 		{
 			$this->setForceVisitorId($customVisitorId);
diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php
index 445fac2700c23546111d492b6f47b3cd14f9c6ea..b516623fe2375cc1844e3171cb915d6da411b8c9 100644
--- a/libs/PiwikTracker/PiwikTracker.php
+++ b/libs/PiwikTracker/PiwikTracker.php
@@ -93,6 +93,8 @@ class PiwikTracker
     	
 		// Allow debug while blocking the request
     	$this->requestTimeout = 600;
+    	$this->doBulkRequests = false;
+    	$this->storedTrackingActions = array();
     }
     
     /**
@@ -244,12 +246,20 @@ class PiwikTracker
     	$this->userAgent = $userAgent;
     }
     
-
+	/**
+	 * Enables the bulk request feature. When used, each tracking action is stored until the
+	 * doBulkTrack method is called. This method will send all tracking data at once.
+	 */
+	public function enableBulkTracking()
+	{
+		$this->doBulkRequests = true;
+	}
+	
     /**
      * Tracks a page view
      * 
      * @param string $documentTitle Page title as it will appear in the Actions > Page titles report
-     * @return string Response
+     * @return string|true Response or true if using bulk requests.
      */
     public function doTrackPageView( $documentTitle )
     {
@@ -262,7 +272,7 @@ class PiwikTracker
      * 
      * @param int $idGoal Id Goal to record a conversion
      * @param float $revenue Revenue for this conversion
-     * @return string Response
+     * @return string|true Response or true if using bulk request
      */
     public function doTrackGoal($idGoal, $revenue = false)
     {
@@ -275,7 +285,7 @@ class PiwikTracker
      * 
      * @param string $actionUrl URL of the download or outlink
      * @param string $actionType Type of the action: 'download' or 'link'
-     * @return string Response
+     * @return string|true Response or true if using bulk request
      */
     public function doTrackAction($actionUrl, $actionType)
     {
@@ -322,6 +332,34 @@ class PiwikTracker
     	return $this->sendRequest($url); 
     }
     
+    /**
+     * Sends all stored tracking actions at once. Only has an effect if bulk tracking is enabled.
+     * 
+     * To enable bulk tracking, call enableBulkTracking().
+     * 
+     * @return string Response
+     */
+    public function doBulkTrack()
+    {
+    	if (empty($this->storedTrackingActions))
+    	{
+    		return '';
+    	}
+    	
+    	$data = array('requests' => $this->storedTrackingActions);
+    	if (!empty($this->token_auth))
+    	{
+    		$data['token_auth'] = $this->token_auth;
+    	}
+    	
+    	$postData = json_encode($data);
+    	$response = $this->sendRequest($this->getBaseUrl(), 'POST', $postData, $force = true);
+    	
+    	$this->storedTrackingActions = array();
+    	
+    	return $response;
+    }
+    
     /**
 	 * Tracks an Ecommerce order.
 	 * 
@@ -337,6 +375,7 @@ class PiwikTracker
 	 * @param float $tax (optional) Tax amount for this order
 	 * @param float $shipping (optional) Shipping amount for this order
 	 * @param float $discount (optional) Discounted amount in this order
+	 * @return string|true Response or true if using bulk request
      */
     public function doTrackEcommerceOrder($orderId, $grandTotal, $subTotal = false, $tax = false, $shipping = false, $discount = false)
     {
@@ -735,8 +774,15 @@ class PiwikTracker
     /**
      * @ignore
      */
-    protected function sendRequest($url)
+    protected function sendRequest( $url, $method = 'GET', $data = null, $force = false )
     {
+    	// if doing a bulk request, store the url
+    	if ($this->doBulkRequests && !$force)
+    	{
+    		$this->storedTrackingActions[] = $url;
+    		return true;
+    	}
+    	
 		$response = '';
 
 		if(!$this->cookieSupport)
@@ -745,8 +791,7 @@ class PiwikTracker
 		}
 		if(function_exists('curl_init'))
 		{
-			$ch = curl_init();
-			curl_setopt_array($ch, array(
+			$options = array(
 				CURLOPT_URL => $url,
 				CURLOPT_USERAGENT => $this->userAgent,
 				CURLOPT_HEADER => true,
@@ -755,8 +800,27 @@ class PiwikTracker
 				CURLOPT_HTTPHEADER => array(
 					'Accept-Language: ' . $this->acceptLanguage,
 					'Cookie: '. $this->requestCookie,
-				),
-			));
+				));
+			
+			switch ($method)
+			{
+				case 'POST':
+					$options[CURLOPT_POST] = TRUE;
+					break;
+				default:
+					break;
+			}
+			
+			// only supports JSON data
+			if (!empty($data))
+			{
+				$options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
+				$options[CURLOPT_HTTPHEADER][] = 'Expect:';
+				$options[CURLOPT_POSTFIELDS] = $data;
+			}
+			
+			$ch = curl_init();
+			curl_setopt_array($ch, $options);
 			ob_start();
 			$response = @curl_exec($ch);
 			ob_end_clean();
@@ -770,12 +834,21 @@ class PiwikTracker
 		{
 			$stream_options = array(
 				'http' => array(
+					'method' => $method,
 					'user_agent' => $this->userAgent,
 					'header' => "Accept-Language: " . $this->acceptLanguage . "\r\n" .
 					            "Cookie: ".$this->requestCookie. "\r\n" ,
 					'timeout' => $this->requestTimeout, // PHP 5.2.1
 				)
 			);
+			
+			// only supports JSON data
+			if (!empty($data))
+			{
+				$stream_options['http']['header'][] = 'Content-Type: application/json';
+				$stream_options['http']['content'] = $data;
+			}
+			
 			$ctx = stream_context_create($stream_options);
 			$response = file_get_contents($url, 0, $ctx);
 			$header = implode("\r\n", $http_response_header); 
@@ -816,9 +889,9 @@ class PiwikTracker
     }
     
     /**
-     * @ignore
+     * Returns the base URL for the piwik server.
      */
-    protected function getRequest( $idSite )
+    protected function getBaseUrl()
     {
     	if(empty(self::$URL))
     	{
@@ -829,8 +902,15 @@ class PiwikTracker
     	{
     		self::$URL .= '/piwik.php';
     	}
-    	
-    	$url = self::$URL .
+    	return self::$URL;
+    }
+    
+    /**
+     * @ignore
+     */
+    protected function getRequest( $idSite )
+    {
+    	$url = $this->getBaseUrl() .
 	 		'?idsite=' . $idSite .
 			'&rec=1' .
 			'&apiv=' . self::VERSION . 
@@ -844,7 +924,7 @@ class PiwikTracker
 			(!empty($this->ip) ? '&cip=' . $this->ip : '') .
     		(!empty($this->forcedVisitorId) ? '&cid=' . $this->forcedVisitorId : '&_id=' . $this->visitorId) . 
 			(!empty($this->forcedDatetime) ? '&cdt=' . urlencode($this->forcedDatetime) : '') .
-			(!empty($this->token_auth) ? '&token_auth=' . urlencode($this->token_auth) : '') .
+			((!empty($this->token_auth) && !$this->doBulkRequests) ? '&token_auth=' . urlencode($this->token_auth) : '') .
 	        
 			// These parameters are set by the JS, but optional when using API
 	        (!empty($this->plugins) ? $this->plugins : '') . 
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 76e56137a18e88d7c66a657385ae44cc6d5a5cb8..03586f6c80a2910c4c23358d98c85dbe50a94cb3 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -1450,4 +1450,26 @@ class Piwik_API_API
 		);
 	}
 	
+	/**
+	 * Performs multiple API requests at once and returns every result.
+	 * 
+	 * @param array $urls The array of API requests.
+	 */
+	public function getBulkRequest( $urls )
+	{
+		if (empty($urls))
+		{
+			return array();
+		}
+		
+		$urls = Piwik_Common::unsanitizeInputValues($urls);
+		
+		$result = array();
+		foreach ($urls as $url)
+		{
+			$req = new Piwik_API_Request($url);
+			$result[] = $req->process();
+		}
+		return $result;
+	}
 }
diff --git a/tests/integration/EcommerceOrderWithItems.test.php b/tests/integration/EcommerceOrderWithItems.test.php
index d1240e59df9d779ab061e1d2d5192d8c5a68e2bc..90b3171867bdf8f84ab95ffb0290429dd896c56d 100755
--- a/tests/integration/EcommerceOrderWithItems.test.php
+++ b/tests/integration/EcommerceOrderWithItems.test.php
@@ -130,7 +130,7 @@ class Test_Piwik_Integration_EcommerceOrderWithItems extends Test_Integration_Fa
 
 			// test segment pageTitle 
 			array('VisitsSummary.get', array('idSite' => $this->idSite, 'date' => $this->dateTime,
-											 'periods' => array('day'), 'segment' => 'pageTitle==incredible%20title!',
+											 'periods' => array('day'), 'segment' => 'pageTitle==incredible title!',
 											 'testSuffix' => '_SegmentPageTitleMatch')),
 			
 			// test Live! output is OK also for the visit that just bought something (other visits leave an abandoned cart)
diff --git a/tests/integration/Integration.php b/tests/integration/Integration.php
index d041dceaeb2e23ef0da3e2bc570cd3fa0ed4c3cc..f24a5358ce2f189f3eb3c1d672e57865a61e0987 100644
--- a/tests/integration/Integration.php
+++ b/tests/integration/Integration.php
@@ -532,10 +532,10 @@ abstract class Test_Integration extends Test_Database_Base
 			// Used in Actions.getPageUrl, .getDownload, etc.
 			// tied to Main.test.php doTest_oneVisitorTwoVisits
 			// will need refactoring when these same API functions are tested in a new function
-			'downloadUrl' 	=> urlencode('http://piwik.org/path/again/latest.zip?phpsessid=this is ignored when searching'),
-			'outlinkUrl' 	=> urlencode('http://dev.piwik.org/svn'),
-			'pageUrl' 		=> urlencode('http://example.org/index.htm?sessionid=this is also ignored by default'),
-			'pageName' 		=> urlencode(' Checkout / Purchasing... '),
+			'downloadUrl' 	=> 'http://piwik.org/path/again/latest.zip?phpsessid=this is ignored when searching',
+			'outlinkUrl' 	=> 'http://dev.piwik.org/svn',
+			'pageUrl' 		=> 'http://example.org/index.htm?sessionid=this is also ignored by default',
+			'pageName' 		=> ' Checkout / Purchasing... ',
 
 			// do not show the millisec timer in response or tests would always fail as value is changing
 			'showTimer'	 => 0,
diff --git a/tests/integration/LabelFilter.test.php b/tests/integration/LabelFilter.test.php
index b0b16cbf3071668acdcae47e3b5b75f7d30dfbbe..af487fa27dbe159910240d308741e985f4900979 100644
--- a/tests/integration/LabelFilter.test.php
+++ b/tests/integration/LabelFilter.test.php
@@ -42,7 +42,7 @@ class Test_Piwik_Integration_LabelFilter extends Test_Integration_Facade
                 'idSite' => $this->idSite,
                 'date' => $this->dateTime,
                 'otherRequestParameters' => array(
-                    'label' => urlencode($label),
+                    'label' => $label,
                     'expanded' => 0
                 )
             ));
@@ -55,7 +55,7 @@ class Test_Piwik_Integration_LabelFilter extends Test_Integration_Facade
             'date' => $this->dateTime,
             'otherRequestParameters' => array(
                 'date' => '2010-03-06,2010-03-08',
-                'label' => urlencode($label),
+                'label' => $label,
                 'expanded' => 0
             )
         ));
@@ -67,7 +67,7 @@ class Test_Piwik_Integration_LabelFilter extends Test_Integration_Facade
             'otherRequestParameters' => array(
 				// encode once for test framework and once for the label filter.
 				// note: title has no blank prefixed here. in the report it has.
-                'label' => urlencode(urlencode('incredible title! <>,;')),
+                'label' => urlencode('incredible title! <>,;'),
                 'expanded' => 0
             )
         ));
@@ -79,9 +79,9 @@ class Test_Piwik_Integration_LabelFilter extends Test_Integration_Facade
             'otherRequestParameters' => array(
                 'label' => 
 					'   '. // test trimming
-					urlencode(urlencode('incredible parent title! <>,;').
+					urlencode('incredible parent title! <>,;').
 					'>'.
-					urlencode('subtitle <>,;')),
+					urlencode('subtitle <>,;'),
                 'expanded' => 0
             )
         ));
@@ -92,13 +92,13 @@ class Test_Piwik_Integration_LabelFilter extends Test_Integration_Facade
             'idSite' => $this->idSite,
             'date' => $this->dateTime,
             'otherRequestParameters' => array(
-               	'label' => urlencode('Google>'.urlencode($keyword)),
+               	'label' => 'Google>'.urlencode($keyword),
                 'expanded' => 0
             )
         );
 		$return[] = array('Referers.getSearchEngines', $searchEngineTest);
 	
-		$searchEngineTest['otherRequestParameters']['label'] = urlencode('Google>'.urlencode(html_entity_decode($keyword)));
+		$searchEngineTest['otherRequestParameters']['label'] = 'Google>'.urlencode(html_entity_decode($keyword));
 		$return[] = array('Referers.getSearchEngines', $searchEngineTest);
 	
 		return $return;
diff --git a/tests/integration/OneVisitorTwoVisits.test.php b/tests/integration/OneVisitorTwoVisits.test.php
index 30548d9ffc786eb9b43f302367a2d5b51cef2504..952508779050a38ede6e22d04b0e89a8bdc34d47 100755
--- a/tests/integration/OneVisitorTwoVisits.test.php
+++ b/tests/integration/OneVisitorTwoVisits.test.php
@@ -25,6 +25,12 @@ class Test_Piwik_Integration_OneVisitorTwoVisits extends Test_Integration_Facade
 	public function getApiToTest()
 	{
 		$enExtraParam = array('expanded' => 1, 'flat' => 1, 'include_aggregate_rows' => 0, 'translateColumnNames' => 1);
+		$bulkUrls = array(
+			"idSite={$this->idSite}&date=2010-03-06&format=json&expanded=1&period=day&method=VisitsSummary.get",
+			"idSite={$this->idSite}&date=2010-03-06&format=xml&expanded=1&period=day&method=VisitsSummary.get",
+			"idSite={$this->idSite}&date=2010-03-06&format=json&expanded=1&period=day&method="
+				."VisitorInterest.getNumberOfVisitsPerVisitDuration"
+		);
 		return array(
 			array('all', array('idSite' => $this->idSite, 'date' => $this->dateTime)),
 			
@@ -34,6 +40,8 @@ class Test_Piwik_Integration_OneVisitorTwoVisits extends Test_Integration_Facade
 								   'periods' => array('month'), 'setDateLastN' => true,
 								   'otherRequestParameters' => $enExtraParam, 'language' => 'en',
 								   'testSuffix' => '_csv')),
+		   
+			array('API.getBulkRequest', array('otherRequestParameters' => array('urls' => $bulkUrls))),
 		);
 	}
 
diff --git a/tests/integration/RowEvolution.test.php b/tests/integration/RowEvolution.test.php
index 76b06794319a257512d67a61a5afa5fbabe18fef..9439af41fc856b2de9c65389d56a1df5b441d5ec 100644
--- a/tests/integration/RowEvolution.test.php
+++ b/tests/integration/RowEvolution.test.php
@@ -27,7 +27,7 @@ class Test_Piwik_Integration_RowEvolution extends Test_Integration_Facade
 				'period' => 'day',
 				'apiModule' => 'Referers',
 				'apiAction' => 'getWebsites',
-                'label' => urlencode('www.referrer2.com'),
+                'label' => 'www.referrer2.com',
                 'expanded' => 0
             )
         );
@@ -37,20 +37,20 @@ class Test_Piwik_Integration_RowEvolution extends Test_Integration_Facade
 		// Websites, hierarchical
 		$config['testSuffix'] = '_referrer2';
 		$referrerLabel = urlencode('www.referrer0.com').'>'.urlencode('theReferrerPage1.html');
-		$config['otherRequestParameters']['label'] = urlencode($referrerLabel);
+		$config['otherRequestParameters']['label'] = $referrerLabel;
 		$return[] = array('API.getRowEvolution', $config);
 		
 		// Websites, multiple labels including one hierarchical
 		$config['testSuffix'] = '_referrerMulti1';
 		$referrerLabel = urlencode($referrerLabel).','.urlencode('www.referrer2.com');
-		$config['otherRequestParameters']['label'] = urlencode($referrerLabel);
+		$config['otherRequestParameters']['label'] = $referrerLabel;
 		$return[] = array('API.getRowEvolution', $config);
 		
         // Keywords, label containing > and ,
 		$config['otherRequestParameters']['apiAction'] = 'getKeywords';
 		$config['testSuffix'] = '_LabelReservedCharacters';
 		$keywords = urlencode($this->keywords[0]).','.urlencode($this->keywords[1]);
-		$config['otherRequestParameters']['label'] = urlencode($keywords);
+		$config['otherRequestParameters']['label'] = $keywords;
 		$return[] = array('API.getRowEvolution', $config);
         
 		// Keywords, hierarchical
@@ -60,7 +60,7 @@ class Test_Piwik_Integration_RowEvolution extends Test_Integration_Facade
 					.',Google>'.urlencode(strtolower($this->keywords[1]))
 					.',Google>'.urlencode(strtolower($this->keywords[2]));
 		// Test multiple labels search engines, Google should also have a 'logo' entry
-		$config['otherRequestParameters']['label'] = urlencode($keywords) . ",Google";
+		$config['otherRequestParameters']['label'] = $keywords . ",Google";
 		$return[] = array('API.getRowEvolution', $config);
 		
 		// Actions > Pages titles, standard label
@@ -68,13 +68,13 @@ class Test_Piwik_Integration_RowEvolution extends Test_Integration_Facade
 		$config['periods'] = array('day', 'week');
 		$config['otherRequestParameters']['apiModule'] = 'Actions';
 		$config['otherRequestParameters']['apiAction'] = 'getPageTitles';
-		$config['otherRequestParameters']['label'] = urlencode('incredible title 0');
+		$config['otherRequestParameters']['label'] = 'incredible title 0';
 		$return[] = array('API.getRowEvolution', $config);
 		
 		// Actions > Page titles, multiple labels
 		$config['testSuffix'] = '_pageTitlesMulti';
 		$label = urlencode('incredible title 0').','.urlencode('incredible title 2');
-		$config['otherRequestParameters']['label'] = urlencode($label);
+		$config['otherRequestParameters']['label'] = $label;
 		$return[] = array('API.getRowEvolution', $config);
 		
 		// Actions > Page URLS, hierarchical label
@@ -83,7 +83,7 @@ class Test_Piwik_Integration_RowEvolution extends Test_Integration_Facade
 		$config['otherRequestParameters']['date'] = '2010-03-01,2010-03-06';
 		$config['otherRequestParameters']['apiModule'] = 'Actions';
 		$config['otherRequestParameters']['apiAction'] = 'getPageUrls';
-		$config['otherRequestParameters']['label'] = urlencode('my>dir>'.urlencode('/page3?foo=bar&baz=bar'));
+		$config['otherRequestParameters']['label'] = 'my>dir>'.urlencode('/page3?foo=bar&baz=bar');
 		$return[] = array('API.getRowEvolution', $config);
 		
 		return $return;
diff --git a/tests/integration/TwoVisitors_TwoWebsites_DifferentDays.test.php b/tests/integration/TwoVisitors_TwoWebsites_DifferentDays.test.php
index c698a4381e83335e0050730d996d9c0dc213392b..f6ccbd2305b99af1d61ef79c5509167cc8359479 100755
--- a/tests/integration/TwoVisitors_TwoWebsites_DifferentDays.test.php
+++ b/tests/integration/TwoVisitors_TwoWebsites_DifferentDays.test.php
@@ -122,10 +122,11 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends Test_
 		// testing with empty URL and empty page title
 		$visitorA->setUrl('  ');
 		$this->checkResponse($visitorA->doTrackPageView('  '));
-	  
+		
 		// - 
 		// Second new visitor on Idsite 1: one page view 
 		$visitorB = $this->getTracker($idSite, $dateTime, $defaultInit = true);
+		$visitorB->enableBulkTracking();
 		$visitorB->setIp('100.52.156.83');
 		$visitorB->setResolution(800, 300);
 		$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(1)->getDatetime());
@@ -133,7 +134,7 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends Test_
 		$visitorB->setUserAgent('Opera/9.63 (Windows NT 5.1; U; en) Presto/2.1.1');
 		$visitorB->setUrl('http://example.org/products');
 		$visitorB->DEBUG_APPEND_URL = '&_idts='.Piwik_Date::factory($dateTime)->addHour(1)->getTimestamp();
-		$this->checkResponse($visitorB->doTrackPageView('first page view'));
+		$this->assertTrue($visitorB->doTrackPageView('first page view'));
 
 		// -
 		// Second visitor again on Idsite 1: 2 page views 2 days later, 2010-01-05
@@ -144,21 +145,22 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends Test_
 
 		$visitorB->setUrlReferrer( 'http://referer.com/Other_Page.htm' );
 		$visitorB->setUrl('http://example.org/index.htm');
-		$this->checkResponse($visitorB->doTrackPageView('second visitor/two days later/a new visit'));
+		$this->assertTrue($visitorB->doTrackPageView('second visitor/two days later/a new visit'));
 		// Second page view 6 minutes later
 		$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.1)->getDatetime());
 		$visitorB->setUrl('http://example.org/thankyou');
-		$this->checkResponse($visitorB->doTrackPageView('second visitor/two days later/second page view'));
+		$this->assertTrue($visitorB->doTrackPageView('second visitor/two days later/second page view'));
 		
 		// testing a strange combination causing an error in r3767
 		$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.2)->getDatetime());
-		$this->checkResponse($visitorB->doTrackAction('mailto:test@example.org', 'link'));
+		$this->assertTrue($visitorB->doTrackAction('mailto:test@example.org', 'link'));
 		$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.25)->getDatetime());
-		$this->checkResponse($visitorB->doTrackAction('mailto:test@example.org/strangelink', 'link'));
+		$this->assertTrue($visitorB->doTrackAction('mailto:test@example.org/strangelink', 'link'));
 		
 		// Actions.getPageTitle tested with this title
 		$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.25)->getDatetime());
-		$this->checkResponse($visitorB->doTrackPageView('Checkout / Purchasing...'));
+		$this->assertTrue($visitorB->doTrackPageView('Checkout / Purchasing...'));
+		$this->checkResponse($visitorB->doBulkTrack());
 		
 		// -
 		// First visitor on Idsite 2: one page view, with Website referer
diff --git a/tests/integration/TwoVisitsWithCustomVariables_SegmentContains.test.php b/tests/integration/TwoVisitsWithCustomVariables_SegmentContains.test.php
index 3f43d1a9c8077cc916e6dd51352d88eadfe7a8ff..194b1aaa4c13538d73cf5baa058affa02687bbf9 100644
--- a/tests/integration/TwoVisitsWithCustomVariables_SegmentContains.test.php
+++ b/tests/integration/TwoVisitsWithCustomVariables_SegmentContains.test.php
@@ -24,11 +24,11 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains extend
 		$api = array('Actions.getPageUrls', 'Actions.getPageTitles', 'VisitsSummary.get');
 		$segmentsToTest = array(
 		// array( SegmentString , TestSuffix , Array of API to test)
-			array("pageTitle=@".urlencode('*_)%'), '_SegmentPageTitleContainsStrangeCharacters', array('Actions.getPageTitles', 'VisitsSummary.get')),
-			array("pageUrl=@".urlencode('user/profile'), '_SegmentPageUrlContains', $api),
-			array("pageTitle=@Profile%20pa", '_SegmentPageTitleContains', $api),
+			array("pageTitle=@*_)%", '_SegmentPageTitleContainsStrangeCharacters', array('Actions.getPageTitles', 'VisitsSummary.get')),
+			array("pageUrl=@user/profile", '_SegmentPageUrlContains', $api),
+			array("pageTitle=@Profile pa", '_SegmentPageTitleContains', $api),
 			array("pageUrl!@user/profile", '_SegmentPageUrlExcludes', $api),
-			array("pageTitle!@Profile%20pa", '_SegmentPageTitleExcludes', $api),
+			array("pageTitle!@Profile pa", '_SegmentPageTitleExcludes', $api),
 		);
 		
 		foreach($segmentsToTest as $segment) 
diff --git a/tests/integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData.test.php b/tests/integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData.test.php
index e27ce6a3539582345427c688ff2ca6973d3fd2d1..a72f0613944de4b1bb2b1bf32ae8ab58a2960ad3 100755
--- a/tests/integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData.test.php
+++ b/tests/integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData.test.php
@@ -24,7 +24,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoal
         // Segment matching ALL
         // + adding DOES NOT CONTAIN segment always matched, to test this particular operator
 		$resolution = $this->width.'x'.$this->height;
-        $segment = 'resolution=='.$resolution.';customVariableName1!@randomvalue%20does%20not%20exist';
+        $segment = 'resolution=='.$resolution.';customVariableName1!@randomvalue does not exist';
 
 		return array(
 			array($apiToCall, array('idSite' => 'all', 'date' => $this->dateTime, 'periods' => array('day', 'week'),
diff --git a/tests/integration/proxy-piwik.php b/tests/integration/proxy-piwik.php
index d0b7338f71e1b532cd3fcbd8ba56d47fb21dd999..e355081a19a4db5fccd1fccde0947edf64fb7a2f 100644
--- a/tests/integration/proxy-piwik.php
+++ b/tests/integration/proxy-piwik.php
@@ -27,6 +27,13 @@ Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled'] = array();
 // Do not run scheduled tasks during tests
 Piwik_Config::getInstance()->Tracker['scheduled_tasks_min_interval'] = 0;
 
+// if nothing found in _GET/_POST and we're doing a POST, assume bulk request. in which case, we have to bypass
+// authentication
+if (empty($_GET) && empty($_POST) && $_SERVER['REQUEST_METHOD'] == 'POST')
+{
+	Piwik_Config::getInstance()->Tracker['tracking_requests_require_authentication'] = 0;
+}
+
 // Tests can force the use of 3rd party cookie for ID visitor
 if(Piwik_Common::getRequestVar('forceUseThirdPartyCookie', false) == 1)
 {