Mar 112015
Article PHP

Factual is a geographical database of places, services, points of interest and landmarks that is made available for free through their Application Programming Interface (API)

This post explains how to use a PHP library to access this database, the characteristics of the service, and the details on the content that can be retrieved from the Factual API.

Obtaining access keys

Requests to the Factual API must be authenticated with OAuth, and thus the first step in the implementation is to register and obtain a pair of OAuth access keys.

To do this, go to the Factual website, and click on GET STARTED > GET API KEY


The sign-up form that is displayed must be filled and submitted.

Once registered, follow the steps indicated, until the authorization keys are presented in the screen:


In this screenshot, we can see:

  • The access level is free (unverified). This access level allows a maximum 100 daily queries. It can be upgraded to free (verified), with a limit of 10,000 daily queries. The upgrade request form asks for credit card details , even if the service is still free.
  • In the request for API keys, access to the service can be restricted to a set of IPs and domains, if desired, to prevent unauthorized use of the service.
  • Also in the request for API keys, the categories of interest need to be specified.
  • Finally, at the bottom of the screen there are links for the download of API libraries for commonly used languages, including PHP.

Downloading the library and verifying the installation

The PHP library download link points to the Factual php driver page at The package installation file (202KB) can be downloaded from this page.

The package is uncompressed into a directory that contains all the scripts in the library. There is one “test.php” script among them, to check that the requirements of the library are met by the system. The script is run with the previously obtained OAuth keys as arguments:


Testing Factual
PHP verison v5+                         Pass
No classname conflicts                  Pass
Parse INI File Function                 Pass
SPL is loaded                           Pass
curl is loaded                          Pass
Creating Query object                   Pass
Setting query parameter                 Pass
API Connection                          Pass
Limit/Filter/Sort                       Pass
Unicode Filter                          Fail
Punctuation                             Pass
Quotes Test                             Pass
'In' Filter                             Pass
Multi Filter                            Pass
Geo Search                              Pass
Multi Country Search                    Pass
Response URL                            Pass
Request Table Name                      Pass
Response Headers                        Pass
Response Code                           Pass
Total Row Count                         Pass
Submit Test                             Fail	You do not have access to the requested resource.
Reverse Geocoder                        Pass
Creating Query object                   Pass
Resolve Endpoint                        Pass
Crosswalk Endpoint                      Pass
Schema Endpoint                         Pass
Diffs Test                              Pass	Not Authorized [403], but that's expected'
Checking Argentina                      Pass
Checking Australia                      Pass
Checking Austria                        Pass
Checking Belgium                        Pass
Checking Brazil                         Pass
Checking Canada                         Pass
Checking Chile                          Pass
Checking China                          Pass
Checking Columbia                       Pass
Checking Croatia (Hrvatska)             Pass
Checking Czech Republic                 Pass
Checking Denmark                        Pass
Checking Egypt                          Pass
Checking Finland                        Pass
Checking France                         Pass
Checking Germany                        Pass
Checking Greece                         Pass
Checking Hong Kong                      Pass
Checking Hungary                        Pass
Checking India                          Pass
Checking Indonesia                      Pass
Checking Ireland                        Pass
Checking Israel                         Pass
Checking Italy                          Pass
Checking Japan                          Pass
Checking Luxembourg                     Pass
Checking Malaysia                       Pass
Checking Mexico                         Pass
Checking Netherlands                    Pass
Checking New Zealand                    Pass
Checking Norway                         Pass
Checking Peru                           Pass
Checking Philippines                    Pass
Checking Poland                         Pass
Checking Portugal                       Pass
Checking Puerto Rico                    Pass
Checking Russia                         Pass
Checking Singapore                      Pass
Checking South Africa                   Pass
Checking South Korea                    Pass
Checking Spain                          Pass
Checking Sweden                         Pass
Checking Switzerland                    Pass
Checking Taiwan                         Pass
Checking Thailand                       Pass
Checking Turkey                         Pass
Checking United Kingdom                 Pass
Checking United States                  Pass
Checking Venezuela                      Pass
Checking Vietnam                        Pass

Simple query to the Factual database

Let’s write a small script to issue a simple query to the Factual DB. The script begins with the loading of the main “Factual.php” library. Next, an instance of the “Factual” class is created with the OAuth credentials:

 $factual = new Factual("FACTUAL-KEY","FACTUAL-SECRET");

Then, an instance of class “FactualQuery” is created. This object will be used to specify the query criteria to be applied:

 $query    = new FactualQuery;
 $query->at(new FactualPoint($latitude,$longitude));

In this example, the query will ask for places near a given geographical point specified by its ($latitude,$longitude) coordinates. Additionally, the result will be sorted by distance to that point.

Finally, the API is accessed with a call to the fetch() method of the $factual object, with the entity type requested (“places”) and the query criteria in the $query object passed as arguments.

  $res = $factual->fetch("places", $query);
  $numresults = $res->getTotalRowCount();
  print_r $res->getData();

