Alert: We are sun-setting access to the V3 APIs (Read, Schema, Facets, Submit). Learn more.

Authentication

Request Signing

OAuth access

Version 3 of the Factual API uses “0-legged” OAuth to authenticate requests.

In a 0-legged OAuth (sometimes called “1-legged”) transaction, your key and secret are used to “sign” your API request (in this case, using HMAC-SHA1) before sending it to Factual. No third party token/secret is involved. The signing process is designed to guarantee that your API key can only be used by your own application. So long as you do not share the secret portion of your API key, other users will not be able to “borrow” your API throttle allocation by simply presenting API calls with your key. If you have any custom API limits from Factual, you MUST OAuthenticate your requests to take advantage of your limits.

While the OAuth signing process can be tedious, using one of our language-specific drivers is the easiest way to get started. Each driver transparently handles the OAuth signing for you. Alternatively, If you are just making a handful of requests and don’t want to install a driver, or if you just want some insight into the signing process to help “roll your own”, a simple alternative is to use Apigee.


Arcane details for those that roll their own (skip to throttle limits for the rest of you):

If you opt to write your own OAuthentication code, you should note that, althought the OAuth RFC allows for passing the OAuthentication params via the Authorization header, within a POST request body, or as GET params (in that specific order of preference); we’ve found that a number of third party libraries fail to properly sign requests using GET params. As such, this method is not supported by the Factual API. Also note that, if you sign a POST response to Factual, you should be using a content-type of application/x-www-form-urlencoded and incorporating any form parameters in your basestring.


Non-OAuth access

Factual accepts unsigned requests using the KEY parameter. Unsigned requests are restricted to 100 requests per day, and are provided purely to aid in debugging for cases where you suspect that your OAuth code may be failing.

Throttle limits

All of Factual’s free access levels are listed here.

Basics

Factual sets limits based on a combination of the table you are accessing, the method of access, and the access level of your account.

For example, if you are making a read call to the US Places table using a basic (free) Factual API developer account, you are limited to 10,000 calls per day and 500 calls per minute. This is the level afforded to OAuthenticated requests. As noted, above, non-OAuthenticated API calls are restricted to 100 per day, regardless of the table.

Daily API limits are based on rolling 24 hour periods, meaning that your limits are not reset at the end of calendar days, but rather once the number of API calls you have made for the previous 24 hour period is below your account’s limit.

What is limited?

Factual enforces the following limits on API requests:

  • maximum requests per day
  • maximum requests per minute (a.k.a. Burst limit)

For read API calls, Factual also enforces the following:

  • maximum rows returned per page (a.k.a. Page limit). This is the largest value that the limit parameter in your requests can take.
  • maximum rows paged into (a.k.a. Row limit). The offset + limit parameters for your requests cannot exceed this value.


What is my current limit?

You can view the remaining portion of your API limits in real-time within any OAuthenticated API request you make. The HTTP response headers returned for each request will contain the X-Factual-Throttle-Allocation header like so:

X-Factual-Throttle-Allocation:{"burst":"0%","daily":"0%"}

The allocation information is decoded as follows:

  • burst shows the percentage of your per minute limit that you have consumed for previuos minute.
  • daily shows the percentage of your per day limit that you have consumed for the previous rolling 24 hour period.


Error responses

You may encounter some of the following throttle related errors as you navigate the API for the reasons explained:

HTTP/1.1 401 API is secure. Needs security Credentials

This error means that you have neither provided an API key using the KEY parameter, nor OAuthenticated your request. The response will be accompanied by the following JSON within the response body:

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "Unsigned requests require the KEY parameter. Please see documentation at http://developer.factual.com."
}


HTTP/1.1 403 Forbidden

This error means that you have exceeded either the daily or burst limit for your account or that you have attempted to use an API method or access a table which you do not have access to. It will be accompanied with a JSON response like:

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "You have exceeded the throttle limit for this request.",
  "data": "N D I 100 122"
}

or

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "You do not have access to the requested resource."
}

You may also receive a 403 error if your OAuth signing process fails to produce a valid OAuth signature. The easiest way to avoid this error is to use a Factual driver.

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "Your OAuth signature could not be verified."
}


HTTP/1.1 416 Requested Range Not Satisfiable

