I came across Google’s FactCheckTools API today, so I thought it might be fun to add to my bmApiCentral Apps Script library. This API can be used to both search popular fake news claims and to post fact check reviews. In this case though, I’ve only implemented the search piece in Apps Script, which is the subject of this article.

Getting started

As usual there’s a few things to do before getting started

Libraries

You’ll need a couple of my libraries.

bmApiCentral – 1L4pGblikbjQLQp8nQCdmCfyCxmF3MIShzsK8yy_mJ9_2YMdanXQA75vI github

bmPreFiddler – 13JUFGY18RHfjjuKmIRRfvmGlCYrEkEtN6uUm-iLUcxOUFRJD-WBX-tkR github

Cloud Project

You’ll also need to use a regular cloud project and replace the default project in your Apps Script settings. I won’t go into the details of all that here, as if you’re reading this, you probably already know how to do all that. That cloud project will need this API enabled

  • Fact Check Tools API

Api keys

For this API you need an ApiKey. You can create this via the API credentials section of the Cloud Console. Add this as a script property called “apiKey” in your project settings.

Exports

All my projects feature an Exports Script nowadays, which is used to manage libraries, check the validity of access to methods and properties, and deal with any declaration order issues. You can copy and paste this one as is into a script file in your project.

var Exports = {

get libExports() {
return bmApiCentral.Exports
},


get Deps() {
return this.libExports.Deps
},

get PreFiddler () {
return bmPreFiddler.PreFiddler
},

newPreFiddler(...args) {
return this.guard(Exports.PreFiddler().getFiddler(...args))
},


/**
* Fact instance with validation
* @param {...*} args
* @return {Fact} a proxied instance of Fact with property checking enabled
*/
newFact(...args) {
return this.libExports.newFact(...args)
},



/**
* Utils namespace
* @return {Utils}
*/
get Utils() {
return this.libExports.libExports.Utils
},

// used to trap access to unknown properties
guard(target) {
return new Proxy(target, this.validateProperties)
},

/**
* for validating attempts to access non existent properties
*/
get validateProperties() {
return {
get(target, prop, receiver) {
// typeof and console use the inspect prop
if (
typeof prop !== 'symbol' &&
prop !== 'inspect' &&
!Reflect.has(target, prop)
) throw `guard detected attempt to get non-existent property ${prop}`

return Reflect.get(target, prop, receiver)
},

set(target, prop, value, receiver) {
if (!Reflect.has(target, prop)) throw `guard attempt to set non-existent property ${prop}`
return Reflect.set(target, prop, value, receiver)
}
}
}


}
Exports.gs

Dependencies

The api library is dependendency free, so you need to register your UrlFetch app. Unusually, the Fact check API does not need any Oauth token (in fact supplying one would make it fail), so the only dependency required is below.

  Exports.Deps.init({
fetch: UrlFetchApp.fetch
})
dependencies

Fact check example

Now we can do a trial fact check – here’s the code to return a single item.

function testFact() {
// initialize library with apps script depdendencies
Exports.Deps.init({
fetch: UrlFetchApp.fetch
})

// get the apiKey
const apiKey = PropertiesService.getScriptProperties().getProperty("apiKey")

const fact = Exports.newFact({
apiKey
})


const r = fact.search({ query: "Elvis is alive" })
console.log(r.data.claims[0])
}
simple fact check

Response

{ text: 'The body of a homeless man found in San Diego has been identified as that of Elvis Presley.',
  claimReview: 
   [ { publisher: [Object],
       url: 'https://www.snopes.com/fact-check/elvis-presley-body-elderly-homeless-man/',
       title: 'Was the Body of an Elderly Homeless Man Identified as Elvis Presley?',
       reviewDate: '2015-01-31T16:00:00Z',
       textualRating: 'False',
       languageCode: 'en' } ] }

Spreadsheet example

Let’s do a little more now and use the API to populate a sheet which has series of claims that need fact checked.

Let’s try these

claim
the earth is flat
Covid is a hoax
The moon landings were faked
Elvis is alive
Loch ness monster exists
alien abductions
near death experiences

As usual,we’ll use my bmPreFiddler library to deal with talking to the sheets. Here’s the code. You’ll also find it in the testFact project – link at the end of this article.

const sheetFact = () => {

// initialize library with apps script depdendencies
Exports.Deps.init({
fetch: UrlFetchApp.fetch
})
// get the apiKey
const apiKey = PropertiesService.getScriptProperties().getProperty("apiKey")

// default api options for all apis for this run
const apiOptions = {
noisy: false,
throwOnError: true,
noCache: false
}

const fact = Exports.newFact({
apiKey
})

const fiddler = Exports.newPreFiddler({
id: "1ZHMCtybDWF4eO-7XUuaGLZDh0OUAiJvYA27Qc5UaSMQ",
sheetName: "claims"
})



// process each claim
const validateClaims = (queries) => {

return queries.reduce((p, query) => {

// fact check each row
const { data } = fact.search({ ...apiOptions, query })

// register results
if (!data || !data.claims || !data.claims.length) {
p.push({
query,
text: "no fact check data found"
})

} else {
// unpack each claim
data.claims.forEach(claim => {
// separate the claim review object from the other properties
let { rest, claimReview } =
[claim].map(({ claimReview, ...rest }) => ({ rest, claimReview }))[0]
if (!claimReview || !claimReview.length) {
// possible there are fact checkswith no reviews
p.push({
query,
...rest
})

} else {
// a set of reviews
claimReview.forEach(review => {
// seperate out the publisher object
const { publisher, substance } =
[review].map(({ publisher, ...substance }) => ({ publisher, substance }))[0]
p.push({
query,
...rest,
...substance,
...publisher
})
})
}
})
}
return p
}, [])
}


// get all the claims from sheet - (drop data from previous runs and dedup)
const queries = Array.from(new Set(fiddler.getData().map(f => f.query))).filter(f => f)

// do the work and dump to sheet
fiddler.setData(validateClaims(queries))
.dumpValues()


}
fact checking a sheet

Pagination with ApiCentral

By default, pagination is handled automatically and you’ll get back all the matching results, irrespective of the pageSize limits on the API. You can limit how many results are returns with the limit parameter- for example:

fact.search({ ...apiOptions, query,limit: 5 })
Limiting the number of results

Note also that bmApiCentral has caching built in by default.

Example results

Here’s the result of fact checking our original queries with a limit of 5 results per query.

Links

bmApiCentral – 1L4pGblikbjQLQp8nQCdmCfyCxmF3MIShzsK8yy_mJ9_2YMdanXQA75vI github

bmPreFiddler – 13JUFGY18RHfjjuKmIRRfvmGlCYrEkEtN6uUm-iLUcxOUFRJD-WBX-tkR github

testFact – Take a copy of the example code in this article github

bmPreFiddler – Take a copy of the test sheet