Docs

Measure

Import your history and connect Search Console

You can bring the numbers you collected before misomoon into your reports. Import them from a Google Analytics property, or upload the CSV files of an export. You can also connect Google Search Console to see the Google searches that brought people to the site.

What an import adds

An import stores daily totals per dimension, for example the visitors from each source on each day. misomoon adds these totals to the data it collects itself in the top-line numbers, the chart, and the breakdowns of the dimensions that imports fill.

Imported numbers appear only in reports without filters. misomoon stores totals, not single visits, so it can't tell which imported visits match a filter. When you add a filter, every report shows collected data only.

The Imported button in the top bar turns imported history on and off. It appears when the site has at least one finished import, and it starts on. Turning it off adds imported=0 to the address, which the API also accepts. The dashboard guide describes the top bar.

Set up Google sign-in on your instance

The Google import and Search Console sign in to Google with one OAuth client, which you create in your own Google Cloud project. If the instance already has the secrets GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, skip this section. CSV uploads don't need Google sign-in.

  1. In the Google Cloud console, create a project or open an existing one.

  2. Enable the Google Analytics Data API, the Google Analytics Admin API, and the Google Search Console API for the project.

  3. Configure the OAuth consent screen for the project.

  4. Create an OAuth client ID with the application type Web application.

  5. Under Authorized redirect URIs, add your misomoon address followed by /api/v1/google/callback, for example https://analytics.example.com/api/v1/google/callback.

  6. Copy the client ID and the client secret.

  7. In apps/api, set the two secrets:

    sh
    wrangler secret put GOOGLE_CLIENT_ID
    wrangler secret put GOOGLE_CLIENT_SECRET

The import uses the Analytics Data and Analytics Admin APIs. Search Console uses the Search Console API. If you want only one of the two features, enable only the APIs it uses.

misomoon builds the redirect URI from the address you open the dashboard at. If you reach the instance at more than one address, such as its workers.dev address and your own domain, add a redirect URI for each one. The PUBLIC_URL variable doesn't change the redirect URI.

If the consent screen's publishing status is Testing, add each Google account that signs in as a test user. Google ends the refresh tokens of apps in testing after 7 days (Google's OAuth documentation, checked 2026-10-25). After that, Search Console shows the error Google sign-in expired; connect again until you connect again.

On an instance deployed with the button, set the two secrets in the Cloudflare dashboard. Open the Worker, then Settings > Variables and Secrets. Turn on optional features and the secrets reference list the other secrets.

Import a Google Analytics property

misomoon adds imported days to the days it collected. A day that is in both counts twice. Before you start, find the day misomoon started collecting on the site, and import only the days before it.

  1. Open Settings > Integrations.
  2. In the Import history card, click Connect Google.
  3. Sign in with a Google account that can read the property, and allow access. Finish within an hour of the click.
  4. Back in Settings, pick the property in Google property. The list shows every property that the Google account can read, as Account: Property.
  5. Set the last day to import in To. Use the day before misomoon started collecting.
  6. To start later than the earliest day Google has, set the first day in From.
  7. Click Import.

Both dates are included. An empty From starts at 2015-08-14, the earliest day Google serves. An empty To ends yesterday.

The import runs on the server, so it doesn't need the page to stay open. Its row in the list says Importing…, and the card checks again every 4 seconds. A finished import shows its first and last day and the number of rows it stored. misomoon stores each Google day under the same date in the site's time zone.

If the import fails, its row shows Failed: and the error from Google or misomoon. To try again, delete the failed import, which also removes any rows it stored. Then start again at Connect Google, because the dashboard forgets the Google sign-in once you click Import.

If Google sign-in fails or you cancel it, Settings opens again with the Connect Google button and no property list. Click Connect Google to try again.

Each import is separate. If you import the same days twice, they count twice, so delete the old import before you import those days again.

Upload CSV files

misomoon reads a CSV export as one ZIP file or as the CSV files from it. Each file holds one report and is named imported_<report>.csv or imported_<report>_<YYYYMMDD>_<YYYYMMDD>.csv. CSV format lists the reports and their columns.

  1. Open Settings > Integrations.
  2. In the Import history card, click Upload CSV or zip.
  3. Pick the ZIP file, or pick one or more CSV files.