This error means that you have either exeeded the page or row limit for your account. You’ll receive one of the following JSON responses:

Row limit exceeded:

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "Illegal row access attempt. Please contact Factual if you require premium access."
}

Page limit exceeded:

{
  "version": 3,
  "status": "error",
  "error_type": "Auth",
  "message": "Illegal value for 'limit'. Please contact Factual if you require premium access."
}

Premium access

If you find yourself exceeding API limits with regularity, you should either decrease your API volume (by putting your access threads to sleep between requests) or contact sales at Factual in regards to premium API access. We may disable your API key if it consistently exceeds the API limits.

Note that API access is not granted for the purpose of downloading data. Paging limits are set to accomodate use cases where the results of your response optimally can service a single user of your application’s search request. Download licensing is available for use cases where you need larger subsets of the data at once.

Read

Alert: We are sun-setting access to the V3 APIs (Read, Schema, Facets, Submit). Learn more.

Use the read call to query from tables using any combination of full-text, parametric, or geo (lat/lng) searches.

Syntax

Reads are always implemented as a HTTP GET requests.

http://api.v3.factual.com/t/[table_id]/[factual_id]?select=[field names]&q=[search terms]&geo=[geo filter]&filters=[row filter]&threshold=[confident|default|comprehensive]&offset=[offset]&limit=[limit]&include_count=[true/false]&sort=[column:asc/desc|blending JSON]

  • To query for sets of data, specify any combination of full-text-search (q), geo, or row filters.
  • Offset and limit are supported for paging.
  • If you require a count of the total rows fitting our query, and don’t mind incurring the performance cost, specify include_count=true.

Examples

Find rows in the global places database in the United States:

http://api.v3.factual.com/t/places?filters={"country":"US"}

Get the row for Factual:

http://api.v3.factual.com/t/places/03c26917-5d66-4de9-96bc-b13066173c65

Find rows in the restaurant database whose names begins with the letters star (case insensitive) and return both the data and a total count of the matched rows:

http://api.v3.factual.com/t/restaurants-us?filters={"name":{"$bw":"star"}}&include_count=true

Do a full-text search of the restaurant database for rows that match the word coffee and the phrase los angeles:

http://api.v3.factual.com/t/restaurants-us?q=Coffee,"Los Angeles"

To support paging in your app, return rows 20-40 of the full-text search result above:

http://api.v3.factual.com/t/restaurants-us?q=Coffee,"Los Angeles"&offset=20&limit=20

Return rows from the global places database with a name equal to “Stand” within 5000 meters of the specified lat/lng:

http://api.v3.factual.com/t/places?filters={"name":"Stand"}&geo={"$circle":{"$center":[34.06018, -118.41835],"$meters": 2500}}

Parameters

Parameter Name Description Examples
filters Restrict the data returned to conform to specific conditions, e.g., “parametric search”. Documentation on filters is here.
filters={"last_name":"Smith"}
geo Restrict data to be returned to be within a geographical range based. Documentation on geo filters is here. Maximum value for $meters is 20000.
geo={"$circle":{"$center":[35.001,-18.221],"$meters": 5000}}
include_count Include a count of the total number of row counts returned. Requesting the row count will increase the time required to return a response. The default behavior is to NOT include a row count.
include_count=true
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.
limit=20
KEY Your API Key. The KEY parameter is only required for non-OAuthenticated requests, which are supported only as a debugging convenience.
KEY=Q3iBjOES4h1mZcNCZyBmQFcYREdjylIucvB3r2oI
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.
offset=100
q Full text search query string. More information
q=Coffee Santa Monica
q=Coffee,Tea
q="Father's Office"
select Only return specified fields
select=name,address
sort The field (or secondary fields) to sort data on, as well as the direction of sort. Alternately, a JSON map of blended sorting coefficients.

Sort options:
  • $distance supported as a sort option if a point/radius geo-filter is specified.
  • $relevance supported 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.
  • Place Rank is available for sorting places by popularity. Place Rank should be sorted descending.
By default, data is sorted by distance and Place Rank blended at 1:1.
sort=name:asc
sort=region:asc,name:asc
sort=$distance
sort=placerank:desc
sort={"placerank":100,"distance":50}
threshold Set a threshold for filtering on the level of confidence that Factual has that places exists. Valid values are confident, default, or comprehensive. If the threshold parameter is not specified, it uses the value default.
threshold=confident

