From 5b74e723e486498119e3c31f05e7d592a38dbd8b Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 29 Jan 2026 12:30:18 -0800 Subject: [PATCH] feat(tools): added similarweb --- apps/docs/components/icons.tsx | 18 ++ apps/docs/components/ui/icon-mapping.ts | 2 + apps/docs/content/docs/en/tools/meta.json | 1 + .../docs/content/docs/en/tools/similarweb.mdx | 183 ++++++++++++++++ apps/docs/content/docs/en/tools/youtube.mdx | 28 +-- apps/sim/blocks/blocks/similarweb.ts | 200 ++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 18 ++ apps/sim/tools/registry.ts | 12 ++ apps/sim/tools/similarweb/bounce_rate.ts | 139 ++++++++++++ apps/sim/tools/similarweb/index.ts | 6 + apps/sim/tools/similarweb/pages_per_visit.ts | 139 ++++++++++++ apps/sim/tools/similarweb/traffic_visits.ts | 139 ++++++++++++ apps/sim/tools/similarweb/types.ts | 139 ++++++++++++ apps/sim/tools/similarweb/visit_duration.ts | 141 ++++++++++++ apps/sim/tools/similarweb/website_overview.ts | 196 +++++++++++++++++ 16 files changed, 1336 insertions(+), 27 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/similarweb.mdx create mode 100644 apps/sim/blocks/blocks/similarweb.ts create mode 100644 apps/sim/tools/similarweb/bounce_rate.ts create mode 100644 apps/sim/tools/similarweb/index.ts create mode 100644 apps/sim/tools/similarweb/pages_per_visit.ts create mode 100644 apps/sim/tools/similarweb/traffic_visits.ts create mode 100644 apps/sim/tools/similarweb/types.ts create mode 100644 apps/sim/tools/similarweb/visit_duration.ts create mode 100644 apps/sim/tools/similarweb/website_overview.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index f9b47f43c..26d31a899 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -5113,3 +5113,21 @@ export function PulseIcon(props: SVGProps) { ) } + +export function SimilarwebIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 046410d29..c6b126adb 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -100,6 +100,7 @@ import { ServiceNowIcon, SftpIcon, ShopifyIcon, + SimilarwebIcon, SlackIcon, SmtpIcon, SQSIcon, @@ -228,6 +229,7 @@ export const blockTypeToIconMap: Record = { sftp: SftpIcon, sharepoint: MicrosoftSharepointIcon, shopify: ShopifyIcon, + similarweb: SimilarwebIcon, slack: SlackIcon, smtp: SmtpIcon, sqs: SQSIcon, diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 0c5635a23..de6fc2118 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -96,6 +96,7 @@ "sftp", "sharepoint", "shopify", + "similarweb", "slack", "smtp", "sqs", diff --git a/apps/docs/content/docs/en/tools/similarweb.mdx b/apps/docs/content/docs/en/tools/similarweb.mdx new file mode 100644 index 000000000..e1c41260b --- /dev/null +++ b/apps/docs/content/docs/en/tools/similarweb.mdx @@ -0,0 +1,183 @@ +--- +title: Similarweb +description: Website traffic and analytics data +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Similarweb](https://www.similarweb.com/) is a leading platform for web analytics that provides in-depth traffic and engagement data for millions of websites. Similarweb gives you insights into website visits, traffic sources, audience behavior, and competitive benchmarks. + +With Similarweb in Sim, your agents can: + +- **Analyze website traffic**: Retrieve key metrics such as monthly visits, average duration, bounce rates, and top countries. +- **Understand audience engagement**: Gain insights into how users interact with websites, including pages per visit and engagement duration. +- **Track rankings and performance**: Access global, country, and category ranks to benchmark sites against competitors. +- **Discover traffic sources**: Break down traffic by channels like direct, search, social, referrals, and more. + +Use Sim's Similarweb integration to automate the monitoring of competitors, track your site’s performance, or surface actionable market research—all integrated directly into your workflows and automations. Empower your agents to access and utilize reliable web analytics data easily and programmatically. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Access comprehensive website analytics including traffic estimates, engagement metrics, rankings, and traffic sources using the Similarweb API. + + + +## Tools + +### `similarweb_website_overview` + +Get comprehensive website analytics including traffic, rankings, engagement, and traffic sources + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | SimilarWeb API key | +| `domain` | string | Yes | Website domain to analyze \(without www or protocol\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `siteName` | string | Website name | +| `description` | string | Website description | +| `globalRank` | number | Global traffic rank | +| `countryRank` | number | Country traffic rank | +| `categoryRank` | number | Category traffic rank | +| `category` | string | Website category | +| `monthlyVisits` | number | Estimated monthly visits | +| `engagementVisitDuration` | number | Average visit duration in seconds | +| `engagementPagesPerVisit` | number | Average pages per visit | +| `engagementBounceRate` | number | Bounce rate \(0-1\) | +| `topCountries` | array | Top countries by traffic share | +| ↳ `country` | string | Country code | +| ↳ `share` | number | Traffic share \(0-1\) | +| `trafficSources` | json | Traffic source breakdown | +| ↳ `direct` | number | Direct traffic share | +| ↳ `referrals` | number | Referral traffic share | +| ↳ `search` | number | Search traffic share | +| ↳ `social` | number | Social traffic share | +| ↳ `mail` | number | Email traffic share | +| ↳ `paidReferrals` | number | Paid referral traffic share | + +### `similarweb_traffic_visits` + +Get total website visits over time (desktop and mobile combined) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | SimilarWeb API key | +| `domain` | string | Yes | Website domain to analyze \(without www or protocol\) | +| `country` | string | Yes | 2-letter ISO country code or "world" for worldwide data | +| `granularity` | string | Yes | Data granularity: daily, weekly, or monthly | +| `startDate` | string | No | Start date in YYYY-MM format | +| `endDate` | string | No | End date in YYYY-MM format | +| `mainDomainOnly` | boolean | No | Exclude subdomains from results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `domain` | string | Analyzed domain | +| `country` | string | Country filter applied | +| `granularity` | string | Data granularity | +| `lastUpdated` | string | Data last updated timestamp | +| `visits` | array | Visit data over time | +| ↳ `date` | string | Date \(YYYY-MM-DD\) | +| ↳ `visits` | number | Number of visits | + +### `similarweb_bounce_rate` + +Get website bounce rate over time (desktop and mobile combined) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | SimilarWeb API key | +| `domain` | string | Yes | Website domain to analyze \(without www or protocol\) | +| `country` | string | Yes | 2-letter ISO country code or "world" for worldwide data | +| `granularity` | string | Yes | Data granularity: daily, weekly, or monthly | +| `startDate` | string | No | Start date in YYYY-MM format | +| `endDate` | string | No | End date in YYYY-MM format | +| `mainDomainOnly` | boolean | No | Exclude subdomains from results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `domain` | string | Analyzed domain | +| `country` | string | Country filter applied | +| `granularity` | string | Data granularity | +| `lastUpdated` | string | Data last updated timestamp | +| `bounceRate` | array | Bounce rate data over time | +| ↳ `date` | string | Date \(YYYY-MM-DD\) | +| ↳ `bounceRate` | number | Bounce rate \(0-1\) | + +### `similarweb_pages_per_visit` + +Get average pages per visit over time (desktop and mobile combined) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | SimilarWeb API key | +| `domain` | string | Yes | Website domain to analyze \(without www or protocol\) | +| `country` | string | Yes | 2-letter ISO country code or "world" for worldwide data | +| `granularity` | string | Yes | Data granularity: daily, weekly, or monthly | +| `startDate` | string | No | Start date in YYYY-MM format | +| `endDate` | string | No | End date in YYYY-MM format | +| `mainDomainOnly` | boolean | No | Exclude subdomains from results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `domain` | string | Analyzed domain | +| `country` | string | Country filter applied | +| `granularity` | string | Data granularity | +| `lastUpdated` | string | Data last updated timestamp | +| `pagesPerVisit` | array | Pages per visit data over time | +| ↳ `date` | string | Date \(YYYY-MM-DD\) | +| ↳ `pagesPerVisit` | number | Average pages per visit | + +### `similarweb_visit_duration` + +Get average desktop visit duration over time (in seconds) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | SimilarWeb API key | +| `domain` | string | Yes | Website domain to analyze \(without www or protocol\) | +| `country` | string | Yes | 2-letter ISO country code or "world" for worldwide data | +| `granularity` | string | Yes | Data granularity: daily, weekly, or monthly | +| `startDate` | string | No | Start date in YYYY-MM format | +| `endDate` | string | No | End date in YYYY-MM format | +| `mainDomainOnly` | boolean | No | Exclude subdomains from results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `domain` | string | Analyzed domain | +| `country` | string | Country filter applied | +| `granularity` | string | Data granularity | +| `lastUpdated` | string | Data last updated timestamp | +| `averageVisitDuration` | array | Desktop visit duration data over time | +| ↳ `date` | string | Date \(YYYY-MM-DD\) | +| ↳ `durationSeconds` | number | Average visit duration in seconds | + + diff --git a/apps/docs/content/docs/en/tools/youtube.mdx b/apps/docs/content/docs/en/tools/youtube.mdx index bf2f53ee6..f79057034 100644 --- a/apps/docs/content/docs/en/tools/youtube.mdx +++ b/apps/docs/content/docs/en/tools/youtube.mdx @@ -26,38 +26,12 @@ In Sim, the YouTube integration enables your agents to programmatically search a ## Usage Instructions -Integrate YouTube into the workflow. Can search for videos, get trending videos, get video details, get video captions, get video categories, get channel information, get all videos from a channel, get channel playlists, get playlist items, and get video comments. +Integrate YouTube into the workflow. Can search for videos, get trending videos, get video details, get video categories, get channel information, get all videos from a channel, get channel playlists, get playlist items, and get video comments. ## Tools -### `youtube_captions` - -List available caption tracks (subtitles/transcripts) for a YouTube video. Returns information about each caption including language, type, and whether it is auto-generated. - -#### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `videoId` | string | Yes | YouTube video ID to get captions for | -| `apiKey` | string | Yes | YouTube API Key | - -#### Output - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `items` | array | Array of available caption tracks for the video | -| ↳ `captionId` | string | Caption track ID | -| ↳ `language` | string | Language code of the caption \(e.g., | -| ↳ `name` | string | Name/label of the caption track | -| ↳ `trackKind` | string | Type of caption track: | -| ↳ `lastUpdated` | string | When the caption was last updated | -| ↳ `isCC` | boolean | Whether this is a closed caption track | -| ↳ `isAutoSynced` | boolean | Whether the caption timing was automatically synced | -| ↳ `audioTrackType` | string | Type of audio track this caption is for | -| `totalResults` | number | Total number of caption tracks available | - ### `youtube_channel_info` Get detailed information about a YouTube channel including statistics, branding, and content details. diff --git a/apps/sim/blocks/blocks/similarweb.ts b/apps/sim/blocks/blocks/similarweb.ts new file mode 100644 index 000000000..432b3a471 --- /dev/null +++ b/apps/sim/blocks/blocks/similarweb.ts @@ -0,0 +1,200 @@ +import { SimilarwebIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const SimilarwebBlock: BlockConfig = { + type: 'similarweb', + name: 'Similarweb', + description: 'Website traffic and analytics data', + longDescription: + 'Access comprehensive website analytics including traffic estimates, engagement metrics, rankings, and traffic sources using the Similarweb API.', + docsLink: 'https://developers.similarweb.com/docs/similarweb-web-traffic-api', + category: 'tools', + bgColor: '#000922', + icon: SimilarwebIcon, + authMode: AuthMode.ApiKey, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Website Overview', id: 'similarweb_website_overview' }, + { label: 'Traffic Visits', id: 'similarweb_traffic_visits' }, + { label: 'Bounce Rate', id: 'similarweb_bounce_rate' }, + { label: 'Pages Per Visit', id: 'similarweb_pages_per_visit' }, + { label: 'Visit Duration (Desktop)', id: 'similarweb_visit_duration' }, + ], + value: () => 'similarweb_website_overview', + }, + { + id: 'domain', + title: 'Domain', + type: 'short-input', + placeholder: 'example.com', + required: true, + }, + { + id: 'country', + title: 'Country', + type: 'dropdown', + options: [ + { label: 'Worldwide', id: 'world' }, + { label: 'United States', id: 'us' }, + { label: 'United Kingdom', id: 'gb' }, + { label: 'Germany', id: 'de' }, + { label: 'France', id: 'fr' }, + { label: 'Spain', id: 'es' }, + { label: 'Italy', id: 'it' }, + { label: 'Canada', id: 'ca' }, + { label: 'Australia', id: 'au' }, + { label: 'Japan', id: 'jp' }, + { label: 'Brazil', id: 'br' }, + { label: 'India', id: 'in' }, + { label: 'Netherlands', id: 'nl' }, + { label: 'Poland', id: 'pl' }, + { label: 'Russia', id: 'ru' }, + { label: 'Mexico', id: 'mx' }, + { label: 'South Korea', id: 'kr' }, + { label: 'China', id: 'cn' }, + ], + value: () => 'world', + condition: { + field: 'operation', + value: 'similarweb_website_overview', + not: true, + }, + }, + { + id: 'granularity', + title: 'Granularity', + type: 'dropdown', + options: [ + { label: 'Monthly', id: 'monthly' }, + { label: 'Weekly', id: 'weekly' }, + { label: 'Daily', id: 'daily' }, + ], + value: () => 'monthly', + condition: { + field: 'operation', + value: 'similarweb_website_overview', + not: true, + }, + }, + { + id: 'startDate', + title: 'Start Date', + type: 'short-input', + placeholder: 'YYYY-MM (e.g., 2024-01)', + condition: { + field: 'operation', + value: 'similarweb_website_overview', + not: true, + }, + wandConfig: { + enabled: true, + prompt: `Generate a date in YYYY-MM format based on the user's description. +Examples: +- "this month" -> Current month in YYYY-MM format +- "last month" -> Previous month in YYYY-MM format +- "3 months ago" -> Date 3 months ago in YYYY-MM format +- "beginning of year" -> January of current year (e.g., 2024-01) + +Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no extra text.`, + placeholder: 'Describe the start date (e.g., "3 months ago", "last month")...', + generationType: 'timestamp', + }, + }, + { + id: 'endDate', + title: 'End Date', + type: 'short-input', + placeholder: 'YYYY-MM (e.g., 2024-12)', + condition: { + field: 'operation', + value: 'similarweb_website_overview', + not: true, + }, + wandConfig: { + enabled: true, + prompt: `Generate a date in YYYY-MM format based on the user's description. +Examples: +- "this month" -> Current month in YYYY-MM format +- "last month" -> Previous month in YYYY-MM format +- "now" -> Current month in YYYY-MM format + +Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no extra text.`, + placeholder: 'Describe the end date (e.g., "this month", "now")...', + generationType: 'timestamp', + }, + }, + { + id: 'mainDomainOnly', + title: 'Main Domain Only', + type: 'switch', + condition: { + field: 'operation', + value: 'similarweb_website_overview', + not: true, + }, + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Similarweb API key', + password: true, + required: true, + }, + ], + + tools: { + access: [ + 'similarweb_website_overview', + 'similarweb_traffic_visits', + 'similarweb_bounce_rate', + 'similarweb_pages_per_visit', + 'similarweb_visit_duration', + ], + config: { + tool: (params) => params.operation, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + domain: { type: 'string', description: 'Website domain to analyze' }, + apiKey: { type: 'string', description: 'Similarweb API key' }, + country: { type: 'string', description: '2-letter ISO country code or "world"' }, + granularity: { type: 'string', description: 'Data granularity (daily, weekly, monthly)' }, + startDate: { type: 'string', description: 'Start date in YYYY-MM format' }, + endDate: { type: 'string', description: 'End date in YYYY-MM format' }, + mainDomainOnly: { type: 'boolean', description: 'Exclude subdomains from results' }, + }, + + outputs: { + // Website Overview outputs + siteName: { type: 'string', description: 'Website name' }, + description: { type: 'string', description: 'Website description' }, + globalRank: { type: 'number', description: 'Global traffic rank' }, + countryRank: { type: 'number', description: 'Country traffic rank' }, + categoryRank: { type: 'number', description: 'Category traffic rank' }, + category: { type: 'string', description: 'Website category' }, + monthlyVisits: { type: 'number', description: 'Estimated monthly visits' }, + engagementVisitDuration: { type: 'number', description: 'Average visit duration (seconds)' }, + engagementPagesPerVisit: { type: 'number', description: 'Average pages per visit' }, + engagementBounceRate: { type: 'number', description: 'Bounce rate (0-1)' }, + topCountries: { type: 'json', description: 'Top countries by traffic share' }, + trafficSources: { type: 'json', description: 'Traffic source breakdown' }, + // Time series outputs + domain: { type: 'string', description: 'Analyzed domain' }, + country: { type: 'string', description: 'Country filter applied' }, + granularity: { type: 'string', description: 'Data granularity' }, + lastUpdated: { type: 'string', description: 'Data last updated timestamp' }, + visits: { type: 'json', description: 'Visit data over time' }, + bounceRate: { type: 'json', description: 'Bounce rate data over time' }, + pagesPerVisit: { type: 'json', description: 'Pages per visit data over time' }, + averageVisitDuration: { type: 'json', description: 'Desktop visit duration data over time' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 202628998..95fd7a43c 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -111,6 +111,7 @@ import { ServiceNowBlock } from '@/blocks/blocks/servicenow' import { SftpBlock } from '@/blocks/blocks/sftp' import { SharepointBlock } from '@/blocks/blocks/sharepoint' import { ShopifyBlock } from '@/blocks/blocks/shopify' +import { SimilarwebBlock } from '@/blocks/blocks/similarweb' import { SlackBlock } from '@/blocks/blocks/slack' import { SmtpBlock } from '@/blocks/blocks/smtp' import { SpotifyBlock } from '@/blocks/blocks/spotify' @@ -280,6 +281,7 @@ export const registry: Record = { sftp: SftpBlock, sharepoint: SharepointBlock, shopify: ShopifyBlock, + similarweb: SimilarwebBlock, slack: SlackBlock, smtp: SmtpBlock, spotify: SpotifyBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index f9b47f43c..26d31a899 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -5113,3 +5113,21 @@ export function PulseIcon(props: SVGProps) { ) } + +export function SimilarwebIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 250a9bbf6..1de533aab 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1342,6 +1342,13 @@ import { shopifyUpdateOrderTool, shopifyUpdateProductTool, } from '@/tools/shopify' +import { + similarwebBounceRateTool, + similarwebPagesPerVisitTool, + similarwebTrafficVisitsTool, + similarwebVisitDurationTool, + similarwebWebsiteOverviewTool, +} from '@/tools/similarweb' import { slackAddReactionTool, slackCanvasTool, @@ -1936,6 +1943,11 @@ export const tools: Record = { github_latest_commit: githubLatestCommitTool, github_latest_commit_v2: githubLatestCommitV2Tool, serper_search: serperSearchTool, + similarweb_website_overview: similarwebWebsiteOverviewTool, + similarweb_traffic_visits: similarwebTrafficVisitsTool, + similarweb_bounce_rate: similarwebBounceRateTool, + similarweb_pages_per_visit: similarwebPagesPerVisitTool, + similarweb_visit_duration: similarwebVisitDurationTool, servicenow_create_record: servicenowCreateRecordTool, servicenow_read_record: servicenowReadRecordTool, servicenow_update_record: servicenowUpdateRecordTool, diff --git a/apps/sim/tools/similarweb/bounce_rate.ts b/apps/sim/tools/similarweb/bounce_rate.ts new file mode 100644 index 000000000..d31a476d2 --- /dev/null +++ b/apps/sim/tools/similarweb/bounce_rate.ts @@ -0,0 +1,139 @@ +import type { ToolConfig } from '@/tools/types' +import type { SimilarwebBounceRateParams, SimilarwebBounceRateResponse } from './types' + +export const similarwebBounceRateTool: ToolConfig< + SimilarwebBounceRateParams, + SimilarwebBounceRateResponse +> = { + id: 'similarweb_bounce_rate', + name: 'SimilarWeb Bounce Rate', + description: 'Get website bounce rate over time (desktop and mobile combined)', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SimilarWeb API key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Website domain to analyze (without www or protocol)', + }, + country: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: '2-letter ISO country code or "world" for worldwide data', + }, + granularity: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Data granularity: daily, weekly, or monthly', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM format', + }, + mainDomainOnly: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Exclude subdomains from results', + }, + }, + + request: { + url: (params) => { + const domain = params.domain + ?.trim() + .replace(/^(https?:\/\/)?(www\.)?/, '') + .replace(/\/$/, '') + const url = new URL( + `https://api.similarweb.com/v1/website/${domain}/total-traffic-and-engagement/bounce-rate` + ) + url.searchParams.set('api_key', params.apiKey?.trim()) + url.searchParams.set('country', params.country?.trim() ?? 'world') + url.searchParams.set('granularity', params.granularity ?? 'monthly') + url.searchParams.set('format', 'json') + if (params.startDate) url.searchParams.set('start_date', params.startDate) + if (params.endDate) url.searchParams.set('end_date', params.endDate) + if (params.mainDomainOnly !== undefined) + url.searchParams.set('main_domain_only', String(params.mainDomainOnly)) + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || data.message || 'Failed to get bounce rate') + } + + const meta = data.meta ?? {} + const request = meta.request ?? {} + + return { + success: true, + output: { + domain: request.domain ?? null, + country: request.country ?? null, + granularity: request.granularity ?? null, + lastUpdated: meta.last_updated ?? null, + bounceRate: + data.bounce_rate?.map((b: { date: string; bounce_rate: number }) => ({ + date: b.date, + bounceRate: b.bounce_rate, + })) ?? [], + }, + } + }, + + outputs: { + domain: { + type: 'string', + description: 'Analyzed domain', + }, + country: { + type: 'string', + description: 'Country filter applied', + }, + granularity: { + type: 'string', + description: 'Data granularity', + }, + lastUpdated: { + type: 'string', + description: 'Data last updated timestamp', + optional: true, + }, + bounceRate: { + type: 'array', + description: 'Bounce rate data over time', + items: { + type: 'object', + properties: { + date: { type: 'string', description: 'Date (YYYY-MM-DD)' }, + bounceRate: { type: 'number', description: 'Bounce rate (0-1)' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/similarweb/index.ts b/apps/sim/tools/similarweb/index.ts new file mode 100644 index 000000000..8418ba21c --- /dev/null +++ b/apps/sim/tools/similarweb/index.ts @@ -0,0 +1,6 @@ +export { similarwebBounceRateTool } from './bounce_rate' +export { similarwebPagesPerVisitTool } from './pages_per_visit' +export { similarwebTrafficVisitsTool } from './traffic_visits' +export * from './types' +export { similarwebVisitDurationTool } from './visit_duration' +export { similarwebWebsiteOverviewTool } from './website_overview' diff --git a/apps/sim/tools/similarweb/pages_per_visit.ts b/apps/sim/tools/similarweb/pages_per_visit.ts new file mode 100644 index 000000000..c60c292f1 --- /dev/null +++ b/apps/sim/tools/similarweb/pages_per_visit.ts @@ -0,0 +1,139 @@ +import type { ToolConfig } from '@/tools/types' +import type { SimilarwebPagesPerVisitParams, SimilarwebPagesPerVisitResponse } from './types' + +export const similarwebPagesPerVisitTool: ToolConfig< + SimilarwebPagesPerVisitParams, + SimilarwebPagesPerVisitResponse +> = { + id: 'similarweb_pages_per_visit', + name: 'SimilarWeb Pages Per Visit', + description: 'Get average pages per visit over time (desktop and mobile combined)', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SimilarWeb API key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Website domain to analyze (without www or protocol)', + }, + country: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: '2-letter ISO country code or "world" for worldwide data', + }, + granularity: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Data granularity: daily, weekly, or monthly', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM format', + }, + mainDomainOnly: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Exclude subdomains from results', + }, + }, + + request: { + url: (params) => { + const domain = params.domain + ?.trim() + .replace(/^(https?:\/\/)?(www\.)?/, '') + .replace(/\/$/, '') + const url = new URL( + `https://api.similarweb.com/v1/website/${domain}/total-traffic-and-engagement/pages-per-visit` + ) + url.searchParams.set('api_key', params.apiKey?.trim()) + url.searchParams.set('country', params.country?.trim() ?? 'world') + url.searchParams.set('granularity', params.granularity ?? 'monthly') + url.searchParams.set('format', 'json') + if (params.startDate) url.searchParams.set('start_date', params.startDate) + if (params.endDate) url.searchParams.set('end_date', params.endDate) + if (params.mainDomainOnly !== undefined) + url.searchParams.set('main_domain_only', String(params.mainDomainOnly)) + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || data.message || 'Failed to get pages per visit') + } + + const meta = data.meta ?? {} + const request = meta.request ?? {} + + return { + success: true, + output: { + domain: request.domain ?? null, + country: request.country ?? null, + granularity: request.granularity ?? null, + lastUpdated: meta.last_updated ?? null, + pagesPerVisit: + data.pages_per_visit?.map((p: { date: string; pages_per_visit: number }) => ({ + date: p.date, + pagesPerVisit: p.pages_per_visit, + })) ?? [], + }, + } + }, + + outputs: { + domain: { + type: 'string', + description: 'Analyzed domain', + }, + country: { + type: 'string', + description: 'Country filter applied', + }, + granularity: { + type: 'string', + description: 'Data granularity', + }, + lastUpdated: { + type: 'string', + description: 'Data last updated timestamp', + optional: true, + }, + pagesPerVisit: { + type: 'array', + description: 'Pages per visit data over time', + items: { + type: 'object', + properties: { + date: { type: 'string', description: 'Date (YYYY-MM-DD)' }, + pagesPerVisit: { type: 'number', description: 'Average pages per visit' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/similarweb/traffic_visits.ts b/apps/sim/tools/similarweb/traffic_visits.ts new file mode 100644 index 000000000..a934399df --- /dev/null +++ b/apps/sim/tools/similarweb/traffic_visits.ts @@ -0,0 +1,139 @@ +import type { ToolConfig } from '@/tools/types' +import type { SimilarwebTrafficVisitsParams, SimilarwebTrafficVisitsResponse } from './types' + +export const similarwebTrafficVisitsTool: ToolConfig< + SimilarwebTrafficVisitsParams, + SimilarwebTrafficVisitsResponse +> = { + id: 'similarweb_traffic_visits', + name: 'SimilarWeb Traffic Visits', + description: 'Get total website visits over time (desktop and mobile combined)', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SimilarWeb API key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Website domain to analyze (without www or protocol)', + }, + country: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: '2-letter ISO country code or "world" for worldwide data', + }, + granularity: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Data granularity: daily, weekly, or monthly', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM format', + }, + mainDomainOnly: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Exclude subdomains from results', + }, + }, + + request: { + url: (params) => { + const domain = params.domain + ?.trim() + .replace(/^(https?:\/\/)?(www\.)?/, '') + .replace(/\/$/, '') + const url = new URL( + `https://api.similarweb.com/v1/website/${domain}/total-traffic-and-engagement/visits` + ) + url.searchParams.set('api_key', params.apiKey?.trim()) + url.searchParams.set('country', params.country?.trim() ?? 'world') + url.searchParams.set('granularity', params.granularity ?? 'monthly') + url.searchParams.set('format', 'json') + if (params.startDate) url.searchParams.set('start_date', params.startDate) + if (params.endDate) url.searchParams.set('end_date', params.endDate) + if (params.mainDomainOnly !== undefined) + url.searchParams.set('main_domain_only', String(params.mainDomainOnly)) + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || data.message || 'Failed to get traffic visits') + } + + const meta = data.meta ?? {} + const request = meta.request ?? {} + + return { + success: true, + output: { + domain: request.domain ?? null, + country: request.country ?? null, + granularity: request.granularity ?? null, + lastUpdated: meta.last_updated ?? null, + visits: + data.visits?.map((v: { date: string; visits: number }) => ({ + date: v.date, + visits: v.visits, + })) ?? [], + }, + } + }, + + outputs: { + domain: { + type: 'string', + description: 'Analyzed domain', + }, + country: { + type: 'string', + description: 'Country filter applied', + }, + granularity: { + type: 'string', + description: 'Data granularity', + }, + lastUpdated: { + type: 'string', + description: 'Data last updated timestamp', + optional: true, + }, + visits: { + type: 'array', + description: 'Visit data over time', + items: { + type: 'object', + properties: { + date: { type: 'string', description: 'Date (YYYY-MM-DD)' }, + visits: { type: 'number', description: 'Number of visits' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/similarweb/types.ts b/apps/sim/tools/similarweb/types.ts new file mode 100644 index 000000000..2dc70e548 --- /dev/null +++ b/apps/sim/tools/similarweb/types.ts @@ -0,0 +1,139 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Common parameters for all SimilarWeb API endpoints + */ +export interface SimilarwebBaseParams { + apiKey: string + domain: string +} + +/** + * Parameters for time-series endpoints (visits, bounce rate, etc.) + */ +export interface SimilarwebTimeSeriesParams extends SimilarwebBaseParams { + country: string + granularity: 'daily' | 'weekly' | 'monthly' + startDate?: string + endDate?: string + mainDomainOnly?: boolean +} + +/** + * Website Overview (API Lite) parameters + */ +export interface SimilarwebWebsiteOverviewParams extends SimilarwebBaseParams {} + +/** + * Website Overview response + */ +export interface SimilarwebWebsiteOverviewResponse extends ToolResponse { + output: { + siteName: string + description: string | null + globalRank: number | null + countryRank: number | null + categoryRank: number | null + category: string | null + monthlyVisits: number | null + engagementVisitDuration: number | null + engagementPagesPerVisit: number | null + engagementBounceRate: number | null + topCountries: Array<{ + country: string + share: number + }> + trafficSources: { + direct: number | null + referrals: number | null + search: number | null + social: number | null + mail: number | null + paidReferrals: number | null + } + } +} + +/** + * Traffic Visits parameters + */ +export interface SimilarwebTrafficVisitsParams extends SimilarwebTimeSeriesParams {} + +/** + * Traffic Visits response + */ +export interface SimilarwebTrafficVisitsResponse extends ToolResponse { + output: { + domain: string + country: string + granularity: string + lastUpdated: string | null + visits: Array<{ + date: string + visits: number + }> + } +} + +/** + * Bounce Rate parameters + */ +export interface SimilarwebBounceRateParams extends SimilarwebTimeSeriesParams {} + +/** + * Bounce Rate response + */ +export interface SimilarwebBounceRateResponse extends ToolResponse { + output: { + domain: string + country: string + granularity: string + lastUpdated: string | null + bounceRate: Array<{ + date: string + bounceRate: number + }> + } +} + +/** + * Pages Per Visit parameters + */ +export interface SimilarwebPagesPerVisitParams extends SimilarwebTimeSeriesParams {} + +/** + * Pages Per Visit response + */ +export interface SimilarwebPagesPerVisitResponse extends ToolResponse { + output: { + domain: string + country: string + granularity: string + lastUpdated: string | null + pagesPerVisit: Array<{ + date: string + pagesPerVisit: number + }> + } +} + +/** + * Average Visit Duration parameters + */ +export interface SimilarwebVisitDurationParams extends SimilarwebTimeSeriesParams {} + +/** + * Average Visit Duration response + */ +export interface SimilarwebVisitDurationResponse extends ToolResponse { + output: { + domain: string + country: string + granularity: string + lastUpdated: string | null + averageVisitDuration: Array<{ + date: string + durationSeconds: number + }> + } +} diff --git a/apps/sim/tools/similarweb/visit_duration.ts b/apps/sim/tools/similarweb/visit_duration.ts new file mode 100644 index 000000000..765ff439d --- /dev/null +++ b/apps/sim/tools/similarweb/visit_duration.ts @@ -0,0 +1,141 @@ +import type { ToolConfig } from '@/tools/types' +import type { SimilarwebVisitDurationParams, SimilarwebVisitDurationResponse } from './types' + +export const similarwebVisitDurationTool: ToolConfig< + SimilarwebVisitDurationParams, + SimilarwebVisitDurationResponse +> = { + id: 'similarweb_visit_duration', + name: 'SimilarWeb Visit Duration', + description: 'Get average desktop visit duration over time (in seconds)', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SimilarWeb API key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Website domain to analyze (without www or protocol)', + }, + country: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: '2-letter ISO country code or "world" for worldwide data', + }, + granularity: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Data granularity: daily, weekly, or monthly', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM format', + }, + mainDomainOnly: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Exclude subdomains from results', + }, + }, + + request: { + url: (params) => { + const domain = params.domain + ?.trim() + .replace(/^(https?:\/\/)?(www\.)?/, '') + .replace(/\/$/, '') + const url = new URL( + `https://api.similarweb.com/v1/website/${domain}/traffic-and-engagement/average-visit-duration` + ) + url.searchParams.set('api_key', params.apiKey?.trim()) + url.searchParams.set('country', params.country?.trim() ?? 'world') + url.searchParams.set('granularity', params.granularity ?? 'monthly') + url.searchParams.set('format', 'json') + if (params.startDate) url.searchParams.set('start_date', params.startDate) + if (params.endDate) url.searchParams.set('end_date', params.endDate) + if (params.mainDomainOnly !== undefined) + url.searchParams.set('main_domain_only', String(params.mainDomainOnly)) + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || data.message || 'Failed to get visit duration') + } + + const meta = data.meta ?? {} + const request = meta.request ?? {} + + return { + success: true, + output: { + domain: request.domain ?? null, + country: request.country ?? null, + granularity: request.granularity ?? null, + lastUpdated: meta.last_updated ?? null, + averageVisitDuration: + data.average_visit_duration?.map( + (d: { date: string; average_visit_duration: number }) => ({ + date: d.date, + durationSeconds: d.average_visit_duration, + }) + ) ?? [], + }, + } + }, + + outputs: { + domain: { + type: 'string', + description: 'Analyzed domain', + }, + country: { + type: 'string', + description: 'Country filter applied', + }, + granularity: { + type: 'string', + description: 'Data granularity', + }, + lastUpdated: { + type: 'string', + description: 'Data last updated timestamp', + optional: true, + }, + averageVisitDuration: { + type: 'array', + description: 'Desktop visit duration data over time', + items: { + type: 'object', + properties: { + date: { type: 'string', description: 'Date (YYYY-MM-DD)' }, + durationSeconds: { type: 'number', description: 'Average visit duration in seconds' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/similarweb/website_overview.ts b/apps/sim/tools/similarweb/website_overview.ts new file mode 100644 index 000000000..70945928e --- /dev/null +++ b/apps/sim/tools/similarweb/website_overview.ts @@ -0,0 +1,196 @@ +import type { ToolConfig } from '@/tools/types' +import type { SimilarwebWebsiteOverviewParams, SimilarwebWebsiteOverviewResponse } from './types' + +export const similarwebWebsiteOverviewTool: ToolConfig< + SimilarwebWebsiteOverviewParams, + SimilarwebWebsiteOverviewResponse +> = { + id: 'similarweb_website_overview', + name: 'SimilarWeb Website Overview', + description: + 'Get comprehensive website analytics including traffic, rankings, engagement, and traffic sources', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SimilarWeb API key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Website domain to analyze (without www or protocol)', + }, + }, + + request: { + url: (params) => { + const domain = params.domain + ?.trim() + .replace(/^(https?:\/\/)?(www\.)?/, '') + .replace(/\/$/, '') + const url = new URL(`https://api.similarweb.com/v1/website/${domain}/general-data/all`) + url.searchParams.set('api_key', params.apiKey?.trim()) + url.searchParams.set('format', 'json') + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || data.message || 'Failed to get website overview') + } + + const topCountriesRaw = data.TopCountryShares ?? data.top_country_shares ?? [] + const topCountries = topCountriesRaw.map( + (c: { + Country?: number + CountryCode?: string + Value?: number + country?: number + country_code?: string + value?: number + }) => ({ + country: c.CountryCode ?? c.country_code ?? String(c.Country ?? c.country ?? ''), + share: c.Value ?? c.value ?? 0, + }) + ) + + const sources = data.TrafficSources ?? data.traffic_sources ?? {} + + const engagements = data.Engagements ?? data.engagements ?? data.engagments ?? {} + + const getGlobalRank = () => { + if (data.GlobalRank?.Rank !== undefined) return data.GlobalRank.Rank + if (data.global_rank?.rank !== undefined) return data.global_rank.rank + if (typeof data.GlobalRank === 'number') return data.GlobalRank + if (typeof data.global_rank === 'number') return data.global_rank + return null + } + + const getCountryRank = () => { + if (data.CountryRank?.Rank !== undefined) return data.CountryRank.Rank + if (data.country_rank?.rank !== undefined) return data.country_rank.rank + if (typeof data.CountryRank === 'number') return data.CountryRank + if (typeof data.country_rank === 'number') return data.country_rank + return null + } + + const getCategoryRank = () => { + if (data.CategoryRank?.Rank !== undefined) return data.CategoryRank.Rank + if (data.category_rank?.rank !== undefined) return data.category_rank.rank + if (typeof data.CategoryRank === 'number') return data.CategoryRank + if (typeof data.category_rank === 'number') return data.category_rank + return null + } + + return { + success: true, + output: { + siteName: data.SiteName ?? data.site_name ?? null, + description: data.Description ?? data.description ?? null, + globalRank: getGlobalRank(), + countryRank: getCountryRank(), + categoryRank: getCategoryRank(), + category: data.Category ?? data.category ?? null, + monthlyVisits: engagements.Visits ?? engagements.visits ?? null, + engagementVisitDuration: engagements.TimeOnSite ?? engagements.time_on_site ?? null, + engagementPagesPerVisit: engagements.PagePerVisit ?? engagements.page_per_visit ?? null, + engagementBounceRate: engagements.BounceRate ?? engagements.bounce_rate ?? null, + topCountries, + trafficSources: { + direct: sources.Direct ?? sources.direct ?? null, + referrals: sources.Referrals ?? sources.referrals ?? null, + search: sources.Search ?? sources.search ?? null, + social: sources.Social ?? sources.social ?? null, + mail: sources.Mail ?? sources.mail ?? null, + paidReferrals: sources['Paid Referrals'] ?? sources.paid_referrals ?? null, + }, + }, + } + }, + + outputs: { + siteName: { + type: 'string', + description: 'Website name', + }, + description: { + type: 'string', + description: 'Website description', + optional: true, + }, + globalRank: { + type: 'number', + description: 'Global traffic rank', + optional: true, + }, + countryRank: { + type: 'number', + description: 'Country traffic rank', + optional: true, + }, + categoryRank: { + type: 'number', + description: 'Category traffic rank', + optional: true, + }, + category: { + type: 'string', + description: 'Website category', + optional: true, + }, + monthlyVisits: { + type: 'number', + description: 'Estimated monthly visits', + optional: true, + }, + engagementVisitDuration: { + type: 'number', + description: 'Average visit duration in seconds', + optional: true, + }, + engagementPagesPerVisit: { + type: 'number', + description: 'Average pages per visit', + optional: true, + }, + engagementBounceRate: { + type: 'number', + description: 'Bounce rate (0-1)', + optional: true, + }, + topCountries: { + type: 'array', + description: 'Top countries by traffic share', + items: { + type: 'object', + properties: { + country: { type: 'string', description: 'Country code' }, + share: { type: 'number', description: 'Traffic share (0-1)' }, + }, + }, + }, + trafficSources: { + type: 'json', + description: 'Traffic source breakdown', + properties: { + direct: { type: 'number', description: 'Direct traffic share' }, + referrals: { type: 'number', description: 'Referral traffic share' }, + search: { type: 'number', description: 'Search traffic share' }, + social: { type: 'number', description: 'Social traffic share' }, + mail: { type: 'number', description: 'Email traffic share' }, + paidReferrals: { type: 'number', description: 'Paid referral traffic share' }, + }, + }, + }, +}