If the values of $latitude, $longitude are set to (40.759139, -73.985179) (the coordinates of Times Square in New York, according to Google Maps), an array of twenty entries is returned and printed to stdout (only the first entry is shown here):

 [0] => Array
     [factual_id] => 90109b13-65fb-4194-8944-0aa0f01bdef3
     [latitude] => 40.75917
     [longitude] => -73.985211
     [$distance] => 4.3756595
     [name] => Blue Fin
     [address] => 1567 Broadway

     [category_ids] => Array
         [0] => 364
         [1] => 366
         [2] => 348
     [category_labels] => Array
         [0] => Array
             [0] => Social
             [1] => Food and Dining
             [2] => Restaurants
             [3] => Seafood

         [1] => Array

         [2] => Array


     [country] => us
     [region] => NY
     [locality] => New York
     [neighborhood] => Array
         [0] => Midtown
         [1] => Midtown West
         [2] => Theatre District
     [postcode] => 10036

     [email] =>
     [tel] => (212) 918-1400
     [tel_normalized] => 2129181400
     [website] =>

     [hours] => Array
         [monday] => Array
             [0] => Array
                 [0] => 7:00
                 [1] => 23:00

         [tuesday] => Array

     [hours_display] => Mon 7:00 AM-11:00 PM; Tue-Thu 7:00 AM-11:30 PM; Fri-Sat 7:00 AM-11:59 PM;
                    Sun 7:00 AM-11:00 PM

 [19] => Array

As can be seen in the example above, the information retrieved from each entry includes:

  • A unique entry identifier (“factual_id”)
  • latitude and longitude where the place is located, and distance from the point specified in the search
  • categories where the entry belongs, in a hierarchical structure. In the example result above, category id 364 is Social > Food and Dining > Restaurants > Seafood.
  • Additional information can optionally be retrieved for each entry. In the example, the name of the restaurant, address, phone number, email and opening hours are included.

Search criteria and parameters

The simple search example analyzed so far, requested places located around a given point identified by its (lat,lon) coordinates, sorted by distance. These criteria are defined by calling the at() method in the FactualQuery class.

There are other methods in the FactualQuery class that can be used to use other search criteria, such as:

  • public function within($geo) – Restricts the search to places located inside the area specified in  the $geo argument (a circle or rectangle)
  • public function search($term) – Requests a “full text search”
  • public function limit($limit) – Establish a limit on the number of records retrieved. The default value if not specified is 20, and the maximum that can be requested is 50.
  • public function offset($offset) – Establish the first record to be retrieved from the full result set (used for pagination)
  • public function includeRowCount() – Include in the response the total number of records matching the search criteria (with some performance penalty)
  • public function select($fields) – Specify with fields are to be returned for each record. The default is to return all fields in the “places” table (See “Places table Schema” below)
  • public function field($field) – Specify search criteria on the values of a given field (See Full Text Search examle below)
  • public function sortAsc($field), public function sortDesc($field) – Sorts in ascending or descending order of the values of the field passed as argument.


Full Text Search

  $query = new FactualQuery;
  $query->search("Sushi Santa Monica");  //searches the text in all fields of the places table
  $query->field("country")->equal("US"); //limit results to places in the US
  $res = $factual->fetch("places", $query);

Search up to ten results inside a 5Km radius

	$query = new FactualQuery;
	$query->within(new FactualCircle(34.06018, -118.41835, 5000)); // 5 Km radius circle
	$query->limit(10); //Limita a 10 resultados
	$query->sortAsc("\$distance"); //sort ascending by distance to the given coordinates
	$res = $factual->fetch("places", $query);

Places table schema

To be able to specify search criteria base on the values of fields in the places table, we must know the names of the fields. A query for the table schema can be issued to the API, as follows:

  $res = $factual->schema("places");

This returns an array with the definition of all 287 fields in the places table:

    [name] => FactualColumnSchema Object
            [name] => name
            [description] => Business/POI name
            [faceted] =>
            [sortable] => 1
            [label] => Name
            [datatype] => String
            [searchable] => 1

    [address] => FactualColumnSchema Object
            [name] => address
            [description] => Address number and street name
            [faceted] =>
            [sortable] => 1
            [label] => Address
            [datatype] => String
            [searchable] => 1


Each of the records in the Factual database is assigned one or several categories: Restaurants, Museums, Pharmacies,…

Searching for records in a given category or categories is probably one of the most commonly used search criteria. The category filter is applied by restricting the results to those with specific values in the “category_ids” field, as in the following example:


Here, $category is the numeric identifier of the category of interest ( 80: Pharmacies, 311: Museums,…)

Categories in Factual are hierarchically structured: Categoría 80 (Pharmacies) belongs to category 62 (Health), together with categories 69 (Dentists), 74 (Hospitals), etc…

The complete list of the more than 400 categories can be found at:

The list can be downloaded in JSON format from that same page


Factual API Documentation

 Posted by at 9:57 am

 Leave a Reply