misomoon reads the files while you wait. The new row in the list shows the file names, the first and last day in the files, and the number of rows stored.

Include imported_visitors.csv. It is the only file that fills the site totals, which are the top-line visitors, visits, pageviews, bounce rate, and visit duration, and the chart. The other files fill breakdowns. imported_custom_events.csv also adds its events to the total event count.

misomoon skips files whose names don't match the pattern and reports it doesn't read. If no file holds rows that misomoon reads, the upload fails with no imported_*.csv files found (expected a CSV export).

To upload from a script, send the files as file fields of a multipart form. The call returns 201 and the new import:

sh
curl -X POST "https://analytics.example.com/api/v1/sites/shop/imports" \
	-H "Authorization: Bearer $MISOMOON_KEY" \
	-F "file=@export.zip"

Delete an import

If an import says Importing…, wait until it finishes or fails before you delete it. Rows that a running import writes after you delete it stay in your reports.

  1. Open Settings > Integrations.
  2. In the Import history card, click the Delete import button on the row of the import.

misomoon deletes the import at once, without asking you to confirm. It removes the import's rows from every shard that holds data for the site, then removes the import from the list. When the last finished import is gone, the Imported button disappears.

Connect Search Console

The site must be in Google Search Console under a Google account that you can sign in with, and the instance needs Google sign-in. If the secrets are missing, the Google Search Console card says Not enabled on this server.

  1. Open Settings > Integrations.
  2. In the Google Search Console card, click Connect Search Console.
  3. Sign in with a Google account that has the site in Search Console, and allow access.
  4. Back in Settings, pick the property in Search Console property.
  5. Click Connect.

Properties for the site's domain come first in the list and say matches this site. A domain property shows as example.com (domain). The list leaves out properties that the account hasn't verified. If none is left, the card says that this Google account has no verified Search Console properties. Add the site in Search Console, or sign in with another account.

The card then shows the property, the person who connected it, and the date. A site has one connected property. If you connect again and pick another property, it replaces the first one.

Find the search terms

  1. Open Overview.
  2. In Where they come from, click the Organic Search channel.
  3. Click the Search terms tab.

The tab lists the terms that brought the most clicks from Google in the period, with the clicks for each one. It appears only for a site with Search Console connected, and only while Organic Search is the chosen channel. Everyone who can see the site's reports sees it, viewers included.

To get impressions, click-through rate, and position too, call the breakdown endpoint with property=search_term, or use the get_search_terms MCP tool.

misomoon reads search terms from Google when you open the report, and keeps each answer for ten minutes. It doesn't save them in its database. Google reports searches about two days late, so the last two or three days of a period fill in later.

Google can filter search terms by page and by device only. With any other filter on, the tab shows an error that names the filter. Remove that filter to see the terms.

Disconnect Search Console

  1. Open Settings > Integrations.
  2. In the Google Search Console card, click the Disconnect Search Console button.

misomoon disconnects at once, without asking you to confirm. It asks Google to revoke its access and deletes the tokens it stored. The Search terms tab disappears.

Reference

Roles

TaskWho can do it
Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRETWhoever deploys the Worker
See the import list, import from Google, upload CSV files, delete importsOwner, admin, editor, and guest editor
Connect, change, and disconnect Search ConsoleOwner, admin, editor, and guest editor
See imported numbers and the Imported buttonEveryone who can see the site's reports
See search termsEveryone who can see the site's reports

The Import history and Google Search Console cards appear only for the roles that can edit the site. An API key acts with its owner's role.

CSV format

These rules apply to every file:

  • The name is imported_<report>.csv, or imported_<report>_<YYYYMMDD>_<YYYYMMDD>.csv with the dates of the export. Inside a ZIP file, a file may sit in a folder.
  • The first line holds the column names. misomoon finds each column by its name, in any order, and ignores columns it doesn't read. A missing column counts as empty.
  • date is YYYY-MM-DD. misomoon skips rows with any other date.
  • Commas separate the values. A value in double quotes may hold commas and line breaks, and "" inside it is one double quote.
  • misomoon rounds counts to whole numbers. It reads an empty, negative, or non-numeric count as 0.
  • misomoon trims text values and cuts them at 500 characters. It stores an empty value as (none).