The q parameter enables full-text search across all the fields of a Factual table. Unlike row filters (using comparison operators like equals or begins with), full-text search more loosely matches the provided search terms against rows in the Factual table. For example, a search for Bar may bring up rows that include the term bar, bars, barred or (potentially) beer.

Examples:
    q=hot dogs (Must have the word hot and the word dogs, but not necesarily in that order, or in a single field.)
    q=sushi, sashimi (Must have either the word sushi or or the word sashimi.)
    q="santa monica" (Must have the phrase santa monica with both terms in that order, in a single field.)

Row filters

Row filters enable parametric searches. Row filters enable you to selectively filter read API requests to only data which fits constraints defined in your filters query parameter, similar to the where clause of a SQL query. Factual row filters are implemented as JSON.

Comparison operators

Comparison operators map field names to a map of an operator, and the appropriate parameters for the operator.

Operator Description Operates on Datatype of Field… Examples
$blank test to see if a value is blank (or null) text,numeric find rows with missing telephone numbers:
filters={"tel":{"$blank":true}}
find rows with non-blank telephone numbers:
filters={"tel":{"$blank":false}}
$bw begins with text
filters={"name":{"$bw":"b"}}
$bwin begins with any of text
filters={"name":{"$bwin":["lt", "sg", "cpt"]}}
$eq equal to text,numeric
filters={"region":{"$eq":"CA"}} or {"region":"CA"} (acceptable shorthand)
$excludes array does not include multivalued text, numeric
filters={"category_ids":{"$excludes":9}}
$excludes_any array does not include any of multivalued text, numeric
filters={"category_ids":{"$excludes_any":[318,321]}}
$gt greater than numeric
filters={"rating":{"$gt":7.5}}
$gte greater than or equal to numeric
filters={"rating":{"$gte":7.5}}
$in equals any of text,numeric
filters={"region":{"$in":["MA","VT","NH","RI","CT"]}}
$includes array includes multivalued text, numeric
filters={"category_ids":{"$includes":10}}
$includes_any array includes any of multivalued text, numeric
filters={"category_ids":{"$includes_any":[10,100]}}
$lt less than numeric
filters={"age":{"$lt":50}}
$lte less than or equal to numeric
filters={"age":{"$lte":7.5}}
$nbw does not begins with text
filters={"name":{"$nbw":"Mr."}}
$nbwin does not begin with any of text
filters={"class":{"$nbwin":["beginner", "intermediate"]}}
$neq not equal to text,numeric
filters={"region":{"$neq":"CA"}}
$nin does not equal any of text,numeric
filters={"locality":{"$nin":["Los Angeles","Santa Monica"]}}
$search full-text search a field text
filters={"name":{"$search":"Charles"}}
$nsearch full-text search a field for words to exclude text
filters={"name":{"$nsearch":"Dogs"}}

Logical operators

Multiple row filters can be logically combined using the $and and $or operators.

AND

The $and operator accepts a JSON array of row filters. For example:

filters={"$and":[{"name":{"$bw":"McD"},"category_ids":{"$includes":338}}]}

will return rows where both name begins with McD AND category_ids includes 338 (Food & Dining):

OR

Similar to the $and operator, the $or operator accepts a JSON array of row filters:

filters={"$or":[{"tel":{"$blank":true}},{"tel":{"$bw":"(212)"}}]}

will return rows where either tel is either blank OR starts with (212):

Combined ANDs and ORs

$and and $or can be combined:

filters={"$and":[{"first_name":"Suzy"},{"$or":[{"last_name":"Q"},{"last_name":{"$blank":true}}]}]}

Full-text-search operator

The $search operator enables making a full-text search on a single field. When using the $search operator.

Geo filters

Geo filters enable read requests to return only data bounded by specified geographic coordinates for tables that support it. The Factual read and facet API methods accept geo filters specified in a JSON format (described below) using the geo parameter.

Examples:

Find all rows within a specified radius of a geo point:

http://api.v3.factual.com/t/restaurants-us?geo={"$circle":{"$center":[34.06021,-118.41828],"$meters": 5000}}

  • When using a point/radius geo filter, distance (in meters) from the point will be returned in the response packet under the $distance key. This distance is calculated as the crow flies.
  • Point/radius queries are implemented as a point at the center of a square with sides twice the radius.
  • The radius for point/radius queries should be limited to 25 km.

