Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

397 lines (316 sloc) 13.955 kB


This is the Factual-supported Java driver for Factual's public API.


Maven users

The driver is in Maven Central, so you can just add this to your Maven pom.xml:


Non Maven users

You can download the individual driver jar, and view the pom.xml file, here: Driver download folder

The pom.xml tells you what dependencies you'll need to plug into your project to get the driver to work (see the section).

Basic Design

The driver allows you to create an authenticated handle to Factual. With a Factual handle, you can send queries and get results back.

Queries are created using the Query class, which provides a fluent interface to constructing your queries.

Results are returned as the JSON returned by Factual. Optionally, there are JSON parsing conveniences built into the driver.


// Create an authenticated handle to Factual
Factual factual = new Factual(MY_KEY, MY_SECRET);

Simple Query Example

// Print 3 random records from Factual's Places table:
  factual.fetch("places", new Query().limit(3)));

Full Text Search

// Print entities that match a full text search for Sushi in Santa Monica:
    factual.fetch("places", new Query().search("Sushi Santa Monica")));

Geo Filters

You can query Factual for entities located within a geographic area. For example:

// Build a Query that finds entities located within 5000 meters of a latitude, longitude
new Query().within(new Circle(34.06018, -118.41835, 5000));

Results sorting

You can have Factual sort your query results for you, on a field by field basis. Simple example:

// Build a Query to find 10 random entities and sort them by name, ascending:
new Query().limit(10).sortAsc("name");

You can specify more than one sort, and the results will be sorted with the first sort as primary, the second sort or secondary, and so on:

// Build a Query to find 20 random entities, sorted ascending primarily by region, then by locality, then by name:
q = new Query()

Limit and Offset

You can use limit and offset to support basic results paging. For example:

// Build a Query with offset of 150, limiting the page size to 10:
new Query().limit(10).offset(150);

Field Selection

By default your queries will return all fields in the table. You can use the only modifier to specify the exact set of fields returned. For example:

// Build a Query that only gets the name, tel, and category fields:
new Query().only("name", "tel", "category");

All Top Level Query Parameters

Parameter Description Example
filters Restrict the data returned to conform to specific conditions. q.field("name").beginsWith("Starbucks")
include count Include a count of the total number of rows in the dataset that conform to the request based on included filters. Requesting the row count will increase the time required to return a response. The default behavior is to NOT include a row count. When the row count is requested, the Response object will contain a valid total row count via .getTotalRowCount(). q.includeRowCount()
geo Restrict data to be returned to be within a geographical range based. (See the section on Geo Filters)
limit Maximum number of rows to return. Default is 20. The system maximum is 50. For higher limits please contact Factual, however consider requesting a download of the data if your use case is requesting more data in a single query than is required to fulfill a single end-user's request. q.limit(10)
search Full text search query string. Find "sushi":"sushi")

Find "sushi" or "sashimi":"sushi, sashimi")

Find "sushi" and "santa" and "monica":"sushi santa monica")

offset Number of rows to skip before returning a page of data. Maximum value is 500 minus any value provided under limit. Default is 0. q.offset(150)
only What fields to include in the query results. Note that the order of fields will not necessarily be preserved in the resulting JSON response due to the nature of JSON hashes. q.only("name", "tel", "category")
sort The field (or secondary fields) to sort data on, as well as the direction of sort. Supports $distance as a sort option if a geo-filter is specified. Supports $relevance as a sort option if a full text search is specified either using the q parameter or using the $search operator in the filter parameter. By default, any query with a full text search will be sorted by relevance. Any query with a geo filter will be sorted by distance from the reference point. If both a geo filter and full text search are present, the default will be relevance followed by distance. q.sortAsc("name").sortDesc("$distance")

Row Filters

The driver supports various row filter logic. Examples:

// Build a query to find places whose name field starts with "Starbucks"
new Query().field("name").beginsWith("Starbucks");

// Build a query to find places with a blank telephone number
new Query().field("tel").blank();

Supported row filter logic