Most reports carry the visit columns:

ColumnWhat it holds
visitorsUnique visitors that day
visitsVisits
pageviewsPageviews
bouncesVisits that bounced
visit_durationThe total length of all the visits, in seconds

misomoon reads these reports. It skips files for other reports.

ReportColumns besides dateWhat it fills
visitorsThe visit columnsThe site totals and the chart
sourcessource, referrer, utm_source, utm_medium, utm_campaign, utm_content, utm_term, and the visit columnssource, channel, referrer, and each UTM dimension that has a value. An empty source is Direct. misomoon works out the channel from source, utm_medium, and utm_source. It keeps the host of referrer without www..
pagespage, visitors, visits, pageviews, total_time_on_page, total_time_on_page_visits, total_scroll_depth, total_scroll_depth_visitspage. Time on page is total_time_on_page, in seconds, divided by total_time_on_page_visits. Scroll depth is total_scroll_depth, in percent, divided by total_scroll_depth_visits.
entry_pagesentry_page, entrances, visitors, pageviews, bounces, visit_durationentry_page. entrances is the visit count.
exit_pagesexit_page, exits, visitors, pageviews, bounces, visit_durationexit_page. exits is the visit count.
locationscountry, region, and the visit columnscountry as an uppercase code, and region
browsersbrowser, browser_version, and the visit columnsbrowser, and browser_version as the browser name followed by the version
operating_systemsoperating_system, operating_system_version, and the visit columnsos, and os_version as the system name followed by the version
devicesdevice and the visit columnsdevice
custom_eventsname, visitors, eventsevent. Outbound Link: Click becomes Outbound link, File Download becomes Download, and Form: Submission becomes Form sent.

What imported data fills

Imported history fills the site totals, the chart, and the breakdowns of these dimensions. The metrics page defines each metric.

DimensionFrom a Google propertyFrom CSV files
Site totals and the chartYesimported_visitors.csv
Custom event counts in the totalsYesimported_custom_events.csv
channelYesYes
sourceYesYes
referrerYesYes
utm_source, utm_medium, utm_campaign, utm_content, utm_termYesYes
pageYesYes
entry_pageYesYes
exit_pageNoYes
country, regionYesYes
cityYesNo
browserYesYes
browser_versionNoYes
os, os_versionYesYes
deviceYesYes
eventYesYes

Imported visitors are unique per day, as Google and the export report them. A period's imported visitors are the sum of its days.

A page's bounce rate and visit duration come from the visits that entered on that page, which are the entry_page rows.

A day of imported data has no time of day. Minute and hour charts show the whole day at midnight.

What a Google import reads

misomoon runs one Google Analytics Data API report per group of dimensions, 100,000 rows per request, and maps the results like this:

misomoonGoogle
VisitorstotalUsers
Visitssessions
PageviewsscreenPageViews
Bouncessessions minus engagedSessions
Visit durationuserEngagementDuration
Time on pageuserEngagementDuration divided by sessions, per pagePath
EventseventCount per eventName
sourcesessionSource without www., or Direct when Google has none
channelsessionDefaultChannelGroup
referrersessionSource when it contains a dot
utm_source, utm_mediumsessionSource, sessionMedium
utm_campaign, utm_content, utm_termsessionCampaignName, sessionManualAdContent, sessionGoogleAdsKeyword
pagepagePath, without the query string or a trailing slash
entry_pagelandingPage, without the query string or a trailing slash
country, region, citycountryId, region, city
devicedeviceCategory as Desktop, Mobile, or Tablet
browserbrowser
os, os_versionoperatingSystem, and operatingSystem followed by operatingSystemVersion

Most Google channel groups keep their names. Organic Video and Paid Video become Video, Organic Shopping becomes Referral, and Paid Shopping becomes Paid Search. Google's automatic events page_view, session_start, first_visit, user_engagement, and scroll are left out, because misomoon measures those itself. Google values (not set), (none), and (direct) count as empty.