Find the closest places to a specified point:

http://api.v3.factual.com/t/places?geo={"$point":[34.06021,-118.41828]}

  • Note, the above method will limit results to within 500 meters of the specified point.

Find places within a rectangular bounding box (points are [top,left],[bottom,right]):

http://api.v3.factual.com/t/places?geo={"$within":{"$rect":[[34.06110,-118.42283],[34.05771,-118.41399]]}}

A couple general notes on geo filters:

  • Points are always ordered as [latitude, longitude].
  • Distance is specified in meters.
  • The maximum area that any geo query can encompass is 625 km2

Schema

Alert: We are sun-setting access to the V3 APIs (Read, Schema, Facets, Submit). Learn more.

This method returns the schema for a Factual table view. The schema includes:

  • Table view meta-data: name, description, list of fields, schema version, and whether or not the table is geo and/or full-text searchable.
  • Field meta-data (for each field): name, optional aliase(s), datatype, whether the field is supports full-text search, facets or writes.

Syntax

A GET request with the proper table view Id will fetch the table schema:

http://api.v3.factual.com/t/[view id]/schema

Examples

Get the schema of the Global places table:

http://api.v3.factual.com/t/places/schema

Get the schema of the US restaurants table:

http://api.v3.factual.com/t/restaurants-us/schema

Parameters

Parameter Name Description Example
KEY Your API Key. The KEY parameter is only required for non-OAuthenticated requests, which are supported only as a debugging convenience.
KEY=Q3iBjOES4h1mZcNCZyBmQFcYREdjylIucvB3r2oI

Facets

Alert: We are sun-setting access to the V3 APIs (Read, Schema, Facets, Submit). Learn more.

The facets API call enables your to return row counts for Factual tables, grouped by facets of the data. For example, you may want to query all businesses within 1 mile of a location and for a count of those businesses by category.

Not all fields are configured to return facet counts. To determine what fields you can return facets for, use the schema call. The faceted attribute of the schema will let you know.

Syntax

Facet calls are performed as HTTP GET requests:

http://api.v3.factual.com/t/[table_name]/facets?select=[field1,field2...]&q=[full-text-search]&filters=[JSON row filters]&geo=[JSON geo-filter]&min_count=[minimum count to show]

Examples

Return a count of Starbucks by country:

http://api.v3.factual.com/t/places/facets?select=country&q=starbucks

Count Starbucks in the US by city and state:

http://api.v3.factual.com/t/places/facets?select=locality,region&q=starbucks&filters={"country":"US"}

Count businesses by category id 5 km around Factual:

http://api.v3.factual.com/t/places/facets?select=category_ids&geo={"$circle":{"$center":[34.06018, -118.41835],"$meters": 5000}}

Parameters

Parameter Name Description Examples
filters Restrict the data returned to conform to specific conditions, e.g., “parametric search”. Documentation on row filters is here.
filters={"last_name":"Smith"}
geo Restrict data to be returned to be within a geographical range based. Documentation on geo filters is here. Maximum value for $meters is 20000.
geo={"$circle":{"$center":[35.001,-18.221],"$meters": 5000}}
KEY Your API Key. The KEY parameter is only required for non-OAuthenticated requests, which are supported only as a debugging convenience.
KEY=Q3iBjOES4h1mZcNCZyBmQFcYREdjylIucvB3r2oI
include_count Include a count of the total number of row counts returned. Requesting the row count will increase the time required to return a response. The default behavior is to NOT include a row count.
include_count=true
limit Maximum number of facet counts to return in the response. Default is 20. Max is 50.
limit=20
min_count For each facet count, the minimum count it must show in order to be returned in the response. Must be zero or greater. The default is 1.
min_count=1
q Full text search query string. Documentation for search filters are here
q=Coffee Santa Monica
q=Coffee,Tea
threshold Set a threshold for filtering on the level of confidence that Factual has that places exists. Valid values are confident, default, or comprehensive. If the threshold parameter is not specified, it uses the value default.
threshold=confident

Aside from min_count, all of the parameters above are identical to the same-named parameters in the read
API call.

Resolve


Use Resolve to enrich your data and match it against Factual’s. For simply mapping your data to Factual’s, see Match.

Syntax

Pass what you know to Factual Resolve via a GET request, with the attributes included as JSON-encoded key/value pairs.

http://api.v3.factual.com/t/[view name]/resolve?values=[JSON hash]&debug=[true|false]

Examples

Find the entity named “McDonald’s” with an address combination

http://api.v3.factual.com/t/places-us/resolve?values={"name":"McDonalds","address":"10451 Santa Monica Blvd","region":"CA","postcode":"90025"}

Find the entity named “McDonald’s” with only a specified lat/lng

http://api.v3.factual.com/t/places-us/resolve?values={"name":"McDonald's","longitude":-118.472964,"latitude":34.033996}

Parameters

Parameter Name Description Examples
values* A JSON hash of key value pairs representing the data that will be matched against.
values={"name":"McDonalds","address":"10451 Santa Monica Blvd","locality":"Los Angeles","region":"CA"}
values={"name":"McDonalds","latitude":34.05671,"longitude":-118.42586}
debug Optionally enables toggling on a debug mode for Resolve that returns all potential candidates for resolution and meta-data regarding resolution quality.
debug=true
debug=false

We accept any Global Local parameters in most combinations; some combinations of parameters will be encountered more frequently than others, and have therefore consumed the respective proportion of our beta development efforts.

Match is designed to work with whatever you throw at it, but the more parameters you provide, the better we can match your attributes to an entity. Give us what you got!

Successful Call, Matched

In this example, Resolve has determined that a single, suitable candidate meets the criteria you passed to the platform. The response will contain that candidate and ONLY that candidate. Resolve will never return more than one matched record, unless the debug flag has been invoked. To support legacy behavior, the parameter resolved:true will always be included in the single returned row.

{
  "version":3,
  "status": "ok",
  "response":
  {
    "data":
    [
      {
      "factual_id": "bd886f67-9d86-40c5-9217-f7bcd53cfc0e",
      "name": "McDonald's",
      "address": "10451 Santa Monica Blvd",
      "locality": "Los Angeles",
      "region": "CA",
      "country": "US",
      "postcode": "90025",
      "tel": "(310) 474-3160",
      "category": "Food & Beverage > Restaurants > Fast Food",
      "website": "http://mcdonalds.com/",
      "latitude": 34.056555,
      "longitude": -118.425755,
      "status": "1"
      "resolved":true
       }
    ],
    "included_rows": 1
  }
}

Successful Call, Unmatched

If Match cannot match your data to an exact record, no data will be returned:

{
  "version": 3,
  "status": "ok",
  "response": {
    "data": [],
    "included_rows": 0
   }
}

Debug Parameter

If you pass the debug flag (=true) within a Resolve call, you will receive a list of candidates (if any) that were returned as likely matches to your criteria, ordered by similarity. Similarity ranges from 1.0 (high quality match) to .2 (low quality). If any of the candidates was deemed to be a suitable match, it will include the parameter resolved:true. Each non-matched candidate will include resolved:false.

{
  "version":3,
  "status": "ok",
  "response":
  {
    "data":
    [
      {
      "factual_id": "dab0005d-21d5-4c48-a9d6-02daa00479d3",
      "name": "Warszawa",
      "address": "1414 Lincoln Blvd",
      "locality": "Santa Monica",
      "region": "CA",
      "country": "US",
      "postcode": "90401",
      "tel": "(310) 393-8831",
      "category": "Food & Beverage > Restaurants",
      "latitude": 34.019207,
      "longitude": -118.49102,
      "status": "1",
      "resolved":true,
      "similarity":.99
      },
      {
      "factual_id": "dc8Defe7-d54c-2123-ede9-19abc220004e",
      "name": "Warszawa Backyard Bar",
      "address": "1414 Lincoln Blvd",
      "locality": "Santa Monica",
      "region": "CA",
      "country": "US",
      "postcode": "90401",
      "tel": "(310) 393-8831",
      "category": "Arts, Entertainment & Nightlife > Bars",
      "latitude": 34.019206,
      "longitude": -118.49100,
      "status": "1",
      "resolved":false,
      "similarity":.98
      }
    ],
    "included_rows": 2
  }
}