
A Mountain of Untapped Qualitative Data
Between online reviews and social media chatter, thereβs more qualitative data than ever β and itβs surfaced directly by your customers. Using natural language processing (NLP), we can go beyond star ratings and actually detect the sentiment of this feedback:
- Whoβs happiest and why?
- What language do our happiest (or angriest) customers use?
- Are there patterns across certain products, service agents, or time periods?
- Can we up-sell to the happiest? Prioritise outreach to those least satisfied?
This kind of analysis lets us identify not just broad trends but specific opportunities for retention, customer service and growth.
Do We Even Speak the Same Language?
Hereβs something we rarely ask: are we using the same language our customers use?
Too often, website copy and email content is written in marketing lingo or internal product terms. But if customers keep describing a feature as βfast and simple,β shouldnβt those exact words be reflected on your landing page?
Sentiment analysis can reveal the emotional cues and vocabulary that matter most to your audience β and help you echo that language to build trust and clarity.
A Quick Win: Analyse Sentiment in Google Sheets
You donβt need a data science team to get started. Years ago, Google published this blog post, which showed how anyone can use their Natural Language API inside Google Sheets.
Weβve adapted that idea for you.
π Click here to open and copy this sentiment analysis Google Sheet
Once you make a copy and add your API key (see below), you can paste in a list of customer reviews or comments and immediately return sentiment scores β from -1 (very negative) to +1 (very positive) β along with detected sentiment magnitude.
The Code (Add in Apps Script)
To get this working, go to Extensions > Apps Script in your copied sheet and paste in the following and follow the instructions provided on tab 1:
/**
* Snippet by Net Impression
* Need help with GA4, GTM, or CRO? Visit our free resources or get in touch:
* Resources: https://www.netimpression.co.uk/free-resources
* Contact: https://www.netimpression.co.uk/contact
*/
/**
* Performs entity sentiment analysis on english text data in a sheet using Cloud Natural Language (cloud.google.com/natural-language/).
*/
var COLUMN_NAME = {
COMMENTS: 'Review_Content',
LANGUAGE: 'Language_detected',
TRANSLATION: 'comments_english',
ENTITY: 'entity_sentiment',
ID: 'Review_Id'
};
/**
* Creates a ML Tools menu in Google Spreadsheets.
*/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('ML Tools')
.addItem('Mark Entities and Sentiment', 'markEntitySentiment')
.addToUi();
};
/**
* For each row in the reviewData sheet with a value in "comments" field,
* will run the retrieveEntitySentiment function
* and copy results into the entitySentiment sheet.
*/
function markEntitySentiment() {
// set variables for reviewData sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName('reviewData');
var rows = dataSheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var headerRow = values[0];
// checks to see if entitySentiment sheet is present; if not, creates new sheet and sets header row
var entitySheet = ss.getSheetByName('entitySentiment');
if (entitySheet == null) {
ss.insertSheet('entitySentiment');
var entitySheet = ss.getSheetByName('entitySentiment');
var esHeaderRange = entitySheet.getRange(1,1,1,6);
var esHeader = [['Review ID','Entity','Salience','Sentiment Score','Sentiment Magnitude','Number of mentions']];
esHeaderRange.setValues(esHeader);
};
// find the column index for comments, language_detected, comments_english
var commentsColumnIdx = headerRow.indexOf(COLUMN_NAME.COMMENTS);
var languageColumnIdx = headerRow.indexOf(COLUMN_NAME.LANGUAGE);
var translationColumnIdx = headerRow.indexOf(COLUMN_NAME.TRANSLATION);
var entityColumnIdx = headerRow.indexOf(COLUMN_NAME.ENTITY);
var idColumnIdx = headerRow.indexOf(COLUMN_NAME.ID);
if (entityColumnIdx == -1) {
Browser.msgBox("Error: Could not find the column named " + COLUMN_NAME.ENTITY + ". Please create an empty column with header \"entity_sentiment\" on the reviewData tab.");
return; // bail
};
ss.toast("Analyzing entities and sentiment...");
// Process each row
for (var i = 0; i < numRows; ++i) {
var value = values[i];
var commentEnCellVal = value[translationColumnIdx];
var entityCellVal = value[entityColumnIdx];
var reviewId = value[idColumnIdx];
// Call retrieveEntitySentiment function for each row that has comments and also an empty entity_sentiment cell
if(commentEnCellVal && !entityCellVal) {
var nlData = retrieveEntitySentiment(commentEnCellVal);
// Paste each entity and sentiment score into entitySentiment sheet
for (var j = 0; j < nlData.entities.length; ++j) {
var entityInResponse = nlData.entities[j];
var lastRowIdx = entitySheet.getLastRow() + 1;
var newValues = [[reviewId, entityInResponse.name, entityInResponse.salience, entityInResponse.sentiment.score, entityInResponse.sentiment.magnitude, entityInResponse.mentions.length]];
var pastingRange = entitySheet.getRange(lastRowIdx,1,1,6);
pastingRange.setValues(newValues);
}
// Paste "complete" into entity_sentiment column to denote completion of NL API call
dataSheet.getRange(i+1, entityColumnIdx+1).setValue("complete");
}
}
};
/**
* Calls the NL API with a string
* @param {String} line The line of string
* @return {Object} the entities and related sentiment present in the string.
*/
function retrieveEntitySentiment (line) {
var apiKey = "";
var apiEndpoint = 'https://language.googleapis.com/v1/documents:analyzeEntitySentiment?key=' + apiKey;
// Create our json request, w/ text, language, type & encoding
var nlData = {
document: {
language: 'en-us',
type: 'PLAIN_TEXT',
content: line
},
encodingType: 'UTF8'
};
// Package all of the options and the data together for the call
var nlOptions = {
method : 'post',
contentType: 'application/json',
payload : JSON.stringify(nlData)
};
// And make the call
var response = UrlFetchApp.fetch(apiEndpoint, nlOptions);
return JSON.parse(response);
};
β
Before you get started, youβll need to retrieve an API key from the Google Cloud Console. Enable the Cloud Natural Language API for your project, then generate an API key and add it to the script in the section that says:
var apiKey = "";
β
Visualising the Results in Looker Studio
Once youβve gathered sentiment scores and entity insights in Google Sheets, the next step is surfacing those insights in a way your team can actually use. By connecting your sheet to Looker Studio, you can build interactive dashboards that show sentiment trends over time, compare products or services by emotional tone, and filter by review segments. This makes it easier to share insights across teams β whether itβs customer support spotting recurring complaints, marketing fine-tuning copy, or leadership monitoring brand perception in real-time. When visualised properly, sentiment data becomes a living, breathing voice-of-the-customer report.
π Additional Use Cases You Might Expand Into
5. Trend detection over time
Track sentiment trends monthly or around new product launches to measure public reception.
6. Competitor analysis
Run the same analysis on competitor reviews to spot what customers love or hate about them.
7. Feature request detection
Entities and keywords with neutral sentiment but high salience may highlight requests, not emotions β a valuable side benefit.
8. Crisis response
Sudden drops in sentiment can help teams detect PR issues, bugs, or service outages in near real-time.
9. Segment-specific copywriting
You could use different messaging depending on how customers in different sentiment bands describe their needs.