What imports leave out

  • Reports with any filter, including segments and goal filters.
  • Goal conversions, revenue, and the Funnels, Paths, Live, Behavior, and Speed views.
  • Dimensions that the table above doesn't list, such as screens, networks, and languages.
  • Scroll depth from a Google import.
  • The All time period. It starts at the first visit that misomoon collected for the site, so it doesn't reach imported days before that visit. To see older imported days, pick a period that reaches back to them. A custom period covers up to 400 days.

Search terms

Each row of the breakdown with property=search_term has these fields:

FieldWhat it holds
keyThe search term
clicksClicks from Google search results to the site
impressionsHow often the site appeared in the results
ctrClicks divided by impressions, in percent, with one decimal
positionThe average position in the results, with one decimal. 1 is the top.
visitorsThe same number as clicks

total_visitors is the sum of clicks on the page of results. misomoon asks Google for web search results, fresh data included.

The filters that misomoon passes to Google:

  • page and entry_page with == or !=, where * matches any characters, or with ~ or !~ and one value.
  • device with == or != and one of desktop, mobile, or tablet.
  • source==google and channel==Organic Search, which change nothing because every search term comes from Google search.

Any other filter returns an error that names it. The search parameter keeps only the terms that contain its text.

Limits

  • A Google sign-in must finish within an hour of the click on Connect Google or Connect Search Console.
  • Google import dates are YYYY-MM-DD. misomoon ignores a date in another form and uses the default instead.
  • An import's name is cut at 120 characters, and its error at 500.
  • While the site's data moves between shards, imports fail with this site's history is being moved between shards; try the import again in a few hours.
  • The breakdown returns up to 1,000 search terms per page. The MCP tool returns up to

What misomoon stores

  • For each import: its source, name, first and last day, status, error, and row count.
  • The imported daily totals, in the shards that hold the site's data for those days.
  • During a Google sign-in, the Google tokens. misomoon deletes them after a successful Google import or a Search Console connection. The next Google sign-in deletes unused ones that are more than a day old.
  • For a connected Search Console property: the property, the Google tokens, the person who connected it, and the date.

misomoon asks Google for read-only access. The import asks for the https://www.googleapis.com/auth/analytics.readonly scope, and Search Console for https://www.googleapis.com/auth/webmasters.readonly. Both ask for offline access, so misomoon can renew the access token without a new sign-in.

API endpoints

Every path starts with /api/v1/sites/:id/ except the callback.

Method and pathRoleWhat it does
GET importsEditorLists the site's imports, newest first: id, site_id, source (csv or google), label, from_day, to_day, status (importing, done, or failed), error, rows, and created_at.
POST importsEditorUploads a CSV export as multipart file fields. Returns 201 and the import.
DELETE imports/:importEditorDeletes the import and its rows. Returns 204.
POST imports/google/connectEditorStarts a Google sign-in for the import. Returns {"url": "..."} to open in the browser.
GET imports/google/properties?grant=EditorLists the Google Analytics properties that the grant can read: id, name, and account.
POST imports/googleEditorStarts an import from {"grant", "property", "from", "to", "label"}. property looks like properties/123. Returns 202 and {"id", "status": "importing"}.
GET search-consoleEditorReturns available, which is true when the instance has the Google secrets, and connected, the connected property or null.
POST search-console/connectEditorStarts a Google sign-in for Search Console. Returns {"url": "..."}.
GET search-console/properties?grant=EditorLists the verified Search Console properties: property, and matches, which is true for the site's domain.
PUT search-consoleEditorConnects {"grant", "property"}. Returns the same object as GET search-console.
DELETE search-consoleEditorDisconnects Search Console. Returns 204.
GET /api/v1/google/callbackNoneGoogle redirects here after sign-in. It opens Settings again with the grant in the address.

Without the Google secrets, every endpoint that calls Google returns 501. GET search-console returns available: false instead.

Search every page and heading. ↑ ↓ to move, enter to open.