Predicate Description Example
equal equal to q.field("region").equal("CA")
notEqual not equal to q.field("region").notEqual("CA")
search full text search q.field("name").search("fried chicken")
in equals any of q.field("region").in("MA", "VT", "NH", "RI", "CT")
notIn does not equal any of q.field("locality").notIn("Los Angeles")
beginsWith begins with q.field("name").beginsWith("b")
notBeginsWith does not begin with q.field("name").notBeginsWith("star")
beginsWithAny begins with any of q.field("name").beginsWithAny("star", "coffee", "tull")
notBeginsWithAny does not begin with any of q.field("name").notBeginsWithAny("star", "coffee", "tull")
blank is blank or null q.field("tel").blank()
notBlank is not blank or null q.field("tel").notBlank()
greaterThan greater than q.field("rating").greaterThan(7.5)
greaterThanOrEqual greater than or equal to q.field("rating").greaterThanOrEqual(7.5)
lessThan less than q.field("rating").lessThan(7.5)
lessThanOrEqual less than or equal to q.field("rating").lessThanOrEqual(7.5)


Queries support logical AND'ing your row filters. For example:

// Build a query to find entities where the name begins with "Coffee" AND the telephone is blank:
Query q = new Query();

Note that all row filters set at the top level of the Query are implicitly AND'ed together, so you could also do this:

new Query()


Queries support logical OR'ing your row filters. For example:

// Build a query to find entities where the name begins with "Coffee" OR the telephone is blank:
Query q = new Query();

Combined ANDs and ORs

You can nest AND and OR logic to whatever level of complexity you need. For example:

// Build a query to find entities where:
// (name begins with "Starbucks") OR (name begins with "Coffee")
// OR
// (name full text search matches on "tea" AND tel is not blank)
Query q = new Query();


The driver fully supports Factual's Crosswalk feature, which lets you "crosswalk" the web and relate entities between Factual's data and that of other web authorities.

(See the Crosswalk Blog for more background.)

Simple Crosswalk Example

// Get all Crosswalk data for a specific Places entity, using its Factual ID:
  new CrosswalkQuery().factualId("97598010-433f-4946-8fd5-4a6dd1639d77"));

Crosswalk Filter Parameters

Filter Description Example
factualId A Factual ID for an entity in the Factual places database q.factualId("97598010-433f-4946-8fd5-4a6dd1639d77")
limit A Factual ID for an entity in the Factual places database q.limit(100)
namespace The namespace to search for a third party ID within. A list of currently supported services is here. q.namespace("foursquare")
namespaceId The id used by a third party to identify a place. q.namespaceId("443338")
only A Factual ID for an entity in the Factual places database q.only("foursquare", "yelp")

NOTE: although these parameters are individually optional, at least one of the following parameter combinations is required:

  • factualId
  • namespace and namespaceId

More Crosswalk Examples

// Get Loopt's Crosswalk data for a specific Places entity, using its Factual ID:
CrosswalkResponse resp = factual.fetch("places",
    new CrosswalkQuery()

// Get all Crosswalk data for a specific Places entity, using its Foursquare ID
CrosswalkResponse resp = factual.fetch("places",
    new CrosswalkQuery()


The driver fully supports Factual's Resolve feature, which lets you start with incomplete data you may have for an entity, and get potential entity matches back from Factual.

Each result record will include a confidence score ("similarity"), and a flag indicating whether Factual decided the entity is the correct resolved match with a high degree of accuracy ("resolved").

For any Resolve query, there will be 0 or 1 entities returned with "resolved"=true. If there was a full match, it is guaranteed to be the first record in the JSON response.

(See the Resolve Blog for more background.)

Simple Resolve Examples

The resolves method gives you all possible matches:

// Get all entities that are possibly a match
ReadResponse resp = factual.resolves(new ResolveQuery()
  .add("name", "Buena Vista")
  .add("latitude", 34.06)
  .add("longitude", -118.40));

The resolve method gives you the one full match if there is one, or null:

// Get the entity that is a full match, or null:
Map rec = factual.resolve(new ResolveQuery()
.add("name", "Buena Vista")
.add("latitude", 34.06)
.add("longitude", -118.40));

Exception Handling

If Factual's API indicates an error, a FactualApiException unchecked Exception will be thrown. It will contain details about the request you sent and the error that Factual returned.

Here is an example of catching a FactualApiException and inspecting it:

Factual badness = new Factual("BAD_KEY", "BAD_SECRET");
try{"places", new Query().field("country").equal(true));
} catch (FactualApiException e) {
  System.out.println("Requested URL: " + e.getRequestUrl());
  System.out.println("Error Status Code: " + e.getResponse().statusCode);
  System.out.println("Error Response Message: " + e.getResponse().statusMessage);

More Examples

For more code examples:

  • See the standalone demos in src/test/java/com/factual/demo
  • See the integration tests in src/test/java/com/factual/
Jump to Line
Something went wrong with that request. Please try again.