Finds the companies competing with you from a description of your business, and files a researched profile of each in Notion.
Uses
Claude
ExaNotion
Find out who you are competing with
Every 30 days, what your company sells is turned into a search of a company index for up to 30 businesses offering something similar. Each one not already in your Notion database gets a short profile written from the text of its own website, filed as a new page in that database. Each run adds up to six (you choose the number) and the rest wait for the next run, so the list keeps growing as new companies appear.
Each profile covers what they sell, who they target, their positioning and their claims, with a link to its source. Anything the site does not say is marked 'Not stated on their site', and a page too thin to describe is skipped rather than filled in from memory. A company already in the database, or looked at in an earlier run, is never given a second page, and your own company is left out even when its name is written differently ('Acme' or 'Acme Ltd'). The run ends with a list of the new competitors and links to their pages. If the database cannot be read, the run stops before anything is searched; if a company cannot be profiled, the run is marked failed, names it, and tries it again next time.
You need Exa, Anthropic and Notion connections, and a Notion database shared with your Notion connection. Only its title column is written, so any database will do. When you add it, you are asked for what you sell, your company name and the database's link.
businessDescription to blank.Set businessDescription to blank.yourCompanyName to blank.Set yourCompanyName to blank.notionDatabaseId to blank.Set notionDatabaseId to blank.titleProperty to Name.Set titleProperty to Name.maxCompetitors to 6.Set maxCompetitors to 6.[?#].*$ with in notionDatabaseId and save it as databasePath.Replace the pattern [?#].*$ with in notionDatabaseId and save it as databasePath.([0-9a-fA-F]{8})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{12}) with $1$2$3$4$5 in databasePath and save it as databaseDigits.Replace the pattern ([0-9a-fA-F]{8})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{12}) with $1$2$3$4$5 in databasePath and save it as databaseDigits.databaseDigits and save it as databaseId.Extract the first match from databaseDigits and save it as databaseId.databaseId is emptyIf databaseId is empty(?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in yourCompanyName and save it as ownWords.Replace the pattern (?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in yourCompanyName and save it as ownWords.[\W_]+ with in ownWords and save it as ownKey.Replace the pattern [\W_]+ with in ownWords and save it as ownKey.ownKey is less than or equal to 2If the length of ownKey is less than or equal to 2filedKeys to |.Set filedKeys to |.existingPages to {results:[]}.Set existingPages to {results:[]}.nextPage to blank.Set nextPage to blank.morePages to true.Set morePages to true.morePages is yes:While morePages is yes:databaseErrorIf a step below fails, save the error as databaseErrordatabaseId. Save it as existingPages.Find pages in databaseId. Save it as existingPages.databaseError.message.Stop and report a problem: Could not read your Notion database, so nothing was searched or written. Check the database link in 'notionDatabaseId', and share the database with your Notion connection: in Notion, open the database, click the ••• menu, choose Add connections and pick the integration you connected to Lodol. Notion said: databaseError.message.existingPage in existingPages.results:For each existingPage in existingPages.results:(?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in existingPage.title and save it as existingWords.Replace the pattern (?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in existingPage.title and save it as existingWords.[\W_]+ with in existingWords and save it as existingKey.Replace the pattern [\W_]+ with in existingWords and save it as existingKey.existingKey is greater than 2If the length of existingKey is greater than 2@filedKeys@existingKey| and save it as filedKeys.Build the text @filedKeys@existingKey| and save it as filedKeys.existingPages.next_cursor, or if it is empty and save it as nextPage.Use existingPages.next_cursor, or if it is empty and save it as nextPage.morePages to existingPages.has_more is true.Change morePages to existingPages.has_more is true.nextPage is blankIf nextPage is blankmorePages to false.Change morePages to false.examinedStore.Get the stored value 'examined_competitor_sites'. Save it as examinedStore.examinedSites to an empty list.Set examinedSites to an empty list.examinedStore.found is yesIf examinedStore.found is yesexaminedSites to examinedStore.value.Change examinedSites to examinedStore.value.businessDescription'. Save it as discovery.Search the web for 'Companies selling a competing product or service to this business: businessDescription'. Save it as discovery.
Number of results: 30
Include relevant excerpts: Nocandidates to discovery.results.Set candidates to discovery.results.candidates is emptyIf candidates is emptycandidates and save it as foundCount.Count the items in candidates and save it as foundCount.promptIntro to:Set promptIntro to:You are writing one competitor profile for a company research database.
Below is the text of one company's own website. Write the profile using ONLY facts stated in that text.
Rules:
- Never use anything you know about this company from anywhere else.
- Never guess or estimate pricing, plans, customer numbers, headcount, funding or revenue. If the page does not state it, do not mention it.
- If the page does not cover a section, write exactly: Not stated on their site.
- Keep the whole profile under 200 words.
Reply in exactly this format and nothing else:
<the company's own short name, o...profilesWritten to 0.Set profilesWritten to 0.addedLines to an empty list.Set addedLines to an empty list.failureLines to an empty list.Set failureLines to an empty list.leftForNextRun to 0.Set leftForNextRun to 0.because 'maxCompetitors' is @maxCompetitors and save it as leftReason.Build the text because 'maxCompetitors' is @maxCompetitors and save it as leftReason.thinPages to 0.Set thinPages to 0.candidate in candidates:For each candidate in candidates:candidate.url and save it as site.Extract the first domain from candidate.url and save it as site.examinedSites contains siteIf examinedSites contains sitefailureLines is greater than or equal to 3If the length of failureLines is greater than or equal to 3leftReason to because 3 companies could not be profiled (listed below), so the rest were not tried.Change leftReason to because 3 companies could not be profiled (listed below), so the rest were not tried.leftForNextRun.Add 1 to leftForNextRun.profilesWritten is greater than or equal to maxCompetitorsIf profilesWritten is greater than or equal to maxCompetitorsleftForNextRun.Add 1 to leftForNextRun.profileErrorIf a step below fails, save the error as profileErrorpageUrl to blank.Set pageUrl to blank.sourceText to blank.Set sourceText to blank.resultFieldErrorIf a step below fails, save the error as resultFieldErrorcandidate.url and save it as pageUrl.Trim candidate.url and save it as pageUrl.candidate.text and save it as sourceText.Trim candidate.text and save it as sourceText.Skipped a search result that had no web address or no readable page text..Show in the output: Skipped a search result that had no web address or no readable page text..sourceText is greater than or equal to 200 and pageUrl contains…If the length of sourceText is greater than or equal to 200 and pageUrl contains…prompt to promptIntroSet prompt to promptIntro
OUR OWN BUSINESS (context only - never a source of facts about them):
businessDescription
THEIR WEBSITE TEXT (the only source of facts about them):
sourceText truncated to 6000 characters.prompt. Save it as verdict.Ask Claude: prompt. Save it as verdict.
Maximum response length: 500verdict.response_text and save it as responseText.Trim verdict.response_text and save it as responseText.companyName to blank.Set companyName to blank.profileBody to blank.Set profileBody to blank.responseText contains ===PROFILE===If responseText contains ===PROFILE===responseText split by ===PROFILE=== and save it as companyName.Trim the first item of responseText split by ===PROFILE=== and save it as companyName.responseText split by ===PROFILE=== to 1800 characters and save it as profileBody.Shorten the last item of responseText split by ===PROFILE=== to 1800 characters and save it as profileBody.(?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in companyName and save it as nameWords.Replace the pattern (?i)\b(inc|incorporated|ltd|limited|llc|llp|lp|plc|corp|corporation|co|company|gmbh|ag|sa|sas|sarl|srl|spa|bv|nv|oy|ab|as|aps|pty|kk)\b\.? with in companyName and save it as nameWords.[\W_]+ with in nameWords and save it as nameKey.Replace the pattern [\W_]+ with in nameWords and save it as nameKey.nameLooksUsable to the length of companyName is greater than 0 and the length of companyName is less than or equal to 80.Set nameLooksUsable to the length of companyName is greater than 0 and the length of companyName is less than or equal to 80.filedKeys contains |nameKey| (ignoring case) and save it as alreadyFiled.Check whether filedKeys contains |nameKey| (ignoring case) and save it as alreadyFiled.nameKey contains ownKey (ignoring case) and save it as isOurselves.Check whether nameKey contains ownKey (ignoring case) and save it as isOurselves.nameKey is greater than 2 and ownKey contains nameKey (ignoring…If the length of nameKey is greater than 2 and ownKey contains nameKey (ignoring…isOurselves to true.Change isOurselves to true.nameLooksUsable is noIf nameLooksUsable is nopageUrl: the AI did not give a usable company name to the end of failureLines and save it as failureLines.Add - pageUrl: the AI did not give a usable company name to the end of failureLines and save it as failureLines.site isn't blankIf site isn't blanksite to the end of examinedSites and save it as examinedSites.Add site to the end of examinedSites and save it as examinedSites.companyName in databaseId. Save it as newPage.Create the page companyName in databaseId. Save it as newPage.@filedKeys@nameKey| and save it as filedKeys.Build the text @filedKeys@nameKey| and save it as filedKeys.profilesWritten.Add 1 to profilesWritten.companyName: newPage.url to the end of addedLines and save it as addedLines.Add - companyName: newPage.url to the end of addedLines and save it as addedLines.site isn't blankIf site isn't blanksite to the end of examinedSites and save it as examinedSites.Add site to the end of examinedSites and save it as examinedSites.pageUrl: the AI's profile did not come back in the expected format to the end of failureLines and save it as failureLines.Add - pageUrl: the AI's profile did not come back in the expected format to the end of failureLines and save it as failureLines.thinPages.Add 1 to thinPages.candidate.url: profileError.message to the end of failureLines and save it as failureLines.Add - candidate.url: profileError.message to the end of failureLines and save it as failureLines.memoryErrorIf a step below fails, save the error as memoryErrorexaminedSites.Set the stored value 'examined_competitor_sites' to examinedSites.memoryError.message to the end of failureLines and save it as failureLines.Add - Could not save the list of sites already looked at, so they will be looked at again next run: memoryError.message to the end of failureLines and save it as failureLines.addedLines with a new line and save it as addedText.Join addedLines with a new line and save it as addedText.failureLines with a new line and save it as failureText.Join failureLines with a new line and save it as failureText.summaryLines to an empty list.Set summaryLines to an empty list.profilesWritten is greater than 0If profilesWritten is greater than 0profilesWritten new competitor(s) to your Notion database:Add Added profilesWritten new competitor(s) to your Notion database:
addedText to the end of summaryLines and save it as summaryLines.foundCount companies, and each one is already in your Notion database, was looked at in an earlier run, or is your own company. to the end of summaryLines and save it as summaryLines.Add No new competitors this run. The search found foundCount companies, and each one is already in your Notion database, was looked at in an earlier run, or is your own company. to the end of summaryLines and save it as summaryLines.No new competitors were added this run. to the end of summaryLines and save it as summaryLines.Add No new competitors were added this run. to the end of summaryLines and save it as summaryLines.leftForNextRun is greater than 0If leftForNextRun is greater than 0leftForNextRun more companies from the search were left for the next run, leftReason. to the end of summaryLines and save it as summaryLines.Add leftForNextRun more companies from the search were left for the next run, leftReason. to the end of summaryLines and save it as summaryLines.thinPages is greater than 0If thinPages is greater than 0thinPages had too little text on their website to profile, so they were skipped. to the end of summaryLines and save it as summaryLines.Add thinPages had too little text on their website to profile, so they were skipped. to the end of summaryLines and save it as summaryLines.failureLines isn't emptyIf failureLines isn't emptyfailureText to the end of summaryLines and save it as summaryLines.summaryLines with a blank line and save it as summary.Join summaryLines with a blank line and save it as summary.failureLines isn't emptyIf failureLines isn't emptysummary.Stop and report a problem: summary.summary.Finish the run